#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <egb.h>
#include <snd.h>
#include <fmcfrb.h>

#include "concorde.h"
#include "tornado.h"
#include "ysmaze2.h"

#define YES          1
#define NO           0
#define MALLOCFUNC  malloc
#define FREEFUNC    free

char egb[EgbWorkSize];
PAGE *wp,vp[2],degi;

int  main(int argc,char *argv[]);
void InitBios();
void SwapPage();
int  LoadMaze(char *fname,MAZEMAP *mz);
void Turn(int *dir,int ofset);
void Progress(MAZEMAP *mz,int *x,int *y,int dir,int v);
void MazeMain(MAZESCREEN *ms,MAZEMAP *mz);
void GetVideoScreen(char *dat);
int  Inkey();
void InitCapture();
void Capture(int mx,int my,char *dat);
void InsertCaptureScreen();

int main(int argc,char *argv[])
{
	MAZEMAP mz;
	MAZESCREEN ms;

	if(argc<2)
	{
		printf("迷路ファイルを指定してください.\n");
		printf("run386 mazewalk ????.maz\n");
		return 1;
	}

	ms.cx=128;     /* 迷路表示の中心座標 */
	ms.cy=120;

	ms.x0=0;      /* 画面描画範囲 */
	ms.y0=0;
	ms.x1=255;
	ms.y1=239;

	ms.wc=25368;  /* 壁の色(グレー [明]) */
	ms.dc=10570;  /* ドアの色(グレー [暗]) */
	ms.cont=5;    /* 距離に対する輝度の減衰率 */
	ms.mag=64;    /* 拡大率(1m前方の1mの棒が見えるドット数) */

	if(LoadMaze(argv[1],&mz)!=YES)
	{
		printf("迷路 Load Error\n");
		exit(1);
	}

	InitBios();
	InitCapture();
	MazeMain(&ms,&mz);
	EGB_writePage(egb,1);
	EGB_dezitize(egb,0);
}

void InitBios()
{
	EGB_init(egb,EgbWorkSize);

	/* レイア0 256×240 32K色モード  レイア1 320×240 32K色モード */
	EGB_resolution(egb,0,8);
	EGB_resolution(egb,1,11);

	/* レイア0 表示開始位置/倍率の設定 */
	EGB_writePage(egb,0);
	EGB_displayStart(egb,0,32,0);
	EGB_displayStart(egb,2,4,1);
	EGB_displayStart(egb,3,256,240);
	EGB_color(egb,1,0x8000);
	EGB_clearScreen(egb);

	/* レイア1 表示開始位置/倍率の設定 */
	EGB_writePage(egb,1);
	EGB_displayStart(egb,0,0,0);
	EGB_displayStart(egb,2,4,1);
	EGB_displayStart(egb,3,320,240);
	EGB_color(egb,1,0x8000);
	EGB_clearScreen(egb);

	/* ビデオディジタイズの設定 */
	EGB_writePage(egb,1);
	EGB_dezitize(egb,1);

	/* 初期状態 レイア1のみ表示ON */
	EGB_displayPage(egb,0,1);

	SND_elevol_init();

	/* ダブルバッファとデジタイズページの初期化 */
	YGH_initPage(&vp[0],SEG_VRAMP,0x00000,256,240);
	YGH_initPage(&vp[1],SEG_VRAMP,0x20000,256,240);
	wp=&vp[0];
	YGH_initPage(&degi,SEG_VRAMP,0x40000,512,240);

	SND_elevol_init();
	SND_elevol_set(1,127,127);
}

void SwapPage()
{
	int oy;

	if(wp==&vp[0])
	{
		oy=0;
		wp=&vp[1];
	}
	else
	{
		oy=256;
		wp=&vp[0];
	}

	EGB_writePage(egb,0);
	EGB_displayStart(egb,1,0,oy);
	EGB_writePage(egb,0);
}

int LoadMaze(char *fname,MAZEMAP *mz)
{
	int siz;
	FILE *fp;
	short *dat;

	if((fp=fopen(fname,"rb"))==NULL)
	{
		return NO;
	}

	fread(&mz->bl,4,1,fp);  /* 1ブロックの壁の長さ (cm) */
	fread(&mz->bh,4,1,fp);  /* 壁の高さ(cm) */
	fread(&mz->lx,4,1,fp);  /* 迷路の横ブロック数 */
	fread(&mz->ly,4,1,fp);  /* 迷路の縦ブロック数 */

	siz=mz->lx*mz->ly;
	if((dat=(short *)MALLOCFUNC(sizeof(short)*siz))==NULL)
	{
		return NO;
	}

	mz->map = dat;          /* 迷路の壁/床/天井データ */
	fread(dat,sizeof(short),siz,fp);

	fclose(fp);
	return YES;
}

void Turn(int *dir,int ofset)
{
	*dir=(*dir+ofset)&65535;
}

void Progress(MAZEMAP *mz,int *x,int *y,int dir,int v)
{
	int vx,vy;
	int lx,ly;
	int x2,y2,bx,by,bx2,by2;

	lx=mz->bl * mz->lx;   /* 迷路全体の横サイズ(cm) */
	ly=mz->bl * mz->ly;   /* 迷路全体の縦サイズ(cm) */

	vx=-MulSin(v,dir);    /* 進もうとするベクトル */
	vy=-MulCos(v,dir);

	bx= *x/(mz->bl);      /* 現在位置(ブロック) */
	by= *y/(mz->bl);

	x2= *x+vx;            /* 移動後の座標(cm)を仮に計算する */
	y2= *y+vy;

	while(x2<  0)x2+=lx;  /* 座標を正規化する */
	while(x2>=lx)x2-=lx;
	while(y2<  0)y2+=ly;
	while(y2>=ly)y2-=ly;

	bx2= x2/mz->bl;       /* 移動後の座標(ブロック) */
	by2= y2/mz->bl;

	if     (vx>0 && bx2!=bx && MazePass(mz,bx,by,3))return;
	else if(vx<0 && bx2!=bx && MazePass(mz,bx,by,1))return;
	else bx=bx2;

	if     (vy>0 && by2!=by && MazePass(mz,bx,by,2))return;
	else if(vy<0 && by2!=by && MazePass(mz,bx,by,0))return;

	*x=x2;
	*y=y2;
}

void MazeMain(MAZESCREEN *ms,MAZEMAP *mz)
{
	int mx,my,dir,pad,key;
	MAZEZBUF buf[320];
	char *dat,*bmbuf;

	/* ビデオ取り込み用バッファ */
	dat=(char *)MALLOCFUNC(320*240*2);

	/* ビットマップ表示用バッファ */
	bmbuf=(char *)MALLOCFUNC((ms->x1 -ms->x0 +1)*(ms->y1 -ms->y0 +1)*2);

	if(dat==NULL)
	{
		printf("Out Of Memory!\n");
		return;
	}

	mx=mz->bl /2;
	my=mz->bl /2;
	dir=49152;

	pad=255;
	key=0;
	while((pad & 16)!=0 && key!='q' && key!='Q')
	{
		GetVideoScreen(dat);

		MazeGetLines(buf,mz,ms,mx,my,dir,1500);
		MazeDrawWalls(wp,mz,ms,buf);

		MazePrepareInsertBitmap(mx,my,dir,1500,ms);
		MazeInsertBitmap(dat,320,240,240,180,mz->bl/2,mz->bl/2);
		InsertCaptureScreen();
		MazeFlushBitmap(wp,buf,bmbuf);

		SwapPage();

		key=Inkey();
		SND_joy_in_2(0,&pad);
		if((pad & 1)==0)Progress(mz,&mx,&my,dir, mz->bl/5);
		if((pad & 2)==0)Progress(mz,&mx,&my,dir,-mz->bl/5);
		if((pad & 4)==0)Turn(&dir, 2048);
		if((pad & 8)==0)Turn(&dir,-2048);

		if((pad & 32)==0 || key=='c' || key=='C')Capture(mx,my,dat);
	}
}

void GetVideoScreen(char *dat)
{
	YGH_get(&degi,dat,0,0,319,239);
}

int Inkey()
{
	unsigned key,dummy;
	key=KYB_read(1,&dummy);
	if((key & 0xff00)==0)
	{
		return key;
	}
	return 0;
}

#define N_VD_MAX 128

static int nVdDat;
static struct vdPos {
	int x,y;
	char *dat;
} vdDat[N_VD_MAX];

void InitCapture()
{
	nVdDat=0;
}

void Capture(int mx,int my,char *dat)
{
	int i;
	char *neo;

	if(nVdDat>=N_VD_MAX || (neo=(char *)MALLOCFUNC(320*240*2))==NULL)
	{
		return;
	}

	/* 同じ位置でボタンを押しすぎて画像が増えすぎるのを防止する */
	for(i=0; i<nVdDat; i++)
	{
		if(mx==vdDat[i].x && my==vdDat[i].y)
		{
			return;
		}
	}

	for(i=0; i<320*240*2; i++)
	{
		neo[i]=dat[i];
	}

	vdDat[nVdDat].x=mx;
	vdDat[nVdDat].y=my;
	vdDat[nVdDat].dat=neo;
	nVdDat++;
}

void InsertCaptureScreen()
{
	int i;

	for(i=0; i<nVdDat; i++)
	{
		MazeInsertBitmap
			(vdDat[i].dat,320,240,240,180,vdDat[i].x,vdDat[i].y);
	}
}
