#include "standard.h"
#include "global.h"


#include "init.h"
#include "winmain.h"
#include "debug.h"
#include "hwrender.h"


defglobal char* GpCmdLine;

extern "C" void S_ExitSystem(char *message){
	DXSetCooperativeLevel(App.lpDD,App.WindowHandle,DDSCL_NORMAL);
	MessageBox(App.WindowHandle,message,NULL,MB_OK);
	ShutdownGame();
	strcpy(exit_message,message);
	EndLog();
	exit(1);}


int FMV_Play(char*){return 0;}
int FMV_PlayIntro(char*,char*){return 0;}


//#define MY_MAX_TEXTPAGES	32
//#define MAX_TLVERTICES 8192
//#define VERTEX_LIMIT (MAX_TLVERTICES-512)
local D3DRENDERSTATETYPE LnAlphaEnableRenderState;


bool GtWindowClosed;
int GnDirectDrawLocks;
local bool LtWM_QUIT;
local bool LtActive;
local bool LtMinimized;


bool DD_SpinMessageLoop(bool tWait)
	{
	static int nReEntry=0;
	if (nReEntry)
		Log(LT_Error,"Re-entry #%d into DD_SpinMessageLoop (bad)",nReEntry);
	++nReEntry;
	if (LtWM_QUIT)
		{
		Log(LT_Warning,"WM_QUIT already happened");
		--nReEntry;
		return false;
		}
	if (tWait)
		WaitMessage();
	MSG msg;
#ifdef DBLOG
	bool tLogged=false;
#endif
	for (;;)
		{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
			{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			if (msg.message==WM_QUIT)
				break;
			}
		else if(App.bFocus)
			{
#ifdef DBLOG
			if (tLogged)
				Log(LT_Info,"Reactivated");
#endif
			--nReEntry;
			return true;
			}
		else
			{
#ifdef DBLOG
			if (!tLogged)
				{
				Log(LT_Info,"Inactive in DD_SpinMessageLoop...");
				tLogged=true;
				}
#endif 

			WaitMessage();
			}
		}
	Log(LT_Info,"DD_SpinMessageLoop: WM_QUIT");
	LtWM_QUIT=true;
	GtWindowClosed=true;
	game_closedown=true;		// for Gav presumably
	//GnAppReturnValue=msg.wParam;
	--nReEntry;
	return false;
}



HRESULT DD_LockSurface(IDirectDrawSurface3* pSurf,DDSURFACEDESC& rDDSD,DWORD dwFlags)
	{
	InitDXStruct(rDDSD);
	HRESULT err=pSurf->Lock(NULL,&rDDSD,dwFlags,NULL);
	//if (DX_TRY(err))
	//	Log(LT_Error,"DD_LockSurface failed");
	//else
	//	{
		//++GnDirectDrawLocks;
		//Log(LT_Info,"Locked: %08x,%d",rDDSD.lpSurface,rDDSD.lPitch);
	//	}
	return err;
	}

// DD_Unlock: Finish with hard byte access to the surface.
// You MUST call this when you've finished hitting the bitmap, or everything will crash.
// Args:
//	pSurf:		ptr to surface you want to access
//	pDDSD:		ptr to a DDSURFACEDESC structure, to receive lpSurface, lPitch & all other surface info
// Returns:
//	DD_OK or DDERR_whatever
// Notes:
//	This could fail, but I doubt if anyone cares.  It would be very wierd for it to fail...
HRESULT DD_UnlockSurface(IDirectDrawSurface3* pSurf,DDSURFACEDESC& rDDSD)
	{
	HRESULT err;
	err=pSurf->Unlock(rDDSD.lpSurface);
	//if (!DX_TRY(err))	// needs log buffering
	//	if (--GnDirectDrawLocks==0)
	//		FlushLogStore();
	return err;
	}


//** ToDo Change to a Blit

bool DD_CopyBitmapToSurface(IDirectDrawSurface3* pSurf,void* pvSrc)
	{
	DDSURFACEDESC desc;
	if (!DX_FAILED(DD_LockSurface(pSurf,desc)))
		{
		int nWidth=desc.dwWidth;
		int nHeight=desc.dwHeight;
		char* pSrc=(char*)pvSrc;
		char* pDest=(char*)desc.lpSurface;
		while (nHeight--)
			{
			memcpy(pDest,pSrc,nWidth);
			pSrc+=nWidth;
			pDest+=desc.lPitch;
			}
		DD_UnlockSurface(pSurf,desc);
		return true;
		}
	return false;
	}




HRESULT DD_CreateSurface(DDSURFACEDESC& ddsd,IDirectDrawSurface3*& rpSurf)
	{
	IDirectDrawSurface* pSurf;
	HRESULT err=App.lpDD->CreateSurface(&ddsd,&pSurf,NULL);
	if (DX_TRY(err))
		return err;
	err=pSurf->QueryInterface(IID_IDirectDrawSurface3,(void**)&rpSurf);
	pSurf->Release();
	return err;
	}



HRESULT DD_EnsureSurfaceAvailable(IDirectDrawSurface3* pSurf,IDirectDrawSurface3* pTopSurf,bool tClearIfLost)
{
	HRESULT err=pSurf->IsLost();
	if (err==DDERR_SURFACELOST)
		{
		Log(LT_Warning,"Surface lost... trying to restore");
		if (!pTopSurf)
			pTopSurf=pSurf;
		err=pTopSurf->Restore();
		Log(LT_Warning,"Restore %s",SUCCEEDED(err)?"succeeded":"failed");
		if (tClearIfLost && SUCCEEDED(err))
			{
			if (!DD_ClearSurface(pSurf))
				Log(LT_Error,"Can't clear restored surface");
			}
		}
	return err;
}


bool DD_ClearSurface(IDirectDrawSurface3* pSurf,RECT* pRect,DWORD dwColour)
{
	DDBLTFX bltfx;
	InitDXStruct(bltfx);
	bltfx.dwFillColor=dwColour;
	if (DX_TRY(pSurf->Blt(pRect,NULL,NULL,DDBLT_COLORFILL|DDBLT_WAIT,&bltfx)))
		return false;
	return true;
}


DWORD DD_PixelFromRGB(PixelFormat& rPF,int nR,int nG,int nB)
{
	return ((nR>>(8-rPF.nRBits))<<rPF.nRShift)|((nG>>(8-rPF.nGBits))<<rPF.nGShift)|((nB>>(8-rPF.nBBits))<<rPF.nBShift);
}

DWORD DD_PixelFromRGBA(PixelFormat& rPF,int nR,int nG,int nB,int nA)
{
	return ((nR>>(8-rPF.nRBits))<<rPF.nRShift)|((nG>>(8-rPF.nGBits))<<rPF.nGShift)|((nB>>(8-rPF.nBBits))<<rPF.nBShift)|((nA>>(8-rPF.nABits))<<rPF.nAShift);
}


local void CalcBitsAndShift(DWORD dwBitMask,int& rnBits,int& rnShift)
	{
	if (dwBitMask)
		{
		int nCount=0;
		while (!(dwBitMask&1))
			{
			dwBitMask>>=1;
			++nCount;
			}
		rnShift=nCount;
		nCount=0;
		while (dwBitMask)
			{
			dwBitMask>>=1;
			++nCount;
			}
		rnBits=nCount;
		}
	else	
		rnBits=rnShift=0;
	}

void PixelFormat::SetFromDDPF(DDPIXELFORMAT& rDDPF)
	{
	dwRMask=rDDPF.dwRBitMask;
	dwGMask=rDDPF.dwGBitMask;
	dwBMask=rDDPF.dwBBitMask;
	dwAMask=rDDPF.dwRGBAlphaBitMask;
	CalcBitsAndShift(dwRMask,nRBits,nRShift);
	CalcBitsAndShift(dwGMask,nGBits,nGShift);
	CalcBitsAndShift(dwBMask,nBBits,nBShift);
	CalcBitsAndShift(dwAMask,nABits,nAShift);
	}




void DX_DoFlipWait()
{
	while(App.lpFrontBuffer->GetFlipStatus(DDGFS_ISFLIPDONE)==DDERR_WASSTILLDRAWING);
}


void DX_ClearBuffers(DWORD dwClrFlags,DWORD dwColour)
	{
	
	RECT rc;
	rc.left=rc.top=0;
	rc.right=DXDisplayMode(App.DeviceInfoPtr,App.DXConfigPtr).w;
	rc.bottom=DXDisplayMode(App.DeviceInfoPtr,App.DXConfigPtr).h;

	if (App.nRenderMode==RENDERER_HAL)
		{
		DWORD dwFlags=0;
		if (dwClrFlags&DXCB_BACK)
			dwFlags=D3DCLEAR_TARGET;
		if (dwClrFlags&DXCB_ZBUFFER)
			dwFlags|=D3DCLEAR_ZBUFFER;
		if (dwFlags)
			{
			D3DRECT rc3d;
			rc3d.x1=rc.left;
			rc3d.y1=rc.top;
			rc3d.x2=rc.right;
			rc3d.y2=rc.bottom;
			App.lpViewPort->Clear(1,&rc3d,dwFlags);
			}
		}
	else if (dwClrFlags&DXCB_BACK)
		DD_ClearSurface(App.lpBackBuffer,&rc,dwColour);
	if (dwClrFlags&DXCB_FRONT)
		DD_ClearSurface(App.lpFrontBuffer,&rc,dwColour);
//	if (dwClrFlags&DXCB_THIRD)
//		DD_ClearSurface(G_ThirdBuffer,&rc,dwColour);
//	if (dwClrFlags&DXCB_RENDER)
//		DD_ClearSurface(G_RenderBuffer,&rc,dwColour);

	if (dwClrFlags&DXCB_PICTURE)
		{
		rc.left=rc.top=0;
		rc.right=640; rc.bottom=480;
		DD_ClearSurface(App.lpPictureBuffer,&rc,dwColour);
		}
/*
	if (dwClrFlags&DXCB_CLIENT)
		{
		rc.left=GnWindowPosX;
		rc.top=GnWindowPosY;
		rc.right=GnWindowPosX+GnWindowWidth;
		rc.bottom=GnWindowPosY+GnWindowHeight;
		DD_ClearSurface(G_FrontBuffer,&rc,dwColour);
		}
*/
 }




bool DX_CheckForLostSurfaces(void)
	{

	if (!App.lpFrontBuffer)
		S_ExitSystem("Oops... no front buffer");
	bool tRestart=DX_TRY(DD_EnsureSurfaceAvailable(App.lpFrontBuffer,0,true));

	
//	if (G_AppSettings.tFullScreen || G_AppSettings.nRenderMode==RENDERER_HAL)
		tRestart=tRestart||DX_TRY(DD_EnsureSurfaceAvailable(App.lpBackBuffer,App.lpFrontBuffer,true));

//	if (G_AppSettings.tTripleBuffering)
//		tRestart=tRestart||DX_TRY(DD_EnsureSurfaceAvailable(G_ThirdBuffer,G_FrontBuffer,true));
//	if (G_AppSettings.nRenderMode==RENDERER_INTERNAL)
//		tRestart=tRestart||DX_TRY(DD_EnsureSurfaceAvailable(G_RenderBuffer));
//	if (G_ZBuffer)
	if(App.lpZBuffer!=0)		
		tRestart=tRestart||DX_TRY(DD_EnsureSurfaceAvailable(App.lpZBuffer,0));
//	if (G_PictureBuffer)
		tRestart=tRestart||DX_TRY(DD_EnsureSurfaceAvailable(App.lpPictureBuffer));
	if (tRestart && !GtWindowClosed)
		{
	//	Log(LT_Info,"At least one surface cannot be restored... Restarting");
	//	DX_Restart();
		if (App.nRenderMode==RENDERER_HAL)
			HWR_GetAllTextureHandles();
		}
    return tRestart;

//	return true;
	}


#include "../game/text.h"

void CSDisplay();


extern int VertsCalced;
extern bool bShowfcnt;
extern int Drawn;

bool DX_UpdateFrame(bool tSpinMessageLoop,RECT* prcWindow)
	{
//	if (!prcWindow)
//		prcWindow=&G_RenderInfo.rcRender;
	DX_CheckForLostSurfaces();
//	if (G_AppSettings.tFullScreen)
//		{
//		if (G_AppSettings.nRenderMode==RENDERER_INTERNAL)
//			{
//			G_BackBuffer->Blt(prcWindow,G_RenderBuffer,prcWindow,DDBLT_WAIT,NULL);
//			}

	
		// Display Console
//		CSDisplay();

//		if(bShowfcnt)		

//		WinDisplayString(prcWindow->right-70,prcWindow->top,"fps %.2f",WinFrameRate());
//		WinDisplayString(prcWindow->left,prcWindow->top,"td %d",Drawn);

		IDirectDrawSurface3* pSrcSurf;
	
		RECT r;

		r.top = 0;
		r.left = 0;
		r.right = DXDisplayMode(App.DeviceInfoPtr,App.DXConfigPtr).w;
		r.bottom = DXDisplayMode(App.DeviceInfoPtr,App.DXConfigPtr).h;


		if(DXD3D(App.DeviceInfoPtr,App.DXConfigPtr).bHardware)
			App.lpFrontBuffer->Flip(NULL,DDFLIP_WAIT);
		else
		{
			pSrcSurf= App.lpBackBuffer;
			App.lpFrontBuffer->Blt(prcWindow,pSrcSurf,prcWindow,DDBLT_WAIT,NULL);
		}




//		}
//	else
//		{
//		RECT rcDest;
//		rcDest.left=GnWindowPosX+prcWindow->left;
//		rcDest.top=GnWindowPosY+prcWindow->top;
//		rcDest.right=GnWindowPosX+prcWindow->right;
//		rcDest.bottom=GnWindowPosY+prcWindow->bottom;
//		IDirectDrawSurface3* pSrcSurf;
//		if (G_AppSettings.nRenderMode==RENDERER_INTERNAL)
//			pSrcSurf=G_RenderBuffer;
//		else
//			pSrcSurf=G_BackBuffer;
//		G_FrontBuffer->Blt(&rcDest,pSrcSurf,prcWindow,DDBLT_WAIT,NULL);
//		}
	if (tSpinMessageLoop)
		return DD_SpinMessageLoop();
	return true;
	}



void SaveDDBuffer(IDirectDrawSurface3 *pSurf)
	{
	static int nFile=0;
	static char header[18]={0,0,2,0,0,0,0,0,0,0,0,0,0x40,0x01,0x00,0x01,16,0};
	char filename[16];
	unsigned int	i,j,k;
	int				r,g,b;
	short* p,* p1,* pd;
	FILE* handle;

	DDSURFACEDESC ddsd;
	InitDXStruct(ddsd);
	if (DX_TRY(pSurf->GetSurfaceDesc(&ddsd)))
		return;
	//if (ddsd.ddpfPixelFormat.dwRGBBitCount==8)
	//	{
	//	SaveScreen();
	//	return;
	//	}
	//if (ddsd.ddpfPixelFormat.dwRGBBitCount!=16)
	//	{
	//	Log("I can only do 8 or 16 bit screen shots");
	//	return;
	//	}
	InitDXStruct(ddsd);
	if (!DX_FAILED(DD_LockSurface(pSurf,ddsd)))
		{
		p=(short*)ddsd.lpSurface;
		sprintf(filename,"tomb%04d.tga",nFile++);
		handle=fopen(filename,"wb");
		if (handle)
			{
			p1=(short*)&header[12];
			*p1++=short(ddsd.dwWidth);
			*p1=short(ddsd.dwHeight);
  			fwrite(header,18,1,handle);

			pd=(short*)malloc_ptr;
			p+=(ddsd.lPitch/2)*ddsd.dwHeight;
			for (i=0;i<ddsd.dwHeight;i++) 
				{
				for(j=0;j<ddsd.dwWidth;j++)
					{
					k=p[j];
					if (ddsd.ddpfPixelFormat.dwRBitMask==0xf800)	// 565 format
						{
						r=(k>>11)&0x1f;
						g=(k>>6)&0x1f;
						b=k&0x1f;
						*pd++=(r<<10)+(g<<5)+b;
						}
					else											// 555 format
						*pd++ = k;
					}
				p-=ddsd.lPitch/2;
				}
			fwrite(malloc_ptr,ddsd.dwWidth*ddsd.dwHeight*2,1,handle);
			fclose(handle);
			++filename[7];
			if(filename[7]>'9') 
				{
				filename[7]='0';
				++filename[6];
				}
			}
		DD_UnlockSurface(pSurf,ddsd);
		}
	else
	{
		Log("Failed Lock....");
	}

	}



