#pragma once

#define NOT_IMPLEMENTED	STOP(("Can't do this yet")); rc = D3D_OK
#define CHECK_RC return rc


namespace Gos2 {

const c_MaxTextures=8;
const c_MaxStreams=8;


class Graphics : public Gos3D
{

	static	IDirect3D8 *		s_pD3D;
	static	IDirect3DDevice8 *	s_pD3DDevice;
	static	HWND				s_hWindow;
	static	GosTexture *		s_apTexture[c_MaxTextures];
	static	GosVertexBuffer *	s_apVertexBuffer[c_MaxStreams];
	static	UINT				s_aStreamStride[c_MaxStreams];
	static	GosIndexBuffer *	s_pIndexBuffer;
	static	UINT				s_BaseVertexIndex;
	static	GosSurface *		s_pRenderTarget;
	static	GosSurface *		s_pZStencil;
    static  GosSurface *		s_pDefaultRenderTarget;
    static  GosSurface *		s_pDefaultDepthStencil;
	static	GosPixelShader *	s_pPixelShader;
	static	GosVertexShader *	s_pVertexShader;
	static	DWORD				s_Left;
	static	DWORD				s_Top;
	static	DWORD				s_Width;
	static	DWORD				s_Height;
	static D3DPRESENT_PARAMETERS s_d3dpp;

public:

	static	IDirect3DDevice8 *	D3DDevice() { return s_pD3DDevice; }


	// Used internally by GOS Framework

	static	bool			Initialize( HWND hWnd );
	static	void			Shutdown();
	static	void			Done();


	// Main 3d API functions

	// Cursor
	static	HRESULT 		SetCursorProperties( UINT XHotSpot, UINT YHotSpot, GosSurface* pCursorBitmap );
	static	void 			SetCursorPosition( UINT XScreenSpace, UINT YScreenSpace, DWORD Flags );
	static	BOOL 			ShowCursor( BOOL bShow );
	// Display control
	static	void			SetDisplay(UINT width, UINT height, D3DFORMAT format, bool windowed );
	static  DWORD			Left();
	static  DWORD			Top();
	static  DWORD			Width();
	static  DWORD			Height();
	static	void 			SetGammaRamp( DWORD Flags, D3DGAMMARAMP* pRamp );
	static	void 			GetGammaRamp( D3DGAMMARAMP* pRamp );
	static	HRESULT 		GetRasterStatus( D3DRASTER_STATUS* pRasterStatus );
	// Bltting
	static	HRESULT 		CopyRects( GosSurface* pSourceSurface, RECT* pSourceRectsArray, UINT cRects, GosSurface* pDestinationSurface, POINT* pDestPointsArray );
	static	HRESULT 		GetFrontBuffer( GosSurface* pDestSurface );
	// Begin/Clear/End Scene, RenderTarget
	static	HRESULT 		BeginScene();
	static	HRESULT 		EndScene();
	static	HRESULT 		Clear( DWORD Count, D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil );
	static	HRESULT 		SetRenderTarget( GosSurface* pRenderTarget, GosSurface* pNewZStencil );
	static	HRESULT 		GetRenderTarget( GosSurface** ppRenderTarget );
	static	HRESULT 		GetDepthStencilSurface( GosSurface** ppZStencilSurface );
	// Geometry
	static	HRESULT 		SetTransform( D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix );
	static	HRESULT 		GetTransform( D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix );
	static	HRESULT 		MultiplyTransform( D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix );
	// Lights & Materials
	static	HRESULT 		SetMaterial( D3DMATERIAL8* pMaterial );
	static	HRESULT 		GetMaterial( D3DMATERIAL8* pMaterial );
	static	HRESULT 		SetLight( DWORD Index, D3DLIGHT8* pLight );
	static	HRESULT 		GetLight( DWORD Index, D3DLIGHT8* pLight );
	static	HRESULT 		LightEnable( DWORD Index, BOOL Enable );
	static	HRESULT 		GetLightEnable( DWORD Index, BOOL* pEnable );
	// Render States
	static	HRESULT 		SetRenderState( D3DRENDERSTATETYPE State, DWORD Value );
	static	HRESULT 		GetRenderState( D3DRENDERSTATETYPE State, DWORD* pValue );
	static	HRESULT 		ApplyStateBlock( GosStateBlock *pBlock );
	// Clipping & Viewport
	static	HRESULT 		SetClipPlane( DWORD Index, float* pPlane );
	static	HRESULT 		GetClipPlane( DWORD Index, float* pPlane );
	static	HRESULT 		SetClipStatus( D3DCLIPSTATUS8* pClipStatus );
	static	HRESULT 		GetClipStatus( D3DCLIPSTATUS8* pClipStatus );
	static	HRESULT 		SetViewport( D3DVIEWPORT8* pViewport );
	static	HRESULT 		GetViewport( D3DVIEWPORT8* pViewport );
	// Texturing
	static	HRESULT 		SetTexture( DWORD Stage, GosTexture* pTexture );
	static	HRESULT 		GetTexture( DWORD Stage, GosTexture** ppTexture );
	static	HRESULT 		SetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value );
	static	HRESULT 		GetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue );
	// Palettes
	static	HRESULT 		SetPaletteEntries( UINT PaletteNumber, PALETTEENTRY* pEntries );
	static	HRESULT 		GetPaletteEntries( UINT PaletteNumber, PALETTEENTRY* pEntries );
	static	HRESULT 		SetCurrentTexturePalette( UINT PaletteNumber );
	static	HRESULT 		GetCurrentTexturePalette( UINT *PaletteNumber );
	// Vertex & Index Buffers
	static	HRESULT 		SetStreamSource( UINT StreamNumber, GosVertexBuffer* pStreamData, UINT Stride );
	static	HRESULT 		GetStreamSource( UINT StreamNumber, GosVertexBuffer** ppStreamData, UINT* pStride );
	static	HRESULT 		SetIndices( GosIndexBuffer* pIndexData, UINT BaseVertexIndex );
	static	HRESULT 		GetIndices( GosIndexBuffer** ppIndexData, UINT* pBaseVertexIndex );
	// Rendering
	static	HRESULT 		DrawPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount );
	static	HRESULT 		DrawIndexedPrimitive( D3DPRIMITIVETYPE PrimitiveType, UINT minIndex, UINT NumIndices, UINT startIndex, UINT primCount );
	static	HRESULT 		DrawPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, void* pVertexStreamZeroData, UINT VertexStreamZeroStride );
	static	HRESULT 		DrawQuadUP( UINT PrimitiveCount, void* pVertexStreamZeroData, UINT VertexStreamZeroStride );
	static	HRESULT 		DrawIndexedPrimitiveUP( D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertexIndices, UINT PrimitiveCount, void* pIndexData, D3DFORMAT IndexDataFormat, void* pVertexStreamZeroData, UINT VertexStreamZeroStride );
	static	HRESULT 		ProcessVertices( UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, GosVertexBuffer* pDestBuffer, DWORD Flags );
	// Vertex Shaders
	static	HRESULT			SetFVFShader( DWORD vertexFormat );
	static	HRESULT 		SetVertexShader( GosVertexShader *pVertexShader );
	static	HRESULT 		GetVertexShader( GosVertexShader **ppVertexShader );
	static	HRESULT 		SetVertexShaderConstant( DWORD Register, void* pConstantData, DWORD ConstantCount );
	static	HRESULT 		GetVertexShaderConstant( DWORD Register, void* pConstantData, DWORD ConstantCount );
	// Pixel Shaders
	static	HRESULT 		SetPixelShader( GosPixelShader *pPixelShader );
	static	HRESULT 		GetPixelShader( GosPixelShader **ppPixelShader );
	static	HRESULT 		SetPixelShaderConstant( DWORD Register, void* pConstantData, DWORD ConstantCount );
	static	HRESULT 		GetPixelShaderConstant( DWORD Register, void* pConstantData, DWORD ConstantCount );
	// High Order Surfaces
	static	HRESULT 		DrawRectPatch( UINT Handle, float* pNumSegs, D3DRECTPATCH_INFO* pSurf );
	static	HRESULT 		DrawTriPatch( UINT Handle, float* pNumSegs, D3DTRIPATCH_INFO* pSurf );
	// Misc
	static  void			SetupViewport( bool FillZ, float ZBuffer, bool FillBG, DWORD BGColor, float top, float left, float bottom, float right, bool ClearStencil=0, DWORD StencilValue=0 );
	static	void			UpdateWindowDimensions();

};

class Surface : public GosSurface
{
	IDirect3DSurface8 *		m_pD3DSurface;
public:
	static	GosSurface*		CreateEmpty( UINT Width, UINT Height, D3DFORMAT Format );
	static	GosSurface*		CreateEmptyDepthStencil( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample );
	static	GosSurface*		CreateRenderTarget( UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, BOOL Lockable );
    virtual HRESULT         Desc( D3DSURFACE_DESC *pDesc );
	virtual HRESULT			LoadFromFile( TCHAR *pName );
	virtual HRESULT			Lock( D3DLOCKED_RECT *pLockedRect, RECT *pRect=NULL );
	virtual HRESULT			Unlock();
							Surface( IDirect3DSurface8 *pD3DSurface );
	virtual					~Surface();

	IDirect3DSurface8 *		D3DSurface() { return m_pD3DSurface; }
};

class Texture : public GosTexture
{
	enum { MaxLevels = 12 };
	enum TextureType { mipmap, volume, cube };
	TextureType				m_Type;
	IDirect3DBaseTexture8 *	m_pD3DBaseTexture;
	Surface *				m_apLevel[MaxLevels];
	DWORD					m_nLevels;
	TCHAR *					m_pName;
	
public:
	// Standard Textures
	static	GosTexture*		CreateFromFile( TCHAR *pName, bool CreateMip=true , /* D3DFORMAT */ int Format=0  );
    static  GosTexture*     CreateFromMemory( LPCVOID pSrcData, UINT SrcDataSize, bool CreateMip , /* D3DFORMAT */ int Format=0, int Pool=0  );
	static	GosTexture*		CreateEmpty( UINT Width, UINT Height, UINT Levels, D3DFORMAT Format, D3DPOOL Pool, const TCHAR *pName=NULL,bool fUse3D=false );

	virtual HRESULT			Lock( D3DLOCKED_RECT *pLockedRect, UINT Level=0, RECT *pRect=NULL );
    virtual HRESULT         LevelDesc( D3DSURFACE_DESC *pDesc, UINT Level=0 );
	virtual GosSurface*		LevelSurface( UINT Level=0 );		// yields the GosSurface for a level of a mipmap
	// Volume Textures
	static	GosTexture*		CreateVolume( UINT Width, UINT Height, UINT Depth, UINT Levels, D3DFORMAT Format );
	virtual HRESULT			Lock( D3DLOCKED_BOX *pLockedBox, UINT Level=0, D3DBOX *pBox=NULL );
	// Cube Textures
	static	GosTexture*		CreateCube( UINT EdgeLength, UINT Levels, D3DFORMAT Format );
	virtual	HRESULT			Lock( D3DLOCKED_RECT *pLockedRect, D3DCUBEMAP_FACES Face, UINT Level=0, RECT *pRect = NULL );
	// All Textures
	virtual HRESULT			Unlock( UINT Level=0, D3DCUBEMAP_FACES Face=D3DCUBEMAP_FACE_POSITIVE_X );
	virtual HRESULT 		Update( GosTexture* pSourceTexture );
	virtual void			SetName( const TCHAR *pName );

							Texture( IDirect3DBaseTexture8 *pD3DTexture, TextureType Type );
	virtual					~Texture();
	IDirect3DBaseTexture8 *	D3DTexture() { return m_pD3DBaseTexture; }
	virtual bool			HasAlpha();
};


class VertexBuffer
{
	IDirect3DVertexBuffer8 *m_pD3DVertexBuffer;
	UINT					m_SizeBytes;
public:
	static	GosVertexBuffer*CreateEmpty(UINT Length, DWORD FVF, DWORD Usage);
	static	GosVertexBuffer*CreateFromMemory(void *pData,UINT Length, DWORD FVF, DWORD Usage);
	virtual void *			Lock( DWORD Usage=0 );
	virtual void			Unlock();
							VertexBuffer( IDirect3DVertexBuffer8 *pD3DVertexBuffer, UINT SizeBytes );
	virtual					~VertexBuffer();
	IDirect3DVertexBuffer8 *D3DVertexBuffer() { return m_pD3DVertexBuffer; }
};

class IndexBuffer
{
	IDirect3DIndexBuffer8 *	m_pD3DIndexBuffer;
	UINT					m_SizeBytes;
public:
	static	GosIndexBuffer*	CreateEmpty(UINT Length, DWORD Usage, D3DFORMAT Format);
	static	GosIndexBuffer*	CreateFromMemory(void *pData,UINT Length, DWORD Usage, D3DFORMAT Format);
	virtual void *			Lock();
	virtual void			Unlock();
							IndexBuffer( IDirect3DIndexBuffer8 *pD3DIndexBuffer, UINT SizeBytes );
	virtual					~IndexBuffer();
	IDirect3DIndexBuffer8 *D3DIndexBuffer() { return m_pD3DIndexBuffer; }
};

class PixelShader
{
	DWORD					m_Handle;
public:
	static	GosPixelShader*	CreateFromMemory(DWORD *pFunction );
	virtual DWORD			GetFunction( DWORD *pBuffer );
	virtual					~PixelShader();
							PixelShader( DWORD Handle );
	DWORD					Handle() { return m_Handle; }
};

class VertexShader
{
	DWORD					m_Handle;
public:
	static	GosVertexShader*CreateFromMemory(DWORD *pDeclaration, DWORD *pFunction=NULL);
	virtual DWORD			GetDeclaration( DWORD *pBuffer );
	virtual DWORD			GetFunction( DWORD *pBuffer );
	virtual					~VertexShader();
							VertexShader( DWORD Handle );
	DWORD					Handle() { return m_Handle; }
};

class StateBlock
{
	DWORD					m_Token;
public:
	static	GosStateBlock*	CreateCurrent( bool GetPixelState, bool GetVertexState );
	static	GosStateBlock*	CreateRecording();
	virtual void			EndRecording();
	virtual void			Update();
							StateBlock( DWORD Token );
	virtual					~StateBlock();

	DWORD					Token() { return m_Token; }
};

} using namespace Gos2;