#include "include.h"

/*** defines ***/
//#define PARANOID	// cos I am

/*** globals ***/
defglobal IDirect3D2* G_Direct3D;
defglobal IDirect3DDevice2* G_Direct3DDevice;
defglobal IDirect3DViewport2* G_Viewport;
defglobal IDirect3DMaterial2* G_BlackMaterial;

/*** locals ***/

/*** code ***/

local bool D3D_Create()
	{
	Log(LT_Enter,"D3D_Create");
	if (DX_TRY(G_DirectDraw->QueryInterface(IID_IDirect3D2,reinterpret_cast<void**>(&G_Direct3D))))
		{
		Log(LT_Error,"QueryInterFace(IID_IDirect3D2) failed");
		return false;
		}
	return true;
	}

local void D3D_Release()
	{
	Log(LT_Enter,"D3D_Release");
#ifdef PARANOID
	RELEASECHECK(G_Direct3D);
#endif
	RELEASE(G_Direct3D);
	}

// return true if the driver described by *pDesc can render Lara & stuff in some sort of reasonable manner...
local bool IsSuitable(D3DDEVICEDESC *pDesc,bool tIsHardwareDesc)
	{
#ifdef DBLOG
	char* szType=tIsHardwareDesc?"Hardware":"Software";
#else
#define szType 0
#endif
	DWORD dwFlags;
	dwFlags=pDesc->dwFlags;
	if (!(dwFlags&D3DDD_COLORMODEL))
		{
		Log(LT_Info,"%s: No colour model",szType);
		return false;
		}
	if (!(pDesc->dcmColorModel&D3DCOLOR_RGB))
		{
		Log(LT_Info,"%s: No RGB color model",szType);
		return false;
		}
	if (!(dwFlags&D3DDD_TRICAPS))
		{
		Log(LT_Info,"%s: No tri caps",szType);
		return false;
		}
	D3DPRIMCAPS& triCaps=pDesc->dpcTriCaps;
	if (!(triCaps.dwShadeCaps&D3DPSHADECAPS_COLORGOURAUDRGB))
		{
		Log(LT_Info,"%s: No Gouraud RGB shading",szType);
		return false;
		}
/*	if (!(triCaps.dwTextureBlendCaps&D3DPTBLENDCAPS_DECAL))
		{
		Log(LT_Info,"%s: No texture decal blending",szType);
		return false;
		}*/
	if (!(triCaps.dwTextureBlendCaps&D3DPTBLENDCAPS_MODULATE))
		{
		Log(LT_Info,"%s: No texture modulate blending",szType);
		return false;
		}
/*	if (!(triCaps.dwTextureCaps&D3DPTEXTURECAPS_TRANSPARENCY))
		{
		Log(LT_Info,"%s: No texture transparency",szType);
		return false;
		}*/
	Log(LT_Info,"%s: Found suitable driver",szType);
	return true;
#ifndef DBLOG
#undef szType
#endif
	}

// this is a bit strange, now you can just use the IIDs of the drivers... need to sort it out
local HRESULT WINAPI D3DEnumDeviceCallback(GUID* pGUID,
										   char* pDeviceDescription,
										   char* pDeviceName,
										   D3DDEVICEDESC* pHWDesc,D3DDEVICEDESC* pHELDesc,
										   void* pContext)
	{
	Log(LT_Enter,"D3DEnumDeviceCallback: %s (%s)",pDeviceName,pDeviceDescription);
	DisplayAdapter* pAdapter=static_cast<DisplayAdapter*>(pContext);
	if (pHWDesc && IsSuitable(pHWDesc,true))
		{
		pAdapter->tHAL=true;
		pAdapter->HAL_GUID=*pGUID;
		pAdapter->HAL_Desc=*pHWDesc;
		D3DPRIMCAPS& rPC=pHWDesc->dpcTriCaps;
		pAdapter->tHALPerspectiveCorrectSupported=(rPC.dwTextureCaps&D3DPTEXTURECAPS_PERSPECTIVE)!=0;
		pAdapter->tHALDitherSupported=(rPC.dwRasterCaps&D3DPRASTERCAPS_DITHER)!=0;
		pAdapter->tHALZBufferSupported=(pHWDesc->dwDeviceZBufferBitDepth)!=0;
		pAdapter->tHALBilinearFilteringSupported=(rPC.dwTextureFilterCaps&D3DPTFILTERCAPS_LINEAR)!=0;
		pAdapter->tHALNeedsStippledAlpha=((rPC.dwShadeCaps&(D3DPSHADECAPS_ALPHAFLATBLEND|D3DPSHADECAPS_ALPHAGOURAUDBLEND))==0);
		}
	return D3DENUMRET_OK;
	}

void D3D_FindDrivers(DisplayAdapter& adapter)
	{
	if (D3D_Create())
		{
		G_Direct3D->EnumDevices(D3DEnumDeviceCallback,static_cast<void*>(&adapter));
		D3D_Release();
		}
	}

bool D3D_UpdateViewport()
	{
	Log(LT_Enter,"D3D_UpdateViewport");
	Log("nW=%d; nH=%d; nBW=%d; nBH=%d;",G_RenderInfo.nWidth,G_RenderInfo.nHeight,G_RenderInfo.nBufferWidth,G_RenderInfo.nBufferHeight);
	D3DVIEWPORT2 vpd;
	InitDXStruct(vpd);
	vpd.dwX=0;
	vpd.dwY=0;
	vpd.dwWidth=(DWORD)G_RenderInfo.nWidth;
	vpd.dwHeight=(DWORD)G_RenderInfo.nHeight;
	vpd.dvClipX=0.0f;
	vpd.dvClipY=0.0f;
	vpd.dvClipWidth=float(G_RenderInfo.nWidth);
	vpd.dvClipHeight=float(G_RenderInfo.nHeight);
	vpd.dvMinZ=0.0f;
	vpd.dvMaxZ=1.0f;

	if (DX_TRY(G_Viewport->SetViewport2(&vpd)))
		{
		G_Viewport->GetViewport2(&vpd);
		Log("dwX=%d; dwY=%d; dwWidth=%d; dwHeight=%d;",vpd.dwX,vpd.dwY,vpd.dwWidth,vpd.dwHeight);
		Log("dvCX=%f; dvCY=%f; dvCW=%f; dvCH=%f;",vpd.dvClipX,vpd.dvClipY,vpd.dvClipWidth,vpd.dvClipHeight);
		Log("dvMinZ=%f; dvMaxZ=%f;",vpd.dvMinZ,vpd.dvMaxZ);
		return false;
		}
	if (DX_TRY(G_Direct3DDevice->SetCurrentViewport(G_Viewport)))
		return false;
	return true;
	}

// Start rendering to a different surface.  I don't currently use this but it might be fun...
bool D3D_RenderToSurface(IDirectDrawSurface3* pRenderTarget,IDirectDrawSurface3** ppOldRenderTarget)
	{
	Log(LT_Enter,"D3D_RenderToSurface");
	if (ppOldRenderTarget)
		{
		IDirectDrawSurface3* pOldSurf;
		G_Direct3DDevice->GetRenderTarget((IDirectDrawSurface**)&pOldSurf);	// does this work?
		*ppOldRenderTarget=pOldSurf;
		}
	if (DX_TRY(G_Direct3DDevice->SetRenderTarget((IDirectDrawSurface*)pRenderTarget,0)))	// does this work?
		{
		Log(LT_Error,"SetRenderTarget failed");
		return false;
		}
/*	DDSURFACEDESC ddsd;
	InitDXStruct(ddsd);
	pRenderTarget->GetSurfaceDesc(&ddsd);
	Log("nW=%d; nH=%d;",ddsd.dwWidth,ddsd.dwHeight);
	G_RenderInfo.nWidth=ddsd.dwWidth;
	G_RenderInfo.nHeight=ddsd.dwHeight;
	G_RenderInfo.nBPP=ddsd.ddpfPixelFormat.dwRGBBitCount;
	G_RenderInfo.tPaletted=bool(ddsd.ddpfPixelFormat.dwFlags&DDPF_PALETTEINDEXED8);
	G_RenderInfo.tUse236Palette=G_RenderInfo.tPaletted && !G_AppSettings.tFullScreen;	//(ddsd.ddsCaps.dwCaps&DDSCAPS_PRIMARYSURFACE);
	G_RenderInfo.tIsStandardVGA=bool(ddsd.ddsCaps.dwCaps&DDSCAPS_STANDARDVGAMODE);*/
	return D3D_UpdateViewport();
	}

void D3D_Start(IDirectDrawSurface3* pRenderTarget)
	{
	Log(LT_Enter,"D3D_Start");
	if (!D3D_Create())
		throw INIT_ERR_D3D_Create;

	if (DX_TRY(G_Direct3D->CreateDevice(IID_IDirect3DHALDevice,(IDirectDrawSurface*)pRenderTarget,&G_Direct3DDevice)))
		throw INIT_ERR_CreateDevice;
	if (DX_TRY(G_Direct3D->CreateViewport(&G_Viewport,NULL)))
		throw INIT_ERR_CreateViewport;
	if (DX_TRY(G_Direct3DDevice->AddViewport(G_Viewport)))
		throw INIT_ERR_AddViewport;
	if (!D3D_UpdateViewport())
		throw INIT_ERR_SetViewport2;
	if (DX_TRY(G_Direct3D->CreateMaterial(&G_BlackMaterial,NULL)))
		throw INIT_ERR_CreateViewport;
	D3DMATERIAL mat;
	InitDXStruct(mat);
	if (DX_TRY(G_BlackMaterial->SetMaterial(&mat)))
		throw INIT_ERR_CreateViewport;
	D3DMATERIALHANDLE handle;
	if (DX_TRY(G_BlackMaterial->GetHandle(G_Direct3DDevice,&handle)))
		throw INIT_ERR_CreateViewport;
	if (DX_TRY(G_Viewport->SetBackground(handle)))
		throw INIT_ERR_CreateViewport;
	}

void D3D_Finish()
	{
	Log(LT_Enter,"D3D_Finish");
#ifdef PARANOID
	RELEASECHECK(G_BlackMaterial);
	RELEASECHECK(G_Viewport);
	RELEASECHECK(G_Direct3DDevice);
#endif
	RELEASE(G_BlackMaterial);
	RELEASE(G_Viewport);
	RELEASE(G_Direct3DDevice);
	D3D_Release();
	}

int D3D_Init()
	{
	Log(LT_Enter,"D3D_Init");
	return true;
	}

void D3D_Cleanup()
	{
	Log(LT_Enter,"D3D_Cleanup");
	}

