///////////////////////////////////////////////////////////////////////////////
/// @file DDirect3D.hpp
///
/// @brief class for Direct3D8 driver
///
/// @author Lightning
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Aug 2001
/// @author Lightning
/// @remarks Creation
///
/// @date May 2002
/// @author Assassin
/// @remarks Sub-VBuffer modifications
///
///////////////////////////////////////////////////////////////////////////////

#ifndef DDIRECT3D_HPP
#define DDIRECT3D_HPP

#pragma comment(lib, "d3dx8")

#define DEDRIVER_DLL_EXPORTS
#define USING_DESTINY3D

//needed global headers
#include <d3d8.h>
#include <d3dx8.h>
#include "Destiny3D.hpp"
#include "deGlobalTypes.hpp"

//include the needed driver headers
#include "deDriver_priv.hpp"
#include "deList.hpp"
#include "deHash.hpp"

static const long deD3D8_MAX_LIGHTS = 8;

//the Direct3D class
class deDirect3D8 : public deDriver
{
public:
	//constructor and destructor
	deDirect3D8(long ID);
	~deDirect3D8();
	long Release();
	deBoolean Init(HWND WindowHandle);

	//adapter and mode counts for adapters
	const deDriverCaps* GetCaps(long Adapter) const;
	long GetAdapterCount() const;
	long GetModeCount(long Adapter) const;
	deBoolean WindowedAllowed(long Adapter) const;

	//display settings
	deBoolean SetDisplay(deDisplay *Display);
	deBoolean Reset(deDisplay *Display = NULL);
	deBoolean GetCurrentDisplay(long Adapter, deDisplay * Display) const;
	deBoolean GetDisplayMode(long Adapter, long Mode, deDisplay *Display) const;
	deBoolean ValidFormat(long Adapter, Resource ResourceType, BPP BPPType, ZPP ZPPType, BPP FrontBuffer, long ExtraFlag) const;
	
	//vertex buffer functions
	deBoolean AddVertexBuffer(IdeVertexBuffer *Vertex);
	deBoolean RemoveVertexBuffer(IdeVertexBuffer *Vertex);
	deBoolean UpdateVertexBuffer(IdeVertexBuffer *Vertex);

	// Shader functions
	deBoolean AddShader(IdeShader * Shader);
	deBoolean SetShader(IdeShader * Shader);
	deBoolean RemoveShader(IdeShader * Shader);

	//frame functions
	deBoolean BeginFrame();
	deBoolean EndFrame();
	deBoolean ClearFrame(deARGB Color, deBoolean ClearTarget, deBoolean ClearZBuffer, deBoolean ClearStencil);
	deBoolean Render(IdeVertexBuffer *Vertex, long IndexOffset, IdeVertexBuffer *IndexOverride);

	//viewport/FOV functions
	deBoolean SetViewport(long X, long Y, long Width, long Height, float MinZ, float MaxZ);
	deBoolean SetDepthRange(deFloat MinZ, deFloat MaxZ);
	deBoolean SetFOVClipping(float FOVY, float Aspect, float ClipNear, float ClipFar);
	deBoolean SetOrthoClipping(float Width, float Height, float ClipNear, float ClipFar, deBoolean Invert);
	deBoolean SetFOVClippingSubRect(deFloat FOVY, deFloat Aspect, deFloat ClipNear, deFloat ClipFar, deRect SubRect);
	deBoolean SetFOVClippingSubRectFloat(deFloat FOVY, deFloat Aspect, deFloat ClipNear, deFloat ClipFar, deFloatRect SubRect);
	deBoolean SetOrthoClippingSubRect(deFloat Width, deFloat Height, deFloat ClipNear, deFloat ClipFar, deBoolean Invert, deRect SubRect);
	deBoolean SetOrthoClippingSubRectFloat(deFloat Width, deFloat Height, deFloat ClipNear, deFloat ClipFar, deBoolean Invert, deFloatRect SubRect);
	
	//camera translation functions
	deBoolean SetCameraTransform(deTransformInfo *M);
	deBoolean SetCameraTransform(deVertex *Eye, deVertex *At, deVertex *Up);
	deBoolean SetCameraTranslation(deVertex *Translation);
	deBoolean SetCameraRotation(deVertex *Rotation);
	deBoolean ResetCameraIdentity();

	//world translation functions
	deBoolean SetWorldTransform(deTransformInfo * Mat);
	deBoolean SetWorldTransform(deVertex *Eye, deVertex *At, deVertex *Up);
	deBoolean SetWorldTranslation(deVertex *Translation);
	deBoolean SetWorldRotation(deVertex *Rotation);
	deBoolean ResetWorldIdentity();

	deBoolean SetTextureTransform(long StageNum, deTransformInfo * Mat);

	//texture functions
	deBoolean AddBitmap(IdeBitmap *Bitmap);
	deBoolean AddBitmapProxy(IdeBitmapProxy *BitmapProxy);
	deBoolean RemoveBitmap(IdeBitmap *Bitmap);

	//render-to-texture functions
	deBoolean RenderTo(IdeBitmap *Bitmap, long CubeMapFace);
	deBoolean ScreenShot(IdeBitmap * &pBitmap, deBoolean GrabWindow);
	deBoolean GetVideoTexture(IdeBitmap* pBitmap, long CubeMapFace);
	
	//render states
	unsigned long GetRenderState(RenderState State);
	deBoolean SetRenderState(const IdeRenderState *State);
	deBoolean SetRenderState(RenderState State, unsigned long Value);
	deBoolean EnableLight(long LightID, deBoolean Enable);
	deBoolean DisableAllLights();
	
	//driver info
	deDriverInfo* GetInfo() const;
	IdeDriver::Types GetType() const;

	void InvalidateRenderStates(deBoolean Valid);
private:
	struct VertexShaderEntry
	{
		long						ID;				//ID to verify the structure
		unsigned long				VertexShader;	//the vertex shader ID
		unsigned long *				ShaderInfo;		//the shader command info
		long						BufferFlags;	//record of which buffers are in it
		// everything below has to go
	};
	struct VertexBufferEntry
	{
		long						ID;				//ID to verify the structure
		long						EntrySize[IdeVertexBuffer::BUFFER_COUNT-1];		//size of each entry
		LPDIRECT3DVERTEXBUFFER8 	Buffers[IdeVertexBuffer::BUFFER_COUNT-1];		//the actual buffers to render
		LPDIRECT3DINDEXBUFFER8		IndexBuffer;	//the index buffer
		D3DPRIMITIVETYPE			RenderType;		//type of rendering to do
		long						PointCount;		//number of points
		long						TriCount;		//number of tris
		long						IndexCount;		//number of index entries
		long						PointStart;
		long						IndexStart;
		long						FrameUsed;
		deBoolean					SubBuffer;
	};

	struct TextureEntry
	{
		long					ID;				//ID to verify the struct
		long					NumFaces;		//if in a cubemap, the face ID
		long					FrameUsed;
		LPDIRECT3DBASETEXTURE8	Texture;		//pointer to the texture
		LPDIRECT3DSURFACE8		DepthStencil;	//pointer (possibly null) to depth-stencil surface if the texture is render-able
	};

	// static variables
	static long				s_AdapterCount;			//number of adapters
	static int *			s_ModeCount;			//total number of modes
	static deBoolean *		s_WindowPossible;		//if each adapter can do windowed
	static HINSTANCE		s_libD3D8;
	static LPDIRECT3D8		s_D3D;
	static D3DCAPS8 *		s_DevCaps;
	static deDriverCaps*	s_Caps;
	static long				s_NumDevices;

	//private variables
	IDirect3DDevice8 *			m_D3DDevice;
	long						m_AdapterNum;
	
	// cached entries to avoid state changes
	deBoolean					m_DeviceLost;
	deBoolean					m_RenderStatesValid;
	deTList <VertexShaderEntry> m_ShaderList;
	unsigned long				m_LastShader;
	deBoolean					m_TextureRendered;
	deBoolean					m_LightsEnabled[deD3D8_MAX_LIGHTS];
	IdeRenderTexture*			m_CachedTexture;
	long						m_FrameNum;

	// cached data
	D3DMATRIX m_WorldMatrix;
	D3DMATRIX m_CameraMatrix;
	
	// some optimizations and leak relief
	IdeRenderMaterial::MatSource_t	m_AmbientSource, m_EmissiveSource;
	IdeRenderMaterial::MatSource_t	m_DiffuseSource, m_SpecularSource;
	deTHashInt <IdeBitmap*>			m_TexturesList;
	deTHashInt <IdeVertexBuffer*>	m_VBufferList;
	LPDIRECT3DSURFACE8				m_pZBuffer;
	IdeBitmap*						m_pCurrentTarget;
	
	//private functions
	void ReportErrorHR(HRESULT hr);
	deBoolean TestDeviceStatus(deBoolean * DeviceCanReset);
	deBoolean ResetDevice(D3DPRESENT_PARAMETERS *D3DParams);
	deBoolean ReleaseVideoMemResources();
	deBoolean BuildD3DParams(deDisplay * Display, D3DPRESENT_PARAMETERS *D3DParams);

	deBoolean SetTextureState(IdeRenderTexture *State);
	deBoolean SetLightState(IdeRenderLight *State);
	deBoolean SetMaterialState(IdeRenderMaterial *State);

	deBoolean FillBufferInfo(VertexBufferEntry *BufferEntry, IdeVertexBuffer *Vertex, IdeVertexBuffer::BufferType BufferType, deBoolean WipeBuffer);
	deBoolean ProcessVertexBuffer(IdeVertexBuffer *Vertex, VertexBufferEntry *VertexBufferInfo, long BufferFlags);

	deBoolean CreateDefaultCachedTexture();

	// manages shader pool and creates/finds one for the specified flags
	VertexShaderEntry * GetFixedFunctionShader(IdeVertexBuffer *Vertex);
	VertexShaderEntry * LookUpFFShader(long BufferFlags) const;
	VertexShaderEntry * MakeFFShader(long BufferFlags);
	long DeleteShaders();
	//convert to/from D3D BPP
	D3DFORMAT ToD3DBPP(IdeDriver::BPP BPPType) const;
	IdeDriver::BPP FromD3DBPP(D3DFORMAT BPPType) const;
	D3DFORMAT ToD3DZPP(IdeDriver::ZPP ZPPType) const;
	IdeDriver::ZPP FromD3DZPP(D3DFORMAT ZPPType) const;
	D3DMATERIALCOLORSOURCE ToD3DMatSource(IdeRenderMaterial::MatSource_t source) const;
};

char* __cdecl HR2String(HRESULT hr, char* szHR);

extern "C"
{
	//include the all important APIs so that we can be used
#ifdef _DEBUG
	__declspec(dllexport) deBoolean IsDestiny3DDriverDebug();
#else
	__declspec(dllexport) deBoolean IsDestiny3DDriver();
#endif
	__declspec(dllexport) IdeDriver	*GetDriverInstance(long ID);
	__declspec(dllexport) const deDriverInfo *GetDriverInfo(long StructSize);
};

#endif