#include <shinobi.h>
#include <kamui2.h>
#include <km2util.h>
#include <sg_sycbl.h>
#include <sg_sytmr.h>
#include "dispstrex.h"
#include "key.h"
#include <string.h>

/* Read From Texture */
extern KMSURFACEDESC Texture[];

#define TEXTUREMEMORYSIZE 	(0x400000)
#define VERTEXBUFFERSIZE	(0x80000)
#define TEXTUREBUFSIZE		(256 * 256 * 2 + 2048)

/*
	Prototypes
*/
Int UserMain(void);
Int UserInit(void);

/*
	Global Params
*/

KMVERTEXBUFFDESC 		VertexBufferDesc;
KMSYSTEMCONFIGSTRUCT 	SystemConfig;
KMSURFACEDESC           PrimarySurfaceDesc;
KMSURFACEDESC           BackSurfaceDesc;
KMDWORD					FBarea[24576 + 19456];
								/* FBstruct 4096 Entry (= 4096 * 24 / 4) *
								 * VQstruct 1024 Entry (= 1024 * 76 / 4) */

NJS_PERIPHERAL 			*per;

KMDWORD					frameCount = 0;

void main(void)
{
    SYE_CBL_CABLE cable;

#ifdef __GNUC__
	shinobi_workaround();
#endif
    /* Initialize Video Mode */
    cable = syCblCheckCable();
    if (cable == SYE_CBL_CABLE_VGA) {
		sbInitSystem((int)KM_DSPMODE_VGA, (int)KM_DSPBPP_RGB565, 1);
    } else {
		sbInitSystem((int)KM_DSPMODE_NTSCNI640x480, (int)KM_DSPBPP_RGB565, 1);
    }

	UserInit();

	/* Render Loop */
	while(UserMain())
	{
		pdExecPeripheralServer();
	}
	sbExitSystem();
}

/*
*		Init
*/
#define RGBAColour(r, g, b, a)   ((unsigned long) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)))
/***************************************
 *	VertexType01用KMSTRIPHEADの構築
 ***************************************/

void	InitStripContext01(
								PKMSTRIPCONTEXT pStripContext,
								PKMSTRIPHEAD	pStripHead
)
{
	pStripContext->nSize						 					= sizeof(KMSTRIPCONTEXT);
	pStripContext->StripControl.nListType		 					= KM_OPAQUE_POLYGON;
	pStripContext->StripControl.nUserClipMode	 					= KM_USERCLIP_DISABLE;
	pStripContext->StripControl.nShadowMode		 					= KM_NORMAL_POLYGON;
	pStripContext->StripControl.bGouraud		 					= KM_TRUE;
	pStripContext->ObjectControl.nDepthCompare			 			= KM_GREATER;
	pStripContext->ObjectControl.nCullingMode			 			= KM_NOCULLING;
	pStripContext->ObjectControl.bZWriteDisable			 			= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nSRCBlendingMode	= KM_ONE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nDSTBlendingMode	= KM_ZERO;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bSRCSelect			= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bDSTSelect			= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nFogMode			= KM_FOGTABLE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bColorClamp		= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bUseAlpha			= KM_FALSE;

	kmGenerateStripHead01( pStripHead,pStripContext );
}

/***************************************
 *	VertexType03用KMSTRIPHEADの構築
 ***************************************/
void	InitStripContext03(
								PKMSTRIPCONTEXT pStripContext,
								PKMSTRIPHEAD	pStripHead,
								PKMSURFACEDESC	pTextureDesc
)
{
	pStripContext->nSize						 					= sizeof(KMSTRIPCONTEXT);
	pStripContext->StripControl.nListType		 					= KM_OPAQUE_POLYGON;
	pStripContext->StripControl.nUserClipMode	 					= KM_USERCLIP_DISABLE;
	pStripContext->StripControl.nShadowMode		 					= KM_NORMAL_POLYGON;
	pStripContext->StripControl.bOffset								= KM_FALSE;
	pStripContext->StripControl.bGouraud		 					= KM_TRUE;
	pStripContext->ObjectControl.nDepthCompare			 			= KM_GREATER;
	pStripContext->ObjectControl.nCullingMode			 			= KM_NOCULLING;
	pStripContext->ObjectControl.bZWriteDisable						= KM_FALSE;
	pStripContext->ObjectControl.bDCalcControl			 			= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nSRCBlendingMode	= KM_ONE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nDSTBlendingMode	= KM_ZERO;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bSRCSelect			= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bDSTSelect			= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nFogMode			= KM_FOGTABLE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bColorClamp		= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bUseAlpha			= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bIgnoreTextureAlpha= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nFlipUV			= KM_NOFLIP;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nClampUV			= KM_NOCLAMP;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nFilterMode		= KM_BILINEAR;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].bSuperSampleMode	= KM_FALSE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].dwMipmapAdjust		= KM_MIPMAP_D_ADJUST_1_00;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].nTextureShadingMode= KM_MODULATE;
	pStripContext->ImageControl[KM_IMAGE_PARAM1].pTextureSurfaceDesc= pTextureDesc;

	kmGenerateStripHead03( pStripHead,pStripContext );
}

void SetBackground (PKMSURFACEDESC TextureID)
{
	KMSTRIPCONTEXT		StripContextBG01;		/* for BG					*/
	KMSTRIPHEAD			StripHeadBG01;			/* for BG					*/
	KMSTRIPCONTEXT		StripContextBG03;		/* for BG					*/
	KMSTRIPHEAD			StripHeadBG03;			/* for BG					*/
	KMVERTEX_01			BV01[3];				/* for NO-TEXTURE-GOURAUD	*/
	KMVERTEX_03			BV03[3];				/* for TEXTURE-GOURAUD		*/

	if(TextureID == (PKMSURFACEDESC)NULL)
	{
		/* No Texture(VertexType01) */
		memset( &StripContextBG01	, 0, sizeof(StripContextBG01));
		memset( &StripHeadBG01		, 0, sizeof(&StripHeadBG01));
		InitStripContext01	(&StripContextBG01,&StripHeadBG01);

		/* No texture */
		/* Vertex #0 */
		BV01[0].ParamControlWord	= KM_VERTEXPARAM_NORMAL;
		BV01[0].fX					=   0.0f;
		BV01[0].fY					= 479.0f;
		BV01[0].u.fZ				=   0.0001f;
		BV01[0].fBaseAlpha			=   0.0f;
		BV01[0].fBaseRed			=   0.1f;
		BV01[0].fBaseGreen			=   0.1f;
		BV01[0].fBaseBlue			=   0.5f;

		/* Vertex #1 */
		BV01[1].ParamControlWord	= KM_VERTEXPARAM_NORMAL;
		BV01[1].fX					= 320.0f;
		BV01[1].fY					=   0.0f;
		BV01[1].u.fZ				=   0.0001f;
		BV01[1].fBaseAlpha			=   0.0f;
		BV01[1].fBaseRed			=   0.1f;
		BV01[1].fBaseGreen			=   0.1f;
		BV01[1].fBaseBlue			=   0.30f;

		/* Vertex #2 */
		BV01[2].ParamControlWord	= KM_VERTEXPARAM_ENDOFSTRIP;
		BV01[2].fX					= 639.0f;
		BV01[2].fY					= 479.0f;
		BV01[2].u.fZ				=   0.0001f;
		BV01[2].fBaseAlpha			=   0.0f;
		BV01[2].fBaseRed			=   0.0f;
		BV01[2].fBaseGreen			=   0.0f;
		BV01[2].fBaseBlue			=   0.5f;

		/* Set BG Plane */
		kmSetBackGround(	&StripHeadBG01,
							KM_VERTEXTYPE_01,
							(PVOID)&BV01[0],
							(PVOID)&BV01[1],
							(PVOID)&BV01[2]	);
	}
	else
	{
		/* Texture(VertexType03) */
		memset( &StripContextBG03	, 0, sizeof(StripContextBG03));
		memset( &StripHeadBG03		, 0, sizeof(&StripHeadBG03));
		InitStripContext03	(&StripContextBG03,&StripHeadBG03,TextureID);

		/* Use texture */
		/* Vertex #0 */
		BV03[0].ParamControlWord	= KM_VERTEXPARAM_NORMAL;
		BV03[0].fX					=   0.0f;
		BV03[0].fY					=   0.0f;
		BV03[0].u.fZ				=   0.0001f;
		BV03[0].fU					=   0.0f;
		BV03[0].fV					=   0.0f;
		BV03[0].uBaseRGB.dwPacked	= RGBAColour (255, 255, 255, 255);
		BV03[0].uOffsetRGB.dwPacked	= RGBAColour (  0, 0, 0, 0);

		/* Vertex #1 */
		BV03[1].ParamControlWord	= KM_VERTEXPARAM_NORMAL;
		BV03[1].fX					= 640.0f;
		BV03[1].fY					=   0.0f;
		BV03[1].u.fZ				=   0.0001f;
		BV03[1].fU					=   1.0f;
		BV03[1].fV					=   0.0f;
		BV03[1].uBaseRGB.dwPacked	= RGBAColour (255, 255, 255, 255);
		BV03[1].uOffsetRGB.dwPacked	= RGBAColour (0, 0, 0, 0);

		/* Vertex #2 */
		BV03[2].ParamControlWord	= KM_VERTEXPARAM_ENDOFSTRIP;
		BV03[2].fX					= 640.0f;
		BV03[2].fY					= 480.0f;
		BV03[2].u.fZ				=   0.0001f;
		BV03[2].fU					=   1.0f;
		BV03[2].fV					=   1.0f;
		BV03[2].uBaseRGB.dwPacked	= RGBAColour (255, 255, 255, 255);
		BV03[2].uOffsetRGB.dwPacked	= RGBAColour (0, 0, 0, 0);

		/* Set BG Plane */
		kmSetBackGround(	&StripHeadBG03,
							KM_VERTEXTYPE_03,
							(PVOID)&BV03[0],
							(PVOID)&BV03[1],
							(PVOID)&BV03[2]	);
	}
}

Int LoadConfiguration(PKMSYSTEMCONFIGSTRUCT pSystemConfig)
{
	char	*pReadBuffer, *pCurrent;
	char	Token[80];
	GDFS	gdfs;
	Sint32 	size;
	Sint32 	ret;

	gdfs = gdFsOpen("Config.sys", NULL);
	if (gdfs == NULL) return 0;

	gdFsGetFileSize(gdfs, &size);
	size = gdFsCalcSctSize(size);

	pReadBuffer = (char *)syMalloc(size * 2048);
	if (pReadBuffer == NULL)
	{
		gdFsClose(gdfs);
		return 0;
	}

	ret = gdFsRead(gdfs, size, pReadBuffer);

	/* close handle */
	gdFsClose(gdfs);

	if(ret != GDD_ERR_OK)
	{
		syFree(pReadBuffer);
		return 0;
	}

	pSystemConfig->flags							= KM_CONFIGFLAG_NOWAITVSYNC | KM_CONFIGFLAG_ENABLE_CLEAR_FRAMEBUFFER | KM_CONFIGFLAG_ENABLE_2V_LATENCY;
	pSystemConfig->nTextureMemorySize 				= TEXTUREMEMORYSIZE;	/* Texture 4MB */
	pSystemConfig->nVertexBufferSize 				= VERTEXBUFFERSIZE;
	pSystemConfig->Pass[0].dwRegionArrayFlag 		= KM_PASSINFO_AUTOSORT;
	pSystemConfig->Pass[0].nDirectTransferList 		= KM_OPAQUE_POLYGON;
	pSystemConfig->Pass[0].fBufferSize[0] =   0.0f;
	pSystemConfig->Pass[0].fBufferSize[1] =   0.0f;
	pSystemConfig->Pass[0].fBufferSize[2] =  50.0f;
	pSystemConfig->Pass[0].fBufferSize[3] =   0.0f;
	pSystemConfig->Pass[0].fBufferSize[4] =  50.0f;

	/* Tokenの切りだし */
	pCurrent = strtok(pReadBuffer,"=");
	do
	{
		strcpy(Token, pCurrent);				/* Command */
		if(strcmp(Token, "VERTEXBUFFERSIZE") == 0)
		{
			pCurrent = strtok(NULL,"\n ");
			pSystemConfig->nVertexBufferSize = atoi(pCurrent);
			if(pSystemConfig->nVertexBufferSize > 0x00800000) pSystemConfig->nVertexBufferSize = 0x00800000; /* 8MB 以上は制限 */
		}
		else if(strcmp(Token, "TEXTURESIZE") == 0)
		{
			pCurrent = strtok(NULL,"\n ");
			pSystemConfig->nTextureMemorySize = atoi(pCurrent);
			if(pSystemConfig->nTextureMemorySize > 0x006A0000 ) pSystemConfig->nVertexBufferSize = 0x006A0000; /* 6.8MB 以上は制限 */
		}
		else
		{
			/* 無効トークンは読み飛ばし */
			pCurrent = strtok(NULL,"\n ");
		}
	}while((pCurrent = strtok(NULL,"\=")) != NULL);

	syFree(pReadBuffer);
	return 1;
}

Int UserInit(void)
{
	PKMSURFACEDESC	pFB[2];
	KMWORKAREASIZE	TexWorkArea;
	KMPACKEDARGB	BorderColor;

	pFB[0] = &PrimarySurfaceDesc;
	pFB[1] = &BackSurfaceDesc;

	if(!LoadConfiguration(&SystemConfig))
	{
		SystemConfig.nVertexBufferSize 		= VERTEXBUFFERSIZE;
		SystemConfig.nTextureMemorySize 	= TEXTUREMEMORYSIZE;	/* Texture 4MB */
		SystemConfig.flags					= KM_CONFIGFLAG_NOWAITVSYNC | KM_CONFIGFLAG_ENABLE_CLEAR_FRAMEBUFFER | KM_CONFIGFLAG_ENABLE_2V_LATENCY;
		/* 1Pass */
		SystemConfig.Pass[0].dwRegionArrayFlag = KM_PASSINFO_AUTOSORT;
		SystemConfig.Pass[0].nDirectTransferList = KM_OPAQUE_POLYGON;
		SystemConfig.Pass[0].fBufferSize[0] =   0.0f;
		SystemConfig.Pass[0].fBufferSize[1] =   0.0f;
		SystemConfig.Pass[0].fBufferSize[2] =  50.0f;
		SystemConfig.Pass[0].fBufferSize[3] =   0.0f;
		SystemConfig.Pass[0].fBufferSize[4] =  50.0f;
	}

	SystemConfig.dwSize					=	sizeof(KMSYSTEMCONFIGSTRUCT);
	SystemConfig.pBufferDesc 			= 	&VertexBufferDesc;
	SystemConfig.ppSurfaceDescArray		=	pFB;
	SystemConfig.fb.nNumOfFrameBuffer	=	2;
	SystemConfig.nNumOfVertexBank		=	2;
	SystemConfig.nPassDepth				= 	1;

	/*
		Texture Memory Settings
	*/
	SystemConfig.nNumOfTextureStruct 	= 4096;
	SystemConfig.nNumOfSmallVQStruct 	= 1024;
	SystemConfig.pTextureWork			= &FBarea[0];

	/*
	 *	Vertex Buffer Settings
	 */
	SystemConfig.pVertexBuffer 			= (PKMDWORD)(((KMDWORD)syMalloc(SystemConfig.nVertexBufferSize) & 0x0FFFFFFFUL ) | 0xA0000000UL);

	kmSetSystemConfiguration(&SystemConfig);

	BorderColor.dwPacked = 0x00000000;
	kmSetBorderColor(BorderColor);

	txt_InitDisplayString();
	txt_ClearScreen();		/* Clear text screen	*/
	PRINTOUT(("Dreamcast Console Version 0.1 Alpha\n"));
	PRINTOUT(("Programmed by M.Imanishi,S.Ohya,S.Uchida\n"));
	PRINTOUT((">"));
	txt_SetBlink( TRUE );
	PRINTOUT(("_"));
	txt_SetBlink( FALSE );

	SetBackground((PKMSURFACEDESC)NULL);

	/* Init Command */
	InitializeCommand();

	SoundInit();
	syRtcInit();

	InitCursor( PDD_PORT_A0 );

	InitMemCommand();			/* mem */
	InitBorderCommand();		/* bordar */
	InitClsCommand();			/* cls */
	InitHelpCommand();			/* Help or ? */
	InitDispTexCommand();		/* loadtex , disptex */
	InitLsCommand();			/* ls */
	InitDpCommand();			/* dp */
	InitDrawBasic();			/* Draw Basic Model */
	InitAdxCommand();
	InitBgCommand();
	InitClockCommand();
}

/*
*		Main Loop
*/
Int UserMain(void)
{
	KeyBoardServer();			/* キーボードサーバーを呼ぶ */
	CursorServer();
	ExecuteProcess(&SystemConfig);			/* Processサーバーを呼ぶ */
	frameCount++;
	SoundServer();			/* Call the server function of sound library */
	return 1;
}