#include "standard.h"
#include "global.h"
#include "winmain.h"
#include "texture.h"
#include "drawprimitive.h"
#include "hwrender.h"
#include "input.h"
#include "hwinsert.h"



/*** globals ***/
defglobal D3DTLVERTEX* CurrentTLVertex;
defglobal DWORD GhCurTextureHandle;
defglobal bool GtColorKeyEnabled;
defglobal DWORD GahTextureHandle[MAX_D3D_TEXTURES];
defglobal bool GtZBufferWriteEnabled;
defglobal bool GtZBufferCompareEnabled;

/*** locals ***/

D3DTLVERTEX VertexBuffer[MAX_TLVERTICES];
D3DTLVERTEX UnRollBuffer[MAX_TLVERTICES];

int LanTextureHandle[MAX_D3D_TEXTURES];
int LnMaxPolys=0;
int LnMaxVertices=0;
int LhPalette=-1;
D3DRENDERSTATETYPE LnAlphaEnableRenderState;

#ifdef GAMEDEBUG
local bool LtInScene=false;
#endif

/*** code ***/

void HWR_InitState()
{
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_FILLMODE,HWConfig.nFillMode);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE,HWConfig.nShadeMode);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAG,HWConfig.Filter);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_DITHERENABLE,HWConfig.Dither);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREPERSPECTIVE,HWConfig.Perspective);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREADDRESS,D3DTADDRESS_CLAMP);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND,D3DTBLEND_MODULATE);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_CULLMODE,D3DCULL_CCW);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,D3DBLEND_SRCALPHA);
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_INVSRCALPHA);
	LnAlphaEnableRenderState=D3DRENDERSTATE_ALPHABLENDENABLE; 
	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_SPECULARENABLE,false);

	//G_TextureSettings.tAlphaTransparency=true;
	
	HWR_ResetCurrentTexture();
	HWR_ResetColorKey();
//	HWR_ResetZBuffer();
}

void HWR_ResetCurrentTexture()
{
	GhCurTextureHandle=0;
	
	SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE,0);
	
//	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE,0);
//	App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_FLUSHBATCH,0);	// hahahahaha
}

void HWR_ResetColorKey()
{
	GtColorKeyEnabled=false;
	
	if(DXD3D(App.DeviceInfoPtr,App.DXConfigPtr).bAlpha)	
		SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,false);
	else
	//if(DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).bAlpha)	
		SetRenderState(D3DRENDERSTATE_COLORKEYENABLE,false);
}

void HWR_ResetZBuffer()
{

	GtZBufferWriteEnabled=false;
	GtZBufferCompareEnabled=false;
	//if(App.DXConfigPtr->bZBuffer)
	//{
	if(App.lpZBuffer)
		App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_ZFUNC,D3DCMP_ALWAYS);
	else
	{
		App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE,false);
		App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE,false);
	}
	//}
}



void HWR_SetCurrentTexture(DWORD dwHandle)
{
	if (dwHandle!=GhCurTextureHandle)
	{
//		App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE,dwHandle);
		SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE,dwHandle);

//		SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE,0);
		
//		SetTextureHandle(0);
		GhCurTextureHandle=dwHandle;
	}
}

void HWR_EnableColorKey(bool tEnable)
{
#ifdef GAMEDEBUG
//	if (rawkey(DIK_LBRACKET))
//		tEnable=true;
#endif
//	if (tEnable!=GtColorKeyEnabled)
//	{

	if(DXD3D(App.DeviceInfoPtr,App.DXConfigPtr).bAlpha	&& DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).bAlpha)	
	{
		SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,tEnable);
	}
	else if(DXD3D(App.DeviceInfoPtr,App.DXConfigPtr).bAlpha	&& ! DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).bAlpha)	
	{
		SetRenderState(D3DRENDERSTATE_COLORKEYENABLE,tEnable);
	}
	else
		SetRenderState(D3DRENDERSTATE_COLORKEYENABLE,tEnable);
			
	
//		GtColorKeyEnabled=tEnable;
//	}



}



void HWR_EnableZBuffer(bool tWrite,bool tCompare)
{

	if(App.lpZBuffer)
	{
		if (tWrite!=GtZBufferWriteEnabled)
		{
			App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE,tWrite);
			GtZBufferWriteEnabled=tWrite;
		}
		if (tCompare!=GtZBufferCompareEnabled)
		{
			if (App.lpZBuffer)
				App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_ZFUNC,tCompare?D3DCMP_LESSEQUAL:D3DCMP_ALWAYS);
			else
				App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_ZENABLE,tCompare);
			GtZBufferCompareEnabled=tCompare;
		}
	}
}



void HWR_BeginScene()
{
#ifdef GAMEDEBUG
	if (LtInScene)
		S_ExitSystem("HWR_BeginScene: Already in scene!");
	LtInScene=true;
//	G_Direct3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND,rawkey(DIK_RBRACKET)?D3DTBLEND_MODULATEALPHA:D3DTBLEND_MODULATE);
#endif

	HWR_GetAllTextureHandles();

	//DX_DoFlipWait();
//	Log("Texture Swaps %d",texchange);
//	texchange = 0;
	
	BeginScene();
	
	if(App.lpZBuffer != 0)
		InitBuckets();

}



void HWR_EndScene()
{
	
	EndScene();

#ifdef GAMEDEBUG
	if (!LtInScene)
		S_ExitSystem("HWR_EndScene: Not in scene!");
	LtInScene=false;
#endif
}




inline void HWR_DrawRoutine(sint16* pInfo)
{
	int nTPage,nV;
	int nRoutine;
	D3DTLVERTEX* pV;

	nRoutine=*pInfo++;

	switch (nRoutine)
		{
			case DRAW_TLV_GT:
			case DRAW_TLV_WGT:
				nTPage=pInfo[0];
				nV=int(pInfo[1]);
				pV=*((D3DTLVERTEX**)(pInfo+2));
				HWR_SetCurrentTexture(GahTextureHandle[nTPage]);
				HWR_EnableColorKey(nRoutine==DRAW_TLV_WGT);
				DrawPrimitive(D3DPT_TRIANGLEFAN,D3DVT_TLVERTEX,pV,nV,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
				break;

			case DRAW_TLV_G:
				nV=int(pInfo[1]);
				pV=*((D3DTLVERTEX**)(pInfo+2));
				HWR_SetCurrentTexture(0);
				HWR_EnableColorKey(false);
				DrawPrimitive(D3DPT_TRIANGLEFAN,D3DVT_TLVERTEX,pV,nV,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
				break;

			case DRAW_TLV_L:
				nV=int(pInfo[1]);
				pV=*((D3DTLVERTEX**)(pInfo+2));
				HWR_SetCurrentTexture(0);
				HWR_EnableColorKey(false);
				DrawPrimitive(D3DPT_LINESTRIP,D3DVT_TLVERTEX,pV,nV,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
				break;

			case DRAW_TLV_GA:
				nV=int(pInfo[1]);
				pV=*((D3DTLVERTEX**)(pInfo+2));
				HWR_SetCurrentTexture(0);
				DWORD dwAlphaEnabled;
				App.lpD3DDevice->GetRenderState(LnAlphaEnableRenderState,&dwAlphaEnabled);
				App.lpD3DDevice->SetRenderState(LnAlphaEnableRenderState,true);
				DrawPrimitive(D3DPT_TRIANGLEFAN,D3DVT_TLVERTEX,pV,nV,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
				App.lpD3DDevice->SetRenderState(LnAlphaEnableRenderState,dwAlphaEnabled);
				break;

			case DRAW_TLV_GTA:
				nTPage=pInfo[0];
				nV=int(pInfo[1]);
				pV=*((D3DTLVERTEX**)(pInfo+2));
				HWR_SetCurrentTexture(GahTextureHandle[nTPage]);
				HWR_EnableColorKey(true);
				App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,D3DBLEND_ONE);
				App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_ONE);
				SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,true);
				DrawPrimitive(D3DPT_TRIANGLEFAN,D3DVT_TLVERTEX,pV,nV,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
				SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,false);
				App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,D3DBLEND_SRCALPHA);
				App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_INVSRCALPHA);
				break;
		
		}

}




inline void HWR_DrawUnRolledList(int cnt,D3DTLVERTEX* pVB,int nRoutine,int nTPage)
{
	switch (nRoutine)
	{
		case DRAW_TLV_GT:
		case DRAW_TLV_WGT:
			HWR_SetCurrentTexture(GahTextureHandle[nTPage]);
			if(nRoutine == DRAW_TLV_WGT)
			{
				HWR_EnableZBuffer(true,true);
				HWR_EnableColorKey(true);
			}
			DrawPrimitive(D3DPT_TRIANGLELIST,D3DVT_TLVERTEX,pVB,cnt,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
			if(nRoutine == DRAW_TLV_WGT)
				HWR_EnableZBuffer(false,true);
			break;

		case DRAW_TLV_G:
			HWR_SetCurrentTexture(0);
			HWR_EnableColorKey(false);
			DrawPrimitive(D3DPT_TRIANGLELIST,D3DVT_TLVERTEX,pVB,cnt,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
			break;

		case DRAW_TLV_GA:
			HWR_SetCurrentTexture(0);
			App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,true);
			DrawPrimitive(D3DPT_TRIANGLELIST,D3DVT_TLVERTEX,pVB,cnt,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
			App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,false);
			break;

		case DRAW_TLV_GTA:
			HWR_SetCurrentTexture(GahTextureHandle[nTPage]);
			HWR_EnableColorKey(true);
			App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,D3DBLEND_ONE);
			App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_ONE);
			SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,true);
			DrawPrimitive(D3DPT_TRIANGLELIST,D3DVT_TLVERTEX,pVB,cnt,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
			SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,false);
			App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,D3DBLEND_SRCALPHA);
			App.lpD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_INVSRCALPHA);
			break;

		case DRAW_TLV_L:
			HWR_SetCurrentTexture(0);
			HWR_EnableColorKey(false);
			DrawPrimitive(D3DPT_LINELIST,D3DVT_TLVERTEX,pVB,cnt,D3DDP_DONOTCLIP|D3DDP_DONOTUPDATEEXTENTS);
			break;
	}
}



/*
void HWR_DrawPolyList()
	{
	sint32* pSort;
	sint16* pInfo;
	int nSurf;

	pSort=(sint32*)sort3d_buffer;
	for (nSurf=0;nSurf<surfacenum;nSurf++)
	{
		pInfo=(sint16*)(*pSort);
		HWR_DrawRoutine(pInfo);
		pSort+=2;
	}

}

*/

void HWR_DrawPolyListFB(int num,sint32* sortptr)
{
	sint32* pSort;
	sint16* pInfo;
	int nSurf;

	
	pSort=(sint32*)sortptr+((num<<1)-2);
	
	for(nSurf=0;nSurf<num;nSurf++)
	{
		pInfo=(sint16*)(*pSort);
		HWR_DrawRoutine(pInfo);	
		pSort-=2;
	}
}

void HWR_DrawPolyList(int num,sint32* sortptr)
{
	sint32* pSort;
	sint16* pInfo;
	int nSurf;

	
	pSort=(sint32*)sortptr;
	
	for(nSurf=0;nSurf<num;nSurf++)
	{
		pInfo=(sint16*)(*pSort);
		HWR_DrawRoutine(pInfo);	
		pSort+=10;
	}
}




inline void TLVERT_To_TLVERT(D3DTLVERTEX* s,D3DTLVERTEX* d)
{
	d->sx       = s->sx;
	d->sy       = s->sy;
	d->sz       = s->sz;
	d->rhw      = s->rhw;
	d->tu       = s->tu;
	d->tv       = s->tv;
	d->color    = s->color;
	d->specular = s->specular;
}



void HWR_DrawPolyListBF(int num,sint32* sortptr)
{
	sint32* pSort;
	sint16* pInfo;
	int nSurf;
	D3DTLVERTEX* pV;
	int Routine1,Tpage1;
	int Routine2,Tpage2;
	int nV,n;
	int vbcnt;

	if(num==0) return;

	pSort = (sint32*)sortptr;
	
	vbcnt    = 0;
	pInfo    = (sint16*) (*pSort);
	Routine1 = *pInfo++;
	Tpage1   = pInfo[0];
	nV       = pInfo[1];
	pV       = *((D3DTLVERTEX**)(pInfo+2));

	for(nSurf=0;nSurf<num;nSurf++)
	{
		pInfo    = (sint16*) (*pSort);
		Routine2 = *pInfo++;
		Tpage2   = pInfo[0];
		nV		 = pInfo[1];
		pV       = *((D3DTLVERTEX**)(pInfo+2));
		
		if(Routine2 != Routine1 || Tpage1 != Tpage2)
		{
			HWR_DrawUnRolledList(vbcnt,&UnRollBuffer[0],Routine1,Tpage1);						
			vbcnt    = 0;
			Routine1 = Routine2;
			Tpage1   = Tpage2;
		}
		
		if(Routine1 != DRAW_TLV_L)
		{
			// Add Fan Into VertexBuffer List
						
			D3DTLVERTEX* pFanStart;
			pFanStart = pV;

			TLVERT_To_TLVERT(pV,&UnRollBuffer[vbcnt+0]);
			pV++;
			TLVERT_To_TLVERT(pV,&UnRollBuffer[vbcnt+1]);
			pV++;
			TLVERT_To_TLVERT(pV,&UnRollBuffer[vbcnt+2]);

			vbcnt += 3;

			nV -= 3;
			for(n=nV;n>0;n--)
			{
				TLVERT_To_TLVERT(pFanStart,&UnRollBuffer[vbcnt+0]);
				TLVERT_To_TLVERT(pV,&UnRollBuffer[vbcnt+1]);
				pV++;
				TLVERT_To_TLVERT(pV,&UnRollBuffer[vbcnt+2]);
				vbcnt+=3;
			}
	
		}
		else
		{
			// Add Lines
			D3DTLVERTEX* pFanStart;
			pFanStart = pV;
			TLVERT_To_TLVERT(pV,&UnRollBuffer[vbcnt+0]);
			pV++;
			TLVERT_To_TLVERT(pV,&UnRollBuffer[vbcnt+1]);
			vbcnt += 2;
		}

		pSort   += 10;
	}

	if(vbcnt > 0)
	{
		HWR_DrawUnRolledList(vbcnt,&UnRollBuffer[0],Routine1,Tpage1);						
	}



/*	pSort=(sint32*)sortptr;
	
	for(nSurf=0;nSurf<num;nSurf++)
	{
		pInfo=(sint16*)(*pSort);
		HWR_DrawRoutine(pInfo);	
		pSort+=2;
	}
*/	
}





void HWR_LoadTexturePages(int nPages,char* pImage,uint8* pPalette)
{
	Log(LT_Enter,"HWR_LoadTexturePages()");
	HWR_FreeTexturePages();	// for the moment...

	int nPage,nHandle;
	if (pPalette)
	{
		LhPalette=DXTextureNewPalette(pPalette);
		Log("LhPalette=%d",LhPalette);
	}

	for (nPage=0;nPage<nPages;++nPage)
	{
		if (pPalette)
		{
			nHandle=DXTextureAddPal(256,256,(uint8*)pImage,LhPalette,DXTextureList);
			pImage+=0x10000;
		}
		else
		{
			nHandle=DXTextureAdd(256,256,(uint16*)pImage,DXTextureList,555);
			pImage+=0x20000;
		}
	
		if (nHandle>=0)
			LanTextureHandle[nPage]=nHandle;
		else
			LanTextureHandle[nPage]=-1;
	}

	HWR_GetAllTextureHandles();
}

void HWR_FreeTexturePages()
	{
	Log(LT_Enter,"HWR_FreeTexturePages");
	for (int nPage=0;nPage<ArraySize(LanTextureHandle);++nPage)
		{
		int nHandle=LanTextureHandle[nPage];
		if (nHandle>=0)
			{
			DXTextureFree(nHandle,DXTextureList);
			LanTextureHandle[nPage]=-1;
			}
		GahTextureHandle[nPage]=0;
		}
	if (LhPalette>=0)
		DXTextureFreePalette(LhPalette);
	}



void HWR_GetAllTextureHandles()
{
	for (int nTPage=0;nTPage<ArraySize(LanTextureHandle);++nTPage)
	{
		int nHandle=LanTextureHandle[nTPage];
		if (nHandle>=0)
			GahTextureHandle[nTPage] = DXTextureGetHandle(nHandle,DXTextureList);
		else
			GahTextureHandle[nTPage] = 0;
	}
}

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

void HWR_InitVertexList()
{
	CurrentTLVertex=VertexBuffer;
}


bool HWR_Init()
{
	Log(LT_Enter,"HWR_Init");
	Zero(VertexBuffer);
	for (int nTexture=0;nTexture<ArraySize(LanTextureHandle);++nTexture)
		LanTextureHandle[nTexture]=-1;
	
	return true;
}




