/*
Copyright (C) 2002 Rice1964

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

void RSP_GBI1_Vtx(Gfx *gfx)
{
	uint32 addr = RSPSegmentAddr((gfx->gbi1vtx.addr));
	uint32 v0  = gfx->gbi1vtx.v0;
	uint32 n   = gfx->gbi1vtx.n;

	if (addr > g_dwRamSize)
	{
		return;
	}

	if ((v0 + n) > 80)
	{
		return;
	}

	ProcessVertexData(addr, v0, n);
	status.dwNumVertices += n;
	DisplayVertexInfo(addr, v0, n);
}

void RSP_GBI1_ModifyVtx(Gfx *gfx)
{
	SP_Timing(RSP_GBI1_ModifyVtx);

	if( gRSP.ucode == 5 && ((gfx->words.w0)&0x00FFFFFF) == 0 && ((gfx->words.w1)&0xFF000000) == 0x80000000 )
	{
		DLParser_Bomberman2TextRect(gfx);
	}
	else
	{
		uint32 dwWhere = ((gfx->words.w0) >> 16) & 0xFF;
		uint32 dwVert   = (((gfx->words.w0)      ) & 0xFFFF) / 2;
		uint32 dwValue  = (gfx->words.w1);

		if( dwVert > 80 )
		{
			return;
		}

		// Data for other commands?
		switch (dwWhere)
		{
		case RSP_MV_WORD_OFFSET_POINT_RGBA:			// Modify RGBA
		case RSP_MV_WORD_OFFSET_POINT_XYSCREEN:		// Modify X,Y
		case RSP_MV_WORD_OFFSET_POINT_ZSCREEN:		// Modify C
		case RSP_MV_WORD_OFFSET_POINT_ST:			// Texture
			ModifyVertexInfo(dwWhere, dwVert, dwValue);
			break;
		default:
			break;
		}
	}
}

void RSP_GBI1_Tri2(Gfx *gfx)
{
	status.primitiveType = PRIM_TRI2;
	bool bTrisAdded = false;
	bool bTexturesAreEnabled = CRender::g_pRender->IsTextureEnabled();

	// While the next command pair is Tri2, add vertices
	uint32 dwPC = gDlistStack[gDlistStackPointer].pc;

	do {
		// Vertex indices are multiplied by 10 for Mario64, by 2 for MarioKart
		uint32 dwV0 = gfx->gbi1tri2.v0/gRSP.vertexMult;
		uint32 dwV1 = gfx->gbi1tri2.v1/gRSP.vertexMult;
		uint32 dwV2 = gfx->gbi1tri2.v2/gRSP.vertexMult;

		uint32 dwV3 = gfx->gbi1tri2.v3/gRSP.vertexMult;
		uint32 dwV4 = gfx->gbi1tri2.v4/gRSP.vertexMult;
		uint32 dwV5 = gfx->gbi1tri2.v5/gRSP.vertexMult;

		// Do first tri
		if (IsTriangleVisible(dwV0, dwV1, dwV2))
		{
			if (!bTrisAdded)
			{
				if( bTexturesAreEnabled )
			{
				PrepareTextures();
				InitVertexTextureConstants();
			}
				CRender::g_pRender->SetCombinerAndBlender();
				bTrisAdded = true;
			}
			PrepareTriangle(dwV0, dwV1, dwV2);
		}

		// Do second tri
		if (IsTriangleVisible(dwV3, dwV4, dwV5))
		{
			if (!bTrisAdded)
			{
				if( bTexturesAreEnabled )
			{
				PrepareTextures();
				InitVertexTextureConstants();
			}
				CRender::g_pRender->SetCombinerAndBlender();
				bTrisAdded = true;
			}
			PrepareTriangle(dwV3, dwV4, dwV5);
		}
		
		gfx++;
		dwPC += 8;
	} while( gfx->words.cmd == (uint8)RSP_TRI2);


	gDlistStack[gDlistStackPointer].pc = dwPC-8;


	if (bTrisAdded)	
	{
		CRender::g_pRender->DrawTriangles();
	}
}

extern D3DXVECTOR4 g_vtxNonTransformed[MAX_VERTS];

void RSP_GBI1_BranchZ(Gfx *gfx)
{
	SP_Timing(RSP_GBI1_BranchZ);

	uint32 vtx = ((gfx->words.w0)&0xFFF)>1;
	float vtxdepth = g_vecProjected[vtx].z/g_vecProjected[vtx].w;

	if( vtxdepth <= (s32)(gfx->words.w1) || g_curRomInfo.bForceDepthBuffer )
	{
		uint32 dwPC = gDlistStack[gDlistStackPointer].pc;		// This points to the next instruction
		uint32 dwDL = *(uint32 *)(g_pRDRAMu8 + dwPC-12);
		uint32 dwAddr = RSPSegmentAddr(dwDL);

		dwAddr = RSPSegmentAddr(dwDL);;

		gDlistStack[gDlistStackPointer].pc = dwAddr;
		gDlistStack[gDlistStackPointer].countdown = MAX_DL_COUNT;
	}
}

void RSP_GBI1_LoadUCode(Gfx *gfx)
{
	SP_Timing(RSP_GBI1_LoadUCode);

	uint32 dwPC = gDlistStack[gDlistStackPointer].pc;
	uint32 dwUcStart = RSPSegmentAddr((gfx->words.w1));
	uint32 dwSize = ((gfx->words.w0)&0xFFFF)+1;
	uint32 dwUcDStart = RSPSegmentAddr(*(uint32 *)(g_pRDRAMu8 + dwPC-12));

	uint32 ucode = DLParser_CheckUcode(dwUcStart, dwUcDStart, dwSize, 8);
	RSP_SetUcode(ucode, dwUcStart, dwUcDStart, dwSize);

}

void RSP_GFX_Force_Matrix(uint32 dwAddr)
{
	if (dwAddr + 64 > g_dwRamSize)
	{
		return;
	}

	// Load matrix from dwAddr
	LoadMatrix(dwAddr);

	CRender::g_pRender->SetWorldProjectMatrix(matToLoad);

}


void DisplayVertexInfo(uint32 dwAddr, uint32 dwV0, uint32 dwN)
{
}

void RSP_MoveMemLight(uint32 dwLight, uint32 dwAddr)
{
	if( dwLight >= 16 )
	{
		return;
	}
	s8 * pcBase = g_pRDRAMs8 + dwAddr;
	uint32 * pdwBase = (uint32 *)pcBase;

	gRSPn64lights[dwLight].dwRGBA		= pdwBase[0];
	gRSPn64lights[dwLight].dwRGBACopy	= pdwBase[1];
	gRSPn64lights[dwLight].x			= pcBase[8 ^ 0x3];
	gRSPn64lights[dwLight].y			= pcBase[9 ^ 0x3];
	gRSPn64lights[dwLight].z			= pcBase[10 ^ 0x3];

	/*
	{
		// Normalize light
		double sum = (double)gRSPn64lights[dwLight].x * gRSPn64lights[dwLight].x;
		sum += (double)gRSPn64lights[dwLight].y * gRSPn64lights[dwLight].y;
		sum += (double)gRSPn64lights[dwLight].z * gRSPn64lights[dwLight].z;

		sum = sqrt(sum);
		sum = sum/128.0;

		gRSPn64lights[dwLight].x /= sum;
		gRSPn64lights[dwLight].y /= sum;
		gRSPn64lights[dwLight].z /= sum;
	}
	*/
					
	if (dwLight == gRSP.ambientLightIndex)
	{
		uint32 dwCol = COLOR_RGBA( (gRSPn64lights[dwLight].dwRGBA >> 24)&0xFF,
					  (gRSPn64lights[dwLight].dwRGBA >> 16)&0xFF,
					  (gRSPn64lights[dwLight].dwRGBA >>  8)&0xFF, 0xff);

		SetAmbientLight( dwCol );
	}
	else
	{
		SetLightCol(dwLight, gRSPn64lights[dwLight].dwRGBA);
		if (pdwBase[2] == 0)	// Direction is 0!
		{
		}
		SetLightDirection(dwLight, 
			gRSPn64lights[dwLight].x,
			gRSPn64lights[dwLight].y,
			gRSPn64lights[dwLight].z);
	}
}

void RSP_MoveMemViewport(uint32 dwAddr)
{
	if( dwAddr+16 >= g_dwRamSize )
	{
		return;
	}

	short scale[4];
	short trans[4];

	// dwAddr is offset into RD_RAM of 8 x 16bits of data...
	scale[0] = *(short *)(g_pRDRAMu8 + ((dwAddr+(0*2))^0x2));
	scale[1] = *(short *)(g_pRDRAMu8 + ((dwAddr+(1*2))^0x2));
	scale[2] = *(short *)(g_pRDRAMu8 + ((dwAddr+(2*2))^0x2));
	scale[3] = *(short *)(g_pRDRAMu8 + ((dwAddr+(3*2))^0x2));

	trans[0] = *(short *)(g_pRDRAMu8 + ((dwAddr+(4*2))^0x2));
	trans[1] = *(short *)(g_pRDRAMu8 + ((dwAddr+(5*2))^0x2));
	trans[2] = *(short *)(g_pRDRAMu8 + ((dwAddr+(6*2))^0x2));
	trans[3] = *(short *)(g_pRDRAMu8 + ((dwAddr+(7*2))^0x2));


	int nCenterX = trans[0]/4;
	int nCenterY = trans[1]/4;
	int nWidth   = scale[0]/4;
	int nHeight  = scale[1]/4;

	// Check for some strange games
	if( nWidth < 0 )	nWidth = -nWidth;
	if( nHeight < 0 )	nHeight = -nHeight;

	int nLeft = nCenterX - nWidth;
	int nTop  = nCenterY - nHeight;
	int nRight= nCenterX + nWidth;
	int nBottom= nCenterY + nHeight;

	//LONG maxZ = scale[2];
	int maxZ = 0x3FF;

	CRender::g_pRender->SetViewport(nLeft, nTop, nRight, nBottom, maxZ);
}


// S2DEX uses this - 0xc1
void RSP_S2DEX_SPObjLoadTxtr_Ucode1(Gfx *gfx)
{
	SP_Timing(RSP_S2DEX_SPObjLoadTxtr_Ucode1);

	// Add S2DEX ucode supporting to F3DEX, see game DT and others
	status.bUseModifiedUcodeMap = true;
	RSP_SetUcode(1);
	memcpy( &LoadedUcodeMap, &ucodeMap1, sizeof(UcodeMap));
	
	LoadedUcodeMap[S2DEX_OBJ_MOVEMEM] = &RSP_S2DEX_OBJ_MOVEMEM;
	LoadedUcodeMap[S2DEX_OBJ_LOADTXTR] = &RSP_S2DEX_SPObjLoadTxtr;
	LoadedUcodeMap[S2DEX_OBJ_LDTX_SPRITE] = &RSP_S2DEX_SPObjLoadTxSprite;
	LoadedUcodeMap[S2DEX_OBJ_LDTX_RECT] = &RSP_S2DEX_SPObjLoadTxRect;
	LoadedUcodeMap[S2DEX_OBJ_LDTX_RECT_R] = &RSP_S2DEX_SPObjLoadTxRectR;

	RSP_S2DEX_SPObjLoadTxtr(gfx);
}
