#include "include.h" 
#include "sg_Chain.h"
#include <shinobi.h>
#include "main.h"

#include "islcard.h"
#include "options.h"

#include	<sg_mw.h>
#include	"usr.h"

#define NOT_FLAG(flag)		(((flag) == OFF) ? ON : OFF)
typedef struct {
	Sint32	term_flag;			//	Application terminate flag
	Sint32	disp_flag;			//	Display information flag
	Sint32	pause;				//	Pause flag
	char	*fname;				//	File name
	MWPLY	ply;				//	Player
	MWS_PLY_CPRM_SFD cprm;		//	Create parameter
} AP_OBJ, *AP;

AP_OBJ ap_obj = {
	OFF,						//	Execute App.
	ON,							//	Display Info.
	OFF,						//	not puase
	"SAMPLE.SFD",				//	File name
	NULL,						//	Player
};


KMPACKEDARGB 	borderColour;
KMDWORD 		FBarea[24576 + 19456];
PDS_PERIPHERAL 	*per[4],*pp;

extern TextureType *hourglass_texes[5];


fixed			gameSpeed				= 4096;
fixed			globalFrogScale			= 2457;

psFont			*font;

PSIMODEL		*testActorModel;
ACTOR			*testActor;
VECTOR			bbsrc;
VECTOR			bbtar;
GsRVIEW2		camera;
//unsigned long 	*sqrtable;
//short			*acostable;
displayPageType displayPage[2];
displayPageType *currentDisplayPage		= 0;
char			*packetSpace;				//used to hold current poly packet

int				originalTexCount		= 0;
int				afterRoomTexCount		= 0;

ULONG			frame					= 0;
ULONG			loop_ticker				= 0;
ULONG			loop_ticker2			= 0;
int				gameTextLang;
GsRVIEW2		mycamera;

VOID_SUBROUTINE draw_routine			= NULL;
VOID_SUBROUTINE cycle_routine			= NULL;


















/*	Callback function when an error occur in middleware	*/
void ap_mw_err_func(void *obj, char *errmsg)
{
	AP ap=(AP)obj;

  	for (;;);
}



/*	ミドルウェアの初期化				*/
/*	Initialize Middleware				*/
void ApInitMw(AP ap,
		Sint32 mod, Sint32 frm, Sint32 cnt, Sint32 ltncy, void *vtxbuf)
{
	MWS_PLY_INIT_SFD	iprm;

	memset(&iprm, 0, sizeof(iprm));	/*	予約メンバのゼロ設定			*/
									/*	initialize reserved member		*/
	iprm.mode		= mod;			/*	Resolution, NTSC, PAL, VGA		*/
	iprm.frame		= frm;			/*	RGB565, ARGB8888				*/
	iprm.count		= cnt;			/*	Game period by the V-Sync count */
	iprm.latency	= ltncy;		/*	Latency (2V or 3V)				*/
	mwPlyInitSfdFx(&iprm);
	mwPlySetVertexBuffer(vtxbuf);
	mwPlyEntryErrFunc(ap_mw_err_func, ap);
}

/*	ミドルウェアの終了					*/
/*	Finalize Middleware					*/
void ApFinishMw(AP ap)
{
	mwPlyFinishSfdFx();
}

/*	アプリケーション開始処理	*/
/*	Start application		*/
void ApStart(AP ap)
{
	MWS_PLY_CPRM_SFD		*cprm=&ap->cprm;

	/*	ハンドルの生成	*/
	/*	Create handle	*/
	memset(cprm, 0, sizeof(cprm));		/*	予約メンバのゼロ設定			*/
										/*	To initialize reserved member	*/
	cprm->compo_mode = MWD_PLY_COMPO_OPEQ;
	cprm->ftype	= MWD_PLY_FTYPE_SFD;
	cprm->dtype	= MWD_PLY_DTYPE_AUTO;	/*	AUTOの方が画質が良い			*/
										/*	Quality is good in case 
										 *	of specified 'AUTO'.			*/

	cprm->max_bps = 450*1024*8;		/*	最大ビットストリーム量(bit/sec)
									 *	実際よりも少なくすることも可能。
									 *	ムービーが途中でフリーズするときは
									 *	この値を増やしてください。			*/
									/*	Maximum number of bit per second
									 *	You can reduce it than a fact.
									 *	Increase this value when the movie
									 *	stops on the way.					*/
	cprm->max_width = 640;			/*	再生画像サイズの最大幅				*/
									/*	Maximum width of video stream		*/
	cprm->max_height = 480;			/*	再生画像サイズの最大高さ			*/
									/*	Maximum height of video stream		*/
	cprm->nfrm_pool_wk = 8;			/*	システム領域のフレームプール数
									 *	標準的には３で良いが、処理負荷の
									 *	変動によりコマ落ちが発生する場合は
									 *	増やしてください。					*/
									/*	Size of working area
									 *	It is good with 3 normally. But please
									 *	increase it when frame drop occurs by
									 *	processing load change.				*/
	/*	作業領域の大きさの計算				*/
	/*	Calculation of working area size	*/
	cprm->wksize = mwPlyCalcWorkCprmSfd(cprm);
	/*	作業領域の確保						*/
	/*	Allocation of working area			*/
	cprm->work = syMalloc(cprm->wksize);
	ap->ply = mwPlyCreateSofdec(cprm);
	if (ap->ply == NULL) {
		for (;;) ;
	}

	mwPlyStartFname(ap->ply, ap->fname);/*	再生開始						*/
										/*	Start to play middleware		*/
}

/*	アプリケーション停止処理	*/
/*	Stop application		*/
void ApStop(AP ap)
{
	mwPlyStop(ap->ply);					/*	再生停止 					*/
										/*	Stop to play middleware		*/
	mwPlyDestroy(ap->ply);				/*	ハンドル消去				*/
										/*	Destroy handle				*/
	ap->ply = NULL;
	syFree(ap->cprm.work);				/*	作業領域の開放				*/
										/*	Release work area			*/
	ap->cprm.work = NULL;
}

/*	情報の表示			*/
/*	Display infomation	*/
void ap_disp_info(AP ap)
{
	Sint32	time, tunit, hh, mm, ss, ff;

	mwPlyGetTime(ap->ply, &time, &tunit);
	UsrSfcnt2time(tunit, time, &hh, &mm, &ss, &ff);
	UsrPrintf(0, 0, "File: %s  Time:%02d:%02d:%02d.%02d", 
							ap->fname, hh, mm, ss, ff);

}

/*	動画の再生					*/
/*	Playing the animation file	*/
Sint32 ApExec(AP ap)
{
	MWPLY					ply=ap->ply;
	MWE_PLY_STAT			stat;
	const PDS_PERIPHERAL	*per;

	per = pdGetPeripheral(PDD_PORT_A0);
	if (per->press & PDD_DGT_ST) {
		ap->pause = NOT_FLAG(ap->pause);
		mwPlyPause(ply, ap->pause);		/*	一時停止解除			*/
										/*	Release pause status	*/
	}
	if (per->press & PDD_DGT_TA)
		ap->disp_flag = NOT_FLAG(ap->disp_flag);
	if (per->press & PDD_DGT_TB)
		return FALSE;

	/*	Checking status												*/
	stat = mwPlyGetStat(ply);
	if ((stat == MWE_PLY_STAT_PLAYEND) || (stat == MWE_PLY_STAT_ERROR))
		return FALSE;
	if ( ap->disp_flag == ON )
		ap_disp_info(ap);

	return TRUE;
}

/***
 *		メイン処理
 *		MAIN
 ***/

/*	GDファイルシステムのエラー発生時に起動する関数			*/
/*	Callback function when an error occur in GD file system	*/
void ApGdErrFunc(void *obj, Sint32 errcode)
{
	AP ap=(AP)obj;
	if (errcode == GDD_ERR_TRAYOPEND || errcode == GDD_ERR_UNITATTENT)
		ap->term_flag = ON;			/*	アプリケーションの終了		*/
									/*	Terminate application		*/
}

/*	ユーザがV-SYNC割り込みに登録する関数	*/
/*	Callback function when V-sync interrupt	*/
void ApVsyncFunc(void)
{
	UsrCheckDoorOpen();
}


















/*	--------------------------------------------------------------------------------
	Function 	: Kamui_Init
	Purpose 	: Initialise Kamui system configuration
	Parameters 	: none
	Returns 	: none
	Info 		:
*/
void Kamui_Init()
{
	int	i;
	
	// create the frame buffers
	pFB[0] = &primarySurfaceDesc;
	pFB[1] = &renderSurfaceDesc;

    // vertex buffer pointer
    dwDataPtr = (PKMDWORD)(((KMDWORD)syMalloc(0x200000) & 0x0FFFFFFFUL) | 0xA0000000);

	// set the system configuration
	kmSystemConfig.dwSize						= sizeof(KMSYSTEMCONFIGSTRUCT);
	kmSystemConfig.flags						= KM_CONFIGFLAG_ENABLE_CLEAR_FRAMEBUFFER;
	// for frame buffer
	kmSystemConfig.ppSurfaceDescArray			= pFB;
	kmSystemConfig.fb.nNumOfFrameBuffer			= 2;
	// for texture memory
    kmSystemConfig.nTextureMemorySize       	= 0x150000 * 3;		// 2 MB for textures
    kmSystemConfig.nNumOfTextureStruct        	= 4096;
    kmSystemConfig.nNumOfSmallVQStruct        	= 0;//1024;
    kmSystemConfig.pTextureWork               	= &FBarea[0];
    // for Vertex buffer
    kmSystemConfig.pBufferDesc                	= &vertexBufferDesc;
    kmSystemConfig.nNumOfVertexBank           	= 2;
    kmSystemConfig.pVertexBuffer              	= dwDataPtr;		// pointer to vertex buffer
    kmSystemConfig.nVertexBufferSize          	= 0x050000 * 4;		// 2MB for vertex buffer
    kmSystemConfig.nPassDepth                 	= 1;
    kmSystemConfig.Pass[0].dwRegionArrayFlag  	= KM_PASSINFO_AUTOSORT;
    kmSystemConfig.Pass[0].fBufferSize[0]     	= 80.0f;
    kmSystemConfig.Pass[0].fBufferSize[1]     	= 0.0f;
    kmSystemConfig.Pass[0].fBufferSize[2]     	= 20.0f;
    kmSystemConfig.Pass[0].fBufferSize[3]     	= 0.0f;
    kmSystemConfig.Pass[0].fBufferSize[4]     	= 0.0f;

	kmSetSystemConfiguration(&kmSystemConfig);

    // initialise border colour
    borderColour.dwPacked = RGBA(0,0,0,255);	// black border
    kmSetBorderColor(borderColour);
    
    // user initialisation
	InitCRCTable();	
	
}
	
void SetCam(VECTOR src, VECTOR tar)
{
	camera.vpx = src.vx;
	camera.vpy = src.vy;
	camera.vpz = src.vz;

	camera.vrx = tar.vx;
	camera.vry = tar.vy;
	camera.vrz = tar.vz;

	camera.rz  =  0;

	GsSetRefView2(&camera);

	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);
}
	  
void InitCam(void)
{
//	static int dist=512;//frog
	static int dist=2040;//am
	// Set up an ambient light
	GsSetAmbient(128*16, 128*16, 128*16);
 	GsSetLightMode(2);

	// Set up a simple camera
	GsSetProjection(900);//320);//475);			// default distance of projection; alters perspective
	SetGeomOffset(640/2,480/2);		// camera focused on middle of screen
   //	SetGeomOffset(0,0);		// camera focused on middle of screen

 	bbsrc.vx = dist;//500;
	bbsrc.vy = -100;//-350;
 	bbsrc.vz = 0;//11000;
	/*
	if(per->press & PDD_DGT_TA)
	{
		dist+=64;
		debug_printf("dist=%d\n",dist);
	}
	if(per->press & PDD_DGT_TB)
	{
		dist-=64;
		debug_printf("dist=%d\n",dist);
	}
	  */
	bbtar.vx = 0;//bbsrc.vx + (sinf(viewAngle) * viewDist);
	bbtar.vy = 0;//from
	bbtar.vy = -300;//am
	bbtar.vz = 0;//bbsrc.vz + (cosf(viewAngle) * viewDist);	

	SetCam(bbsrc,bbtar);
}

void InitLookupTables()
{
	int	fileLength;

  //	sqrtable = fileLoad("INCBIN\\Sqrtable.bin",&fileLength);
  //	acostable = fileLoad("INCBIN\\acostab.bin",&fileLength);
}





void mikeyInit()
{
	packetSpace = syMalloc(sizeof(POLY_GT4)*20);//should be the size of freds biggest poly packet

	GsSetProjection(900);//320);//475);			// default distance of projection; alters perspective
	SetGeomOffset(640/2,480/2);		// camera focused on middle of screen

}


void mikeyTest()
{
	POLY_G3 *si;

				
	BEGINPRIM(si,POLY_G3);

	si->r0=0; si->g0=128; si->b0=120;
	si->r1=0; si->g1=128; si->b1=120;
	si->r2=0; si->g2=128; si->b2=120;
//	si->r3=0; si->g3=128; si->b3=120;

	si->code=0x30;
//	si->code=0x38;

	si->x0=20;   si->y0=20;
	si->x1=100;  si->y1=20;
	si->x2=20;   si->y2=100;
//	si->x3=100;  si->y3=100;

	ENDPRIM(si,1,POLY_G3);


}

void mikeyTest2()
{
	POLY_GT4 *si;
	TextureType *tex;
	int w,h;
	static int t=156;

	if(padData.digital[0]&PAD_LEFT)t--;
	if(padData.digital[0]&PAD_RIGHT)t++;

//	tex = textureFindInAllBanks("BAGFRONT");
	tex = &DCKtextureList[t];
	w=tex->w;
	h=tex->h;

   	debug_printf("texture %d wh=%d %d\n",t,w,h);

	BEGINPRIM(si, POLY_GT4);

	setPolyGT4(si);

	si->tpage = tex->tpage;

	setXY4(si, 
			100,100,
			100+w,100,
			100,100+h,
			100+w,100+h);
	setUV4(si, 
			tex->u0, tex->v0,
			tex->u1, tex->v1,
			tex->u2, tex->v2,
			tex->u3, tex->v3);
					    
	setRGB0(si,128,128,128);
	setRGB1(si,128,128,128);
	setRGB2(si,128,128,128);
	setRGB3(si,128,128,128);

	ENDPRIM(si, 5, POLY_GT4);
}


void padTest()
{
	if(padData.debounce[0] & PAD_LEFT)debug_printf("debounce left\n");
	if(padData.debounce[0] & PAD_RIGHT)debug_printf("debounce right\n");
	if(padData.debounce[0] & PAD_UP)debug_printf("debounce up\n");
	if(padData.debounce[0] & PAD_DOWN)debug_printf("debounce down\n");

	if(padData.digital[0] & PAD_LEFT)debug_printf("digital left\n");
	if(padData.digital[0] & PAD_RIGHT)debug_printf("digital right\n");
	if(padData.digital[0] & PAD_UP)debug_printf("digital up\n");
	if(padData.digital[0] & PAD_DOWN)debug_printf("digital down\n");

	if(padData.debounce[0] & PAD_TRIANGLE)debug_printf("debounce tri\n");
	if(padData.debounce[0] & PAD_CIRCLE)debug_printf("debounce circ\n");
	if(padData.debounce[0] & PAD_CROSS)debug_printf("debounce cross\n");
	if(padData.debounce[0] & PAD_SQUARE)debug_printf("debounce sqr\n");

	if(padData.digital[0] & PAD_TRIANGLE)debug_printf("digital tri\n");
	if(padData.digital[0] & PAD_CIRCLE)debug_printf("digital circ\n");
	if(padData.digital[0] & PAD_CROSS)debug_printf("digital cross\n");
	if(padData.digital[0] & PAD_SQUARE)debug_printf("digital sqr\n");

	if(padData.debounce[0] & PAD_L1)debug_printf("debounce l1\n");
	if(padData.debounce[0] & PAD_R1)debug_printf("debounce r1\n");
	if(padData.debounce[0] & PAD_START)debug_printf("debounce start\n");


	if(padData.digital[0] & PAD_L1)debug_printf("digital l1\n");
	if(padData.digital[0] & PAD_R1)debug_printf("digital r1\n");
	if(padData.digital[0] & PAD_START)
	{
	 //	debug_printf("digital start\n");
		//analog test
		debug_printf("%d  %d\n",padData.analogX[0], padData.analogY[0]);

	}
}

void main()
{
	void *old_cycle;
	int a;
	AP ap=&ap_obj;
	void *vtxbuf;
	Sint32 mode, frame, count, latency;
	Bool ret;

#ifdef __GNUC__
	shinobi_workaround();
#endif

	
  /*	mwPlyPreInitSofdec();
	vtxbuf = UsrInitVideo(&mode, &frame, &count, &latency);
	gdFsEntryErrFuncAll((void *)ApGdErrFunc, ap);
	UsrInitSound();
	UsrSetVsyncFunc((void (*)(void))ApVsyncFunc);
	ApInitMw(ap, mode, frame, count, latency, vtxbuf);
  	*/
    // Check malloc alignment
    Init32Malloc();

	  
	InitLookupTables();
	utilInitCRC();
	utilSeedRandomInt(398623);
//	sfxInitialise(SFX_REVERB_MODE_OFF);
	  
	//	InitCam();
	actorInitialise();
  	psiInitialise(20);
	initialisePsxStrips();
	cardInitialise();
	   
	InitTimerbar();
	mikeyInit();
	   
	  	 
	font = fontLoad("TEXTURES\\FONT.BMP");//must be after psxstrips
//	jim_fontLoad("\\SHELL\\FONT12_x.FON");
	
	textureLoadBank("INVENTORY\\BACK.SPT");
	{
		TextureBankType *generic_textures;
			TextureType *sprs[9];
			char *names[9] = {"TRIANGLE","CIRCLE","CROSS","SQUARE", "L1", "L2", "R1", "R2", "STARTBUTTON"};
			int i;

			generic_textures = textureLoadBank("SHELL\\GENERIC.SPT");

			for(i = 0; i < 9; i++)
			{
				sprs[i] = textureFindInAllBanks(names[i]);
			}

			fontRegisterButtonSprites(sprs[0],sprs[1],sprs[2],sprs[3]);

			fontRegisterOtherSprites('L', sprs[4]);
			fontRegisterOtherSprites('l', sprs[5]);
			fontRegisterOtherSprites('R', sprs[6]);
			fontRegisterOtherSprites('r', sprs[7]);
			fontRegisterOtherSprites('A', sprs[8]);
	}
	   	   
	dcSetInventory("none");//auto inventory loading! - code in empty.c
	   
 	gameTextInit("SHELL\\GAMETEXT.TXT", TEXTSTR_MAX, 11, 0);
	
	frame = 0;
	loop_ticker = 0;
	loop_ticker2 = 0;

	originalTexCount = DCKnumTextures;

	particleInitialiseList();

// Although this is done in "startup", during development, we're not always *calling* startup
// (oops, keep after the setups!)
	cardInitialPrefs(&optionData);
 	cardInitialGameState();

	menuStart();

   /*	gdFsChangeDir("\\");
	while ( ap->term_flag == OFF ) 
	{
		ApStart(ap);
		while (1) 
		{
			mwPlyExecTexSvr();			
			UsrBeginDraw();

			mwPlyExecDrawSvr();			
			ret = ApExec(ap);
			UsrEndDraw();
			
			pp = pdGetPeripheral(PDD_PORT_A0);

		 	if(pp->press & PDD_DGT_TA)ap->term_flag = ON;

			if ( ret == FALSE || ap->term_flag == ON )
				break;
			UsrWaitVBlank();
		}
		ApStop(ap);
	}
 	ApFinishMw(ap);			
							
	UsrFinishVideo();		
	 */


//		viewerStart();
//		mapviewStart();
		menuStart();
//		languageStart();
	


	// render loop
	while(TRUE)
	{
		kmBeginScene(&kmSystemConfig);
		kmBeginPass(&vertexBufferDesc);

		DCTIMER_START(0);

		padHandler();

   	  //	testActor->position.vx = padData.analogXS[0];
   	 //	testActor->position.vy = 0;
   	//	testActor->position.vz = padData.analogYS[0];
	   	
		//testActor->psiData.object->rotate.vy = roty;
  		//roty+=32;

		//padEmu();
//		padTest();

//		frame++;
  //		if(frame>2358)frame=1;
  //		frame&=255;

//		frame=1;

//   	   	psiSetKeyFrames(testActor->psiData.object, frame);
//   	   	actorMove(testActor);
//	   	actorDraw(testActor);

// 	   	mikeyTest();
//		fontPrint(font, 100,150, "ABCDEFGH", 128,128,128);
 //		fontPrint(font, 100,180, "abcdefghijklmnopqrstuvwxyz", 128,128,128);


//	   	if(padData.digital[0] & PAD_CIRCLE)
 //		{
  //	   		overlaySprite(hourglass_texes[0],-200,0,1,0xff808080);
   //	   		overlaySprite(hourglass_texes[1],-100,0,1,0xff808080);
	//   		overlaySprite(hourglass_texes[2],0,0,1,0xff808080);
	 //  		overlaySprite(hourglass_texes[3],100,0,1,0xff808080);
	  // 		overlaySprite(hourglass_texes[4],200,0,1,0xff808080);
	   //	}




		old_cycle = cycle_routine;

		if(cycle_routine)
			cycle_routine();

		while(old_cycle != cycle_routine)
		{
			old_cycle = cycle_routine;
			padHandleInput();
			padHandleShock();
			cycle_routine();
		}

//				CHARACTER *pC = characterGetMainCharacter();

  		if(draw_routine)
  			draw_routine();
  //		overlayDraw();



		loop_ticker++;
		loop_ticker2++;

		DCTIMER_STOP(0);


   		DCTIMER_PRINTS();


		kmEndPass(&vertexBufferDesc);
				
		kmRender(KM_RENDER_FLIP);
		kmEndScene(&kmSystemConfig);

	}
	
//	amShutdown();
//	syChainDeleteHandler(VblChain);                   
	
//	FreeBackDrop();

	sbExitSystem();
}
