#pragma once
#pragma warning( disable : 4200 ) 

namespace Gos2 {

class Font3D;

const c_MaxSurfaces = 32;
const c_SurfaceWidth = 128;
const c_SurfaceHeight = 128;
const c_VertexBufferSize = 128;
const c_NodePoolSize = 128;
const c_PolygonsPerLine = 64;
const c_CachePoolSize = 32;
const c_MaxAverageTextBucketSize = 1;		// suggest 1..5
const c_InitialTextHashTableSize = 32;		// must be power of two

#define FVF_FONT2DVERTEX  (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)

	struct D3DFontData 
	{
		DWORD				dwSig;				// 0x46443344
		char				szFaceName[64];
		int					iSize;				// point size of font used to create this font
		bool				bItalic;			// italic on or off when this font was created
		int					iWeight;			// weight of font created
		int					iTextureCount; 
		DWORD				dwFontHeight;		// height in pixels of the resulatant font
		BYTE				bTexture[256];		// indicate which texture each character is on
		BYTE				bX[256];			// indicates x position of each character on it's texture
		BYTE				bY[256];			// indicates y position of each character on it's texture
		BYTE				bW[256];			// indicates width of each character in the texture
		signed char			cA[256];			// indicates amount of space to add before each character (may be -)
		signed char			cC[256];			// indicates amount of space to add after each character (may be -)
	};

	struct D3DFontTexture
	{
		DWORD				dwSize;
		BYTE				bPixels[0];
	};

	struct SurfaceCache;

	struct SurfaceNode
	{
		int					iTop;
		int					iLeft;
		int					iWidth;
		int					iHeight;
		SurfaceNode *		pChildX;
		SurfaceNode *		pChildY;
		SurfaceNode *		pParent;
		SurfaceCache *		pSurface;
		char				cFull;	// 0 = empty; 1 = X full; 2 = Y full; 3 = XY Full (or leaf)
	};

	struct NodePool
	{
		SurfaceNode			Pool[c_NodePoolSize];
		NodePool *			pNext;
	};

	struct SurfaceCache
	{
		SurfaceCache *		pNext;
		IDirect3DTexture8 *	pD3DTexture;
		SurfaceNode	*		pTree;
	};

	struct Font2DVertex
	{
		float				sX;
		float				sY;
		float				sZ;
		float				rhw;
		DWORD				Color;
		float				TexU;
		float				TexV;
	};

	struct LineOfText
	{
		_TCHAR *			pStart;
		int					iCount;
		int					iWidth;
		int					iHeight;
		float				fYPos;
		float				fXPos;
	};

	struct CachedLine
	{
		_TCHAR *			pString;
		int					iLength;
		int					iWidth;
		int					iHeight;
		float				fYPos;
		float				fXPos;
		SurfaceNode *		pPolyNodes[c_PolygonsPerLine];
	};


	struct CacheEntry
	{
		_TCHAR *			pString;
		Font3D *			pFont;
		CacheEntry *		pNextHash;
		CacheEntry *		pLRU;
		CacheEntry *		pMRU;
		CachedLine *		pLines;
		DWORD				dwHashID;
		int					iLineCount;
		int					iFlags; // 0x1=Proportional, 0x2=Bold, 0x4=Italic, 0x8=WordWrap
		int					iXPos;
		int					iYPos;
		int					iFrameUsed;
		int					iPointSize;
		float				fScale;
	};

	struct CachePool
	{
		CacheEntry			Pool[c_CachePoolSize];
		CachePool *			pNext;
	};
	

class Text : public GosText
{
	static GosFont3D *		s_pFont;
	static Text *			s_pText;
	static DWORD			s_ForegroundColor;
	static float			s_FontSize;
	static bool				s_WordWrap;
	static bool				s_Proportional;
	static bool				s_Bold;
	static bool				s_Italic;
	static DWORD			s_Alignment;
	static int				s_XPosition;
	static int				s_YPosition;
	static int				s_RegionLeft;
	static int				s_RegionTop;
	static int				s_RegionRight;
	static int				s_RegionBottom;



public:

							Text();
	virtual					~Text();

	static void				Set( GosText::TextProperty Property,... );
	static void				Get( GosText::TextProperty Property,... );
	static void				Draw( _TCHAR *Message,... );
	static void				DrawUncached( _TCHAR *Message,... );
	static void				StringBounds( DWORD* Width, DWORD* Height, const TCHAR *Message, ... );

	static void				DrawBackground( int Left, int Top, int Right, int Bottom, DWORD Color );
	static void				SetAttributes( GosFont3D *pFont, DWORD Foreground, float Size, bool WordWrap, bool Proportional, bool Bold, bool Italic, DWORD Alignment=0 );
	static void				SetPosition( int PosX, int PosY );
	static void				SetRegion( int Left, int Top, int Right, int Bottom );
	static void				SelectFont( GosFont3D *pFont );

	static	bool			Initialize();
	static	void			Shutdown();
	static	void			Flush( bool DeleteAll = false );
	static	Text *			GetTextObj(){return s_pText;};
	static	int				GetPointSize(){ return s_pText->m_iPointSize;};
	static	float			GetScale(){ return s_pText->m_fScale;};

	_TCHAR *				Buffer(){return m_szTextBuffer;};
	SurfaceNode *			FindFreeNode(CacheEntry *pEntry, int Width, int Height);
	void					SetTextRenderstates();
	void					ResetTextRenderstates();
	void					SetDefaultTexture();
	SurfaceCache *			LockSurface(int TexID, D3DLOCKED_RECT *pD3Dlr);
	void					LockSurface(SurfaceCache *pSC, D3DLOCKED_RECT *pD3Dlr);
	void					FillBlock( D3DLOCKED_RECT *pDst, RECT *pBounds, WORD Color);
	void					UnlockSurface(SurfaceCache *pSC);
	void					RenderLineFromTexture(RECT *pBounds, float XPosition, float YPosition, float Scale);
	void					RenderTextTextures();
	int						GetScratchHeight(){ return m_pSurfaceCache->pTree->pChildX->iHeight;};
	SurfaceNode *			GetFreeNode();
	SurfaceNode *			SearchTree( SurfaceNode * pTree, int Width, int Height, bool NoSplit = false);
	void					FreeNode(SurfaceNode *pNode);
	LineOfText *			GetLine(int ID){ return &m_pLOT[ID]; };

protected:
	
	enum
	{
		e_XYNotFull,
		e_XTreeFull,
		e_YTreeFull,
		e_SubtreesFull

	};
	
	enum
	{
		e_SpaceOK,
		e_OutOfSpace
	};
			

	SurfaceCache *			m_pSurfaceCache;
	CachePool *				m_pCachePool;
	CacheEntry *			m_pFreeCache;
	CacheEntry *			m_pLRU;
	CacheEntry *			m_pMRU;
	NodePool *				m_pNodePool;
	SurfaceNode *			m_pFreeNode;

	CacheEntry **			m_ppTextHashTable;
	DWORD					m_dwTextHashSpaceAvailable;		// number of objects we could add before having to use one more entry in has table
	DWORD					m_dwTextHashEntriesInUse;		// this increments by one and hash table is realloced when it hits new pwr of 2
	DWORD					m_dwTextHashCutoff;				// First of "new" buckets
	DWORD					m_dwTextHashMask;				// num buckets in table - 1... not all are "in use"

	int						m_iSurfaceCount;
	int						m_iTotalTextureArea;
	int						m_iTextureAreaFree;
	_TCHAR					m_szTextBuffer[8192];
	LineOfText				m_pLOT[512];
	int						m_iStrln;
	int						m_iPointSize;
	float					m_fScale;


	// Temporary!!!!
	DWORD					m_dwSavedStates[29];
	GosVertexShader *		m_pSavedVertexShader;
	GosVertexBuffer *		m_pSavedVertexBuffer;
	UINT					m_uiSavedStride;
	bool					m_bResetStates;
	GosVertexBuffer *		m_pGOSVertextBuffer;

protected:

	bool					ProcessString();
	CacheEntry *			IsCached(_TCHAR *pString);
	void					AddTextToCache(CacheEntry **ppText);
	int						AddTextureArea(int width, int height);
	void					RenderCachedText(CacheEntry *pText);
	bool					FlushArea(int Width, int Height, int Age = 0);
	CacheEntry *			GetFreeEntry();
	int						GetTextureSpace(int Height);
	int						GetNodeSpace(SurfaceNode *pNode, int Height);
	void					FreeEntry(CacheEntry *pEntry);
	void					FixNodeTree(SurfaceNode * pParent, SurfaceNode ** ppNode, unsigned long diff);

	SurfaceNode *			SplitNodeVertical( SurfaceNode * pTree, int Width);
	SurfaceNode *			SplitNodeHorizontal( SurfaceNode * pTree, int Width, int Height);
	Font2DVertex			Init2DFontVertex(float sX, float sY, float sZ, float rhw, DWORD color, float texU, float texV)
							{
								Font2DVertex v; v.sX=sX; v.sY=sY; v.sZ=sZ; v.rhw=rhw; v.Color=color; v.TexU=texU; v.TexV=texV;
								return v;
							}

protected:

	void					RebalanceTextHashTable();
	DWORD					HashIndex( DWORD HashValue );
	DWORD					HashID( _TCHAR *pString );

};

class Font3D : public GosFont3D
{
protected:

	static Font3D *			s_pFont;

	TCHAR					m_szFontFile[MAX_PATH];
	int						m_iCharCount;				// number of chars in font (valid range 33 to 256) ofr .d3f and .tga
	int						m_iWidth;					// Width of font grid
	int						m_iHeight;					// Height of font grid
	int						m_iPointSize;
	DWORD					m_dwSpaceWidth;
	float					m_fScale;
	bool					m_bBold;
	bool					m_bProportional;
	bool					m_bItalic;

	// This stuff (maybe) should be in the TTFont and D3FFont
	// subclasses, but they have to be intited once (and may
	// need to be shared anyway), so they can stay here....
	static SurfaceCache *	s_pGlyphCache;
	static SurfaceNode **	s_ppGlyphNodes;
	static int				s_iGlyphSpace;
	static HDC				s_hDC;
	static DWORD *			s_pBMPBits;
	static HBITMAP			s_hBMP;
	static WORD				s_PixelLookup[16];

	virtual void			CreateFont() = 0;

public:
							Font3D();
	virtual					~Font3D();

	virtual int				StringBounds(Text *pText, DWORD* Width, DWORD* Height) = 0;
	virtual void			DrawSringToTexture(Text *pText, CacheEntry *pEntry) = 0;
	virtual void			DrawUncachedText(Text *pText) = 0;
	virtual int				LineBounds(DWORD* Width, DWORD* Height, _TCHAR *pString) = 0;
	virtual int				WordWidth(DWORD* pWidth, _TCHAR *pString) = 0;
	virtual	void			UpdateFont() = 0;

	int						Height()	{return m_iHeight;};
	int						Width()		{return m_iWidth;};
	int						PointSize()	{return m_iPointSize;};
	float					Scale()		{return m_fScale;};
	bool					IsBold()	{return m_bBold;};
	bool					IsItalic()	{return m_bItalic;};
	DWORD					SpaceWidth(){return m_dwSpaceWidth;};
	void					FlushGlyphCache();
	void					RenderGlyphTexture();
	void					FlushNode(SurfaceNode *pNode);


	static	bool			Initialize();
	static	void			Shutdown();
	static GosFont3D *		CreateFromFile( TCHAR *pFileName, DWORD StartLine = 0, int CharCount=256 );
	static GosFont3D *		CreateFromTexture( GosTexture *pTexture, DWORD StartLine = 0, int CharCount=256 );
};



class TTFont : public Font3D
{
protected:

	HFONT					m_hFont;

	virtual void			CreateFont();
	void					DrawBitsToTexture(D3DLOCKED_RECT *pD3Dlr, DWORD *pSrcBits, RECT *pBounds);

public:
							TTFont();
	virtual					~TTFont();

	virtual int				StringBounds(Text *pText, DWORD* Width, DWORD* Height);
	virtual void			DrawSringToTexture(Text *pText, CacheEntry *pEntry);
	virtual void			DrawUncachedText(Text *pText);
	virtual int				LineBounds(DWORD* Width, DWORD* Height, _TCHAR *pString);
	virtual int				WordWidth(DWORD* pWidth, _TCHAR *pString);
	virtual	void			UpdateFont();

	HDC						GetDC(){return s_hDC;};
	HBITMAP					GetBMP(){return s_hBMP;}
	DWORD *					GetBMPBits(){return s_pBMPBits;};
	void					SetDC(HDC hDC){s_hDC = hDC;};
	void					SetBMP(HBITMAP hBMP){s_hBMP = hBMP;};
	void					SetBMPBits(DWORD *pBits){s_pBMPBits = pBits;};
};


class D3FFont : public Font3D
{
protected:

	D3DFontTexture *		m_pFontData;				// 8bpp alpha data for font
	BYTE **					m_ppChrBits;				// Start of the data for each char
	BYTE *					m_pCharWidth;				// Actual width of the char
	BYTE *					m_pCharHeight;				// Actual height of the char
	BYTE *					m_pTopOffset;				// Offset (in lines) to the top of the char
	WORD *					m_pCharStride;				// Stride for each char
	int						m_iMaxWidth;				// Used for fixed spacing

	virtual void			CreateFont();
	void					GetCharSize( int *pWidth, int *pHeight, _TCHAR Chr);
	void					DrawGlyph(SurfaceNode * pNode, D3DLOCKED_RECT *pDst, _TCHAR Chr);
	SurfaceNode *			IsCached(_TCHAR chr);
	void					BltBlockToTexture(D3DLOCKED_RECT *pGlyph, D3DLOCKED_RECT *pDst, int XOffset, RECT *pClipRect, _TCHAR *pString);
	int						BltChar(SurfaceNode *pNode, D3DLOCKED_RECT *pSrc, D3DLOCKED_RECT *pDst, int XOffset, RECT *pClipRect, _TCHAR Chr);
	int						BltItalicChar(SurfaceNode *pNode, D3DLOCKED_RECT *pSrc, D3DLOCKED_RECT *pDst, int XOffset, RECT *pClipRect, _TCHAR Chr);
	int						MaxWidth();

public:
							D3FFont();
	virtual					~D3FFont();

	virtual int				StringBounds(Text *pText, DWORD* Width, DWORD* Height);
	virtual void			DrawSringToTexture(Text *pText, CacheEntry *pEntry);
	virtual void			DrawUncachedText(Text *pText);
	virtual int				LineBounds(DWORD* Width, DWORD* Height, _TCHAR *pString);
	virtual int				WordWidth(DWORD* pWidth, _TCHAR *pString);
	virtual	void			UpdateFont();

};

} using namespace Gos2;
