#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"

#include	"islvideo.h"
#include "vmucheck.h"
#include "pause.h"
#include "fadeout.h"
#include "startup.h"



#include "fireworks.h"
#include "seesaw.h"
#include "catapult.h"
#include "engine.h"
#include "construct.h"
#include "egglaying.h"
#include "wings.h"
#include "crate.h"
#include "dogchase.h"
#include "card.h"
#include "enemies.h"

int	digistick = 0;
int digistick_debounced = 0;
int preTitle = YES;
int inDraw = NO;

char playing_ticker_enabled = 0;

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

extern int noControllerInputTime;
extern SYCHAIN				usrg_chid;

int mapPolyCount;
int globalAbortFlag = 0;

int				texNumAfterFont;

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;

int displayMode, frameBufferFormat;	// character;
void *vertexBuffer; 
extern int ignoreAbort;







static void gdFsErrorCallback(void *obj, Sint32 err)
{
	// check if the user opened the tray..
	if (globalAbortFlag == 0)
	{
		// was the tray opened?
		if (err == GDD_ERR_TRAYOPEND || err == GDD_ERR_UNITATTENT)
		{
			globalAbortFlag = 1;
//			if((!ignoreAbort) && (overlayLoadingDisplay >= OVL_LOAD_MAX))
//				resetToBootROM();
		}
	}
}



/*	--------------------------------------------------------------------------------
	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);
	vertexBuffer = dwDataPtr;

	// 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 * 2;		// 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);
  	acostable = fileLoad("asintabl.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_SUBROUTINE main_bankswitch_startrou;
int main_bankswitch_overlay;
VOID_SUBROUTINE menuStart2pointer = menuStart2;

// rqd for the menu to get to the subgames
VOID_SUBROUTINE fireworksStartPointer = fireworksStart;
VOID_SUBROUTINE catapultStartPointer = catapultStart;
VOID_SUBROUTINE seesawStartPointer = seesawStart;
VOID_SUBROUTINE engineStartPointer = engineStart;
VOID_SUBROUTINE constructStartPointer = constructStart;
VOID_SUBROUTINE wingsStartPointer = wingStart;
VOID_SUBROUTINE crateStartPointer = crateStart;
VOID_SUBROUTINE dogStartPointer = dogchaseStart;
VOID_SUBROUTINE elStartPointer = elStart;



// Immediately ensure the overlay and change the cycle routines again...
void main_BankswitchCycle()
{
	DB("Bankswitch Cycle\n");
	main_EnsureOverlay(main_bankswitch_overlay);
	main_bankswitch_startrou();
}
// This will never get called, because bankswitchcycle always changes the cycle routine again, straight away.
// (I've included it anyway as a safety measure)
void main_BankswitchDraw()
{
}

// Allow an overlay-to-overlay cycle routine swap
// (Note - this needs to return to the SOURCE overlay)
void main_OverlayCycleChange(int overlay, VOID_SUBROUTINE start_rou)
{
	DB("Cycle Changing\n");
	cycle_routine = main_BankswitchCycle;
	draw_routine = main_BankswitchDraw;
	main_bankswitch_startrou = start_rou;
	main_bankswitch_overlay = overlay;
}





void languageInit(void)
{
int res, lang;

#if TERRIT == 1

	do
	{
		res = syCfgGetLanguage(&lang);			// Get the language from the Dreamcast settings
	}while(res != SYD_CFG_OK);
	ASSERT(res == SYD_CFG_OK);				// Make sure the we retrieved the language okay...

	switch(lang)
	{
		case SYD_CFG_JAPANESE :
			gameTextLang=LANG_F;
			break;

		case SYD_CFG_ENGLISH :
			gameTextLang=LANG_F;
			break;

		case SYD_CFG_GERMAN :
			gameTextLang=LANG_D;
			break;

		case SYD_CFG_FRENCH :
			gameTextLang=LANG_F;
			break;

		case SYD_CFG_SPANISH :
			gameTextLang=LANG_SP;
			break;

		case SYD_CFG_ITALIAN :
			gameTextLang=LANG_IT;
			break;

		default:
			gameTextLang=LANG_F;
			break;
	} // end-switch
#else

#if PALMODE == 1
	gameTextLang = LANG_UK;
#else
	gameTextLang = LANG_US;
#endif
#endif
}

extern int disableEggFlip;
void displayAdxScreen()
{
	TIM_HEADER *adx;

	disableEggFlip = 1;
#if PALMODE==1
	dcTim_Load(&adx,"SHELL\\S_A640PB.TIM");
#else
	dcTim_Load(&adx,"SHELL\\S_A640NB.TIM");
#endif
	loop_ticker = 0;
	ScreenFade(0,255,30);

	while(loop_ticker++ < 4*60)
	{
		kmBeginScene(&kmSystemConfig);
		kmBeginPass(&vertexBufferDesc);
		inDraw = YES;

		dcTim_Draw(0,0,640,480,adx);
		DrawScreenTransition();

		if(loop_ticker == 40)
		{
			padHandler();
			ShowLCDLogoOnAllCards();
			debug_printf("start font load\n");
			font = fontLoad("SHELL\\FONT12.FON");//must be after psxstrips

			texNumAfterFont=DCKnumTextures;

			textureLoadBank("SHELL\\DCBITS.SPT");
			{
				TextureBankType *generic_textures;
					TextureType *sprs[9];
					char *names[9] = {"Y_BUTTON","B_BUTTON","A_BUTTON","X_BUTTON", "L1", "L2", "R1", "R2", "STARTBUTTON"};
					int i;

					generic_textures = textureLoadInOneGo("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

			languageInit();
   			gameTextInit("SHELL\\GAMETEXT.TXT", TEXTSTR_MAX, LANG_FILE_NUMLANGS, gameTextLang);	// 4 is number of langs, gameTextLang is one we want

//			ScreenFade(255,0,30);
			keepFade = YES;

			// ma - load the generic sound bank
			sound_LoadLanguageBank(gameTextLang);

#ifdef ECTS_DEMO
			ectsmenuStart();
#else
			loop_ticker = 0;
			startupStart();
			preTitle = NO;
			break;
//		menuStart();
//		vmucheckStart();
#endif

		}


		inDraw = NO;
		kmEndPass(&vertexBufferDesc);
	  	kmRender(KM_RENDER_FLIP);
		kmEndScene(&kmSystemConfig);
	}

	keepFade = NO;
	dcTim_Unload(&adx);
	loop_ticker = 0;
	disableEggFlip = 0;
}

char	message[256];

Sint32 soundMode;
int resetDebounce = 0;
extern int videoLanguage;
int softResetAllowed = 0;
int softresetlimit = 2;
void main()
{
	void *old_cycle;
	char *cfgBuf=NULL;
	int numLines,globalDeathStore;


	#ifdef __GNUC__
	shinobi_workaround();
	#endif



	mwPlyPreInitSofdec();
	vidInfo.ap=&ap_obj;

	//all video init stuff now done in usrinitvideo
	vidInfo.vtxbuf = UsrInitVideo(&vidInfo.mode, &vidInfo.frame, &vidInfo.count, &vidInfo.latency);
	displayMode = vidInfo.mode;

//	gdFsEntryErrFuncAll((void *)ApGdErrFunc, vidInfo.ap);

	// Initialise the ADX stuff......
	ADXT_Init();

	// **jim** - you may want to check this fn, make sure it doesn't cause problems for your sfx stuff
	// JIM : no conflicts as far as I can tell.....
	// sdMemBlkSetTransferMode(SDE_MEMBLK_TRANSFER_MODE_DMA);
	UsrInitSound();
	UsrSetVsyncFunc((void (*)(void))ApVsyncFunc);

	gdFsEntryErrFuncAll(gdFsErrorCallback, (void *)0);

	//inits the middleware
	ApInitMw(vidInfo.ap, vidInfo.mode, vidInfo.frame, vidInfo.count, vidInfo.latency, vidInfo.vtxbuf);

    Init32Malloc();

	// ==== Initialise the buffer used for the config stuff
	cfgBuf = syMalloc(16 * 1024);
	ASSERT(cfgBuf);
	syCfgInit(cfgBuf);
	syCfgGetSoundMode(&soundMode);

	if(soundMode == SYD_CFG_STEREO)
		optionData.sound.stereo = TRUE;
	else
		optionData.sound.stereo = FALSE;
	optionsHitOptions(OPTIONS_HIT_STEREO);


	InitLookupTables();
	utilInitCRC();
	utilSeedRandomInt(398623);
	sfxInitialise(SFX_REVERB_MODE_OFF);


//ok


	actorInitialise();
  	psiInitialise(20);
	initialisePsxStrips();
	cardInitialise();

	mikeyInit();
	emptyInit();
/*
	debug_printf("start font load\n");
	font = fontLoad("SHELL\\FONT12.FON");//must be after psxstrips
	textureLoadBank("SHELL\\DCBITS.SPT");
	{
		TextureBankType *generic_textures;
			TextureType *sprs[9];
			char *names[9] = {"Y_BUTTON","B_BUTTON","A_BUTTON","X_BUTTON", "L1", "L2", "R1", "R2", "STARTBUTTON"};
			int i;

			generic_textures = textureLoadInOneGo("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

	languageInit();
   	gameTextInit("SHELL\\GAMETEXT.TXT", TEXTSTR_MAX, LANG_FILE_NUMLANGS, gameTextLang);	// 4 is number of langs, gameTextLang is one we want
*/
	frame = 0;
	loop_ticker = 0;
	loop_ticker2 = 0;


	particleInitialiseList();

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


#ifndef DEBUG
	videoLanguage = NO;
	skipable = 0;
	noEggFade = YES;
  	videoPlayStream("BLITZBIG.SFD");
	skipable = 1;
  	videoPlayStream("EIDOS20FPS.SFD");
  	videoPlayStream("DW.SFD");
  	videoPlayStream("AA.SFD");
	noEggFade = NO;
#endif

	displayAdxScreen();
	originalTexCount = DCKnumTextures;
	syCfgExit();
	syFree(cfgBuf);


/*
	// ma - load the generic sound bank
	sound_LoadLanguageBank(gameTextLang);

#ifdef ECTS_DEMO
		ectsmenuStart();
#else
		startupStart();
//		menuStart();
//		vmucheckStart();
#endif
*/
	InitTimerbar();

	InitCam();//dc fn - do we need?

	// render loop
	while((globalAbortFlag == 0) && (ignoreAbort == 0))
	{
		kmBeginScene(&kmSystemConfig);
		kmBeginPass(&vertexBufferDesc);
		inDraw = YES;

		//output the available sound mem
//		sprintf(message,"SFX: %d",sfxGetFreeSoundMemory());
//		fontPrint(font, 0,0, message, 255,255,255);

		DCTIMER_START(0);

		padHandler();
		digistick_debounced = padData.debounce[0];

#if PALMODE==1
		if(softResetAllowed > softresetlimit)
#else
		if((firstPad != -1) && (padData.present[firstPad]) && (softResetAllowed > softresetlimit))
#endif
		{
			if((padData.digital[0] & (PAD_START + PAD_CROSS + PAD_CIRCLE + PAD_SQUARE + PAD_TRIANGLE)) == PAD_START + PAD_CROSS + PAD_CIRCLE + PAD_SQUARE + PAD_TRIANGLE)
			{
				if(resetDebounce == 0)
				{
					resetDebounce = 1;
					if(titleScreen)
					{
						globalAbortFlag = 1;
						resetToBootROM();
					}
					else
					{
						pauseEndQuitGame();
	//					menuStart();
					}
					padData.digital[0] = 0;
					padData.debounce[0] = 0;
				}
			}
			else
				resetDebounce = 0;
		}

		old_cycle = cycle_routine;

		if( vmuBeepStopTimer )
			vmuBeepStopTimer--;

		if( !vmuBeepStopTimer )
			cardBeep( 0, NO );

		if(cycle_routine)
			cycle_routine();

		while(old_cycle != cycle_routine)
		{
			old_cycle = cycle_routine;
			padHandler();
			digistick_debounced = padData.debounce[0];
			padHandleShock();
			cycle_routine();
		}

  		if(draw_routine)
		{
  			draw_routine();
			if((pauseOn) && (cycle_routine != noControllerPauseCycle))
				pauseDraw();
			softResetAllowed++;
		}

		if(firstPad == -1)
		{
#if PALMODE==1
			if((!titleScreen) || ((padData.present[0] == 0) && (padData.present[1] == 0) && (padData.present[2] == 0) && (padData.present[3] == 0)))
#endif
			{
				numLines = fontCentredMultiLine(font,0,-12,512 - 40,TEXTSTR(TEXTSTR_STR_DC_START_PAD_NOPAD),128,128,128);
				optionsAlphaRectangle(-246,-17,512-20,numLines*12 + 10,0,0x808080);
			}
		}
		else if(padData.present[firstPad] == 0)
		{
			if(cycle_routine != noControllerPauseCycle)
				noControllerPauseStart();


			globalDeathStore = globalDeathOn;
			globalDeathOn = 0;
			numLines = fontCentredMultiLine(font,0,-12,512 - 40,TEXTSTR(TEXTSTR_STR_DC_PAD_NOT_PRESENT),128,128,128);
			optionsAlphaRectangle(-246,-17,512-20,numLines*12 + 10,0,0x808080);
			globalDeathOn = globalDeathStore;
		}

		if(((palMode) && (noControllerInputTime > 25*300)) || ((!palMode) && (noControllerInputTime > 30*300)))
		{
			if(keepFade == 0)
			{
				ScreenFade(255,100,10);
				keepFade = 1;
			}
		}


		DrawScreenTransition();


		soundCycle();

		loop_ticker++;
		loop_ticker2++;
		if(playing_ticker_enabled)
			gameSaveData.time_spent_playing++;


		//////////////////////////////////////////////////

		DCTIMER_STOP(0);


//		DCTIMER_PRINTS();
		
		inDraw = NO;
		kmEndPass(&vertexBufferDesc);
	  	kmRender(KM_RENDER_FLIP);
		kmEndScene(&kmSystemConfig);

		timersWait();
	}
	
	// shutdown
	bpAmShutdown();
	syChainDeleteHandler(usrg_chid);

	// exit the system
	sbExitSystem();

	// forcibly displays the main menu screen of the BootROM
	syBtExit();

}


void resetToBootROM()
{
	if(inDraw)
	{
		kmEndPass(&vertexBufferDesc);
  		kmRender(KM_RENDER_FLIP);
		kmEndScene(&kmSystemConfig);
	}

	bpAmShutdown();
	syChainDeleteHandler(usrg_chid);
	sbExitSystem();
	syBtExit();

	while(1);
}

void mainDebugMenuVideo(int n)
{
}

#ifdef WAIT_FOR_BUTTON
void waitForButton(int i)
{
	if(padData.digital[0] & PAD_L2)
	{
		do
		{
			padHandler();
		}while(padData.debounce[0] == 0);
	}
}
#endif
