///////////////////////////////////////////////////////////////////////////////
/// @file deFont.cpp
///
/// @brief class for text display
///
/// @author Lightning, Assassin
///
/// 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 Jan 2002
/// @author Lightning
/// @remarks Creation
///
/// @date Feb 2002
/// @author Assassin
/// @remarks Cleanup and Modifications
///
///////////////////////////////////////////////////////////////////////////////

#include <cstdio>
#include <windows.h> // for various stuff (BITMAPINFO, etc)
#include "deFont_priv.hpp"
#include "deBitmap.hpp"
#include "deMath.hpp"

IdeFont* IdeFont_CreateFont()
{
	return new deFont;
}

deFont::deFont()
{
	//default constructor

	//clear out all the private members
	InitData();
}

deFont::deFont(const char *Font, long Height, long FontFlags, long Color)
{
	//contructor with params to set a font

	//clear out all the private members
	InitData();

	//call the setFont function with the params to set
	SetFont(Font, Height, FontFlags);

	//set the fore and background color
	SetColor(Color);
	return;
}

deFont::deFont(IdeBitmapProxy *Font, long Height, long *Widths)
{
	//constructor with params to set a font via a bitmap

	//clear out all the private members
	InitData();

	//call the setFont function with the params to set
	SetFont(Font, Height, Widths);

	return;
}

deFont::~deFont()
{
	//if a font exists and we own it, destroy it
	if(m_FontTexture)
		m_FontTexture->Release();

	//free the font bitmap
	if(m_TextureState)
		m_TextureState->Release();
}

int deFont::Release()
{
	delete this;
	return 0;
}

deBoolean deFont::SetFont(const char *Fontname, long Height, long FontFlags)
{
	BITMAPINFO		BitmapInfo;
	DWORD *			BitmapBits;
	DWORD *			CurBitmapBit;
	IdeBitmap*		FontBitmap;
	long			BoldFlag;
	long			ItalicFlag;
	long			UnderlineFlag;
	long			StrikeOutFlag;
	HFONT			FontHandle;
	short			Str;
	SIZE			StrSize = {0,0};	
	long			X, Y;
	long			BitmapWidth, BitmapHeight;
	HDC				BitmapDC;
	HBITMAP			BitmapDIB;

	//create a font via the specified font type

	//if a font exists, remove it from the drivers that are attached

	//if a font already exists and we are the owner, destroy it
	if(m_FontTexture)
	{
		m_FontTexture->Release();
		m_FontTexture = NULL;
	}

	//create a new 32bit bitmap

	BitmapWidth = 1024;
	BitmapHeight = 1024;

	//clear out the header info
	memset(&BitmapInfo.bmiHeader, 0, sizeof(BITMAPINFOHEADER));

	//setup the bitmap header
	BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	BitmapInfo.bmiHeader.biWidth = BitmapWidth;
	BitmapInfo.bmiHeader.biHeight = -BitmapHeight;	//MS with odd things, negative is positive
	BitmapInfo.bmiHeader.biPlanes = 1;
	BitmapInfo.bmiHeader.biCompression = BI_RGB;
	BitmapInfo.bmiHeader.biBitCount = 32;

	//create a bitmap for the font now
	BitmapDC = CreateCompatibleDC(NULL);
	if(!BitmapDC)
		return deFALSE;

	BitmapDIB  = CreateDIBSection(BitmapDC, &BitmapInfo, DIB_RGB_COLORS, (void **)&BitmapBits, NULL, 0);
	if(!BitmapDIB)
	{
		//problem, fail
		DeleteDC(BitmapDC);
		BitmapDC = NULL;
		return deFALSE;
	}

	//set how to map the font to the bitmap
	SetMapMode(BitmapDC, MM_TEXT);

	//msdn shows using MulDiv for this, I'm unsure why as it would take some very large
	//numbers to overflow 32bits
	Height = -(Height * GetDeviceCaps(BitmapDC, LOGPIXELSY) / 72);
	BoldFlag = (FontFlags & STYLE_BOLD) ? FW_BOLD : FW_NORMAL;
	ItalicFlag = (FontFlags & STYLE_ITALIC) ? TRUE : FALSE;
	UnderlineFlag = (FontFlags & STYLE_UNDERLINE) ? TRUE : FALSE;
	StrikeOutFlag = (FontFlags & STYLE_STRIKEOUT) ? TRUE : FALSE;

	//create the font finally
	//FontHandle = CreateFont(Height, 0, 0, 0, BoldFlag, ItalicFlag, UnderlineFlag, StrikeOutFlag, DEFAULT_CHARSET,	OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, Fontname);
	FontHandle = CreateFont(Height, 0, 0, 0, BoldFlag, ItalicFlag, UnderlineFlag, StrikeOutFlag, 
							DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, 
							VARIABLE_PITCH, Fontname);
	if(!FontHandle)
	{
		//cleanup and fail
		DeleteObject(BitmapDIB);
		BitmapDIB = NULL;
		DeleteDC(BitmapDC);
		BitmapDC = NULL;
		return deFALSE;
	}

	//created a font, set the font object to the bitmap DC
	SelectObject(BitmapDC, BitmapDIB);
	SelectObject(BitmapDC, FontHandle);

	//make the font white with a black background and align to the top of the bitmap
	SetTextColor(BitmapDC, 0x00FFFFFF);
	SetBkColor(BitmapDC, 0x00000000);
	SetTextAlign(BitmapDC, TA_TOP);


	//go thru all characters so we even get characters from other languages
	X = 0;
	Y = 0;
	//char CharBuffer[256];
	//CharBuffer[0] = '\0';
	//int CharsInBuffer = 0;
	RECT ClipRect;
	for(Str = 0x20; Str < 0xFF; Str++)
	{
		char ch = (char)((unsigned char)Str);
		//get the size of the character
		GetTextExtentPoint32(BitmapDC, &ch, 1, &StrSize);

		if (!UnderlineFlag)
			StrSize.cx++;

		//if we hit the end of the bitmap, go down a line
		if((X + StrSize.cx) > BitmapWidth)
		{
			X = 0;
			Y = Y + StrSize.cy;

			if(Y > BitmapHeight || Y + StrSize.cy > BitmapHeight)
			{
				//got a prob with the font being too large
				DeleteObject(FontHandle);
				DeleteObject(BitmapDIB);
				DeleteDC(BitmapDC);
				return deFALSE;
			}
		}

		//output the character
		ClipRect.left = X;
		ClipRect.right = X + StrSize.cx;
		ClipRect.top = Y;
		ClipRect.bottom = Y + StrSize.cy;
		ExtTextOut(BitmapDC, X, Y, ETO_CLIPPED | ETO_OPAQUE, &ClipRect, &ch, 1, NULL);

		//now save positional data about the character
		m_Coords[Str - 32].Top = (deFloat)Y;
		m_Coords[Str - 32].Bottom = (deFloat)Y + StrSize.cy;
		m_Coords[Str - 32].Left = (deFloat)X / BitmapWidth;
		m_Coords[Str - 32].Right = (deFloat)(X + StrSize.cx) / BitmapWidth;

		// Add a little 1-pixel space between letters
		X = X + StrSize.cx;
	}

	//adjust the bitmap height so that it is only as tall as the letters if possible
	//must be a power of 2, minimum 8
	Y = Y + StrSize.cy;
	m_InternalScale = 1.0f;		//exact internal scale at the moment
    for(Str = 3; Str < 32; Str++)
	{
		//if we find something too large, break
		if((DWORD)(1 << Str) >= (DWORD)BitmapHeight)
			break;

		//if we find something useful, use it
		if((DWORD)(1 << Str) >= (DWORD)Y)
		{
			//adjust the internal scale for the difference in height
			m_InternalScale = (deDouble)(1 << Str) / BitmapHeight;

			//change the height
			BitmapHeight = (DWORD)(1 << Str);
			break;
		}
	}

	//now go thru the letters and adjust the top and bottom positions
	for(Str = 0; Str < 224; Str++)
	{
		m_Coords[Str].Top = m_Coords[Str].Top / BitmapHeight;
		m_Coords[Str].Bottom = m_Coords[Str].Bottom / BitmapHeight;
		m_Coords[Str].Left += 0.125f/BitmapWidth;
		m_Coords[Str].Right += 0.125f/BitmapWidth;
	}

	//delete the font
	DeleteObject(FontHandle);

	//we now have a bitmap full of characters, go thru and set the alpha of the
	//bits so that black is see-thru
	CurBitmapBit = BitmapBits;
	for(Y = 0; Y < BitmapHeight; Y++)
	{
		for(X = 0; X < BitmapWidth; X++)
		{
			//if the pixel has a value then modify it
			// this get a "grayish" color value to have a semi-transparent alpha value
			if(*CurBitmapBit)
				*CurBitmapBit |= (*CurBitmapBit << 24) & 0xff000000;

			//go to the next bitmap bit
			CurBitmapBit++;
		}
	}

	FontBitmap = IdeBitmap_CreateBitmapWithInfo(BitmapWidth, BitmapHeight, IdeDriver::BPP_A1R5G5B5);
	m_FontTexture = IdeBitmap_CreateProxy(FontBitmap);
	if (!FontBitmap || !m_FontTexture)
	{
		DeleteObject(BitmapDIB);
		DeleteDC(BitmapDC);
		if (FontBitmap)
			FontBitmap->Release();
		if (m_FontTexture)
			m_FontTexture->Release();
		FontBitmap = NULL;
		m_FontTexture = NULL;
		DEBUGRETURN(deFALSE, "SetFont: failed to create m_FontTexture");
	}

	//copy the data, downsampling to 16-bit format, since it's essentially monochrome anyway
	void * SrcPtr = BitmapBits;
	void * TargPtr= FontBitmap->GetData();
	FontBitmap->DownSampleMIP(SrcPtr, TargPtr, BitmapWidth, BitmapHeight, BitmapWidth*4,
									BitmapWidth, BitmapHeight, BitmapWidth*2,
									IdeDriver::BPP_A8R8G8B8, IdeDriver::BPP_A1R5G5B5);

	//free the objects
	DeleteObject(BitmapDIB);
	DeleteDC(BitmapDC);
	
#if 0
	// for a bit of old-school compatibility with Voodoo3 and stuff
	if (BitmapWidth > 256 && BitmapHeight * (BitmapWidth/256) <= 256)
	{
#pragma fixme(this causes letters to hang off the edge)
		// we can resize this texture to be all square-like
		int NewWidth = 256;
		int NewHeight = BitmapHeight * (BitmapWidth/256);
		IdeBitmap * NewBMP = IdeBitmap_CreateBitmapWithInfo(NewWidth, NewHeight, IdeDriver::BPP_A1R5G5B5);
		deRect BMPRect;
		if (NewBMP)
		{
			deFloat PercentChange = ((deFloat)NewWidth) / BitmapWidth;
			deFloat InvPercentChange = 1.0f / PercentChange;
			for(Str = 0; Str < 224; Str++)
			{
				m_Coords[Str].Top *= PercentChange;
				m_Coords[Str].Bottom *= PercentChange;
				m_Coords[Str].Left *= InvPercentChange;
				m_Coords[Str].Right *= InvPercentChange;
				if (m_Coords[Str].Right > 1.0)
				{
					m_Coords[Str].Top += PercentChange * (int)(m_Coords[Str].Right);
					m_Coords[Str].Bottom += PercentChange * (int)(m_Coords[Str].Right);
					m_Coords[Str].Left -= (int)(m_Coords[Str].Left);
					m_Coords[Str].Right -= (int)(m_Coords[Str].Right);
				}
			}

			for (X = 0; X < InvPercentChange; ++X)
			{
				BMPRect.Top = 0;
				BMPRect.Bottom = BitmapHeight;
				BMPRect.Left = X * NewWidth;
				BMPRect.Right = (X+1) * NewWidth;
				Y = X * BitmapHeight;
				NewBMP->CopyRect(FontBitmap, &BMPRect, 0, Y);
			}
			
			FontBitmap->Release();
			FontBitmap = NewBMP;
			m_FontTexture->SetBitmap(NewBMP);
		}
	}
#endif

	if (!GetTextureState())
		DEBUGRETURN(deFALSE, "SetFont: Failed to get a valid texturestate");

	m_TextureState->Lock();
	m_TextureState->SetBitmap(0, m_FontTexture);
	m_TextureState->Unlock();

	// the proxy holds the reference now
	FontBitmap->Release();

	//return
	return deTRUE;
}

deBoolean deFont::SetFont(IdeBitmapProxy *Font, long Height, long *Widths)
{
	//create a font with the specified bitmap
#pragma todo(SetFont(IdeBitmapProxy* ...) - unfinished function in deFont.cpp)
	Font = 0;
	Height = 0;
	Widths = 0;
	return deFALSE;
}

deBoolean deFont::SetColor(deARGB Color)
{
	//set the fore color for text
	m_Color = Color;

	//return
	return deTRUE;
}

deBoolean deFont::SetScale(deDouble Scale)
{
	//set the scale for the text we render
	m_Scale = Scale;

	return deTRUE;
}

deFloat deFont::GetStringWidth(char * String)
{
	unsigned int i;
	float Width = 0.0f;
	char * Char = String;
	for(i = 0; i < strlen(String); i++)
	{
		Width += m_Coords[*Char - 32].Right -  m_Coords[*Char - 32].Left;
		++Char;
	}

	Width *= m_FontTexture->GetWidth();

	return Width;
}

deFloat deFont::GetStringHeight(char * String)
{
	unsigned int i;
	float Height = 0.0f;
	char * Char = String;
	for(i = 0; i < strlen(String); i++)
	{
		float CharacterSize = m_Coords[*Char - 32].Bottom -  m_Coords[*Char - 32].Top;
		if(CharacterSize > Height)
			Height = CharacterSize;
		++Char;
	}

	Height *= m_FontTexture->GetHeight();

	return Height;
}

/*
deBoolean deFont::Print2D(deRect *Rect, deDouble X, deDouble Y, int DisplayFlags, char *Format, ...)
{
	//if not font, exit
	if(!m_FontBitmapBits)
		return deFALSE;

	char				PrintStr[256];		//1KB for a string better be long enough
	va_list				VAList;
	DriverEntry *		CurDriverEntry;
	void *				ptr;
	deFloat				Width;
	deFloat				Height;
	IdeVertexBuffer *	VertexBuffer = NULL;
	RenderStateValue	States[8];
	long				Counter;
//	deFloat				Aspect;

	//print 2d text on the screen with the attached driver

	//create the string to display
	va_start(VAList, Format);
	vsprintf(PrintStr, Format, VAList);
	va_end(VAList);
	
	//take the height of the rect and divide by the height of the bitmap to figure out
	//a pixel to pixel ratio then multiply by the scale
	//do the same for width
	Width = (deFloat)(Rect->Right - Rect->Left);
	Height = (deFloat)(Rect->Bottom - Rect->Top);
//	Aspect = Width/Height;
	if (DisplayFlags & deFONT_POSITION_PERCENT)
	{
		X = X * Width;
		Y = Y * Height;
	}
	Y = Height - Y;
	Y += 0.05;		// hack to try and stop letters from "dropping" by a pixel

	//create the vertex buffer
	VertexBuffer = CreateVertexBuffer(X, Y, 0.9, NULL, NULL, DisplayFlags, PrintStr);
	if(!VertexBuffer)
		return deFALSE;

	ptr = m_DriverList.GetNext(NULL, CurDriverEntry);
	while(ptr)
	{

		deTransform position;
		deVertex translation = {Width/2, Height/2, 0};
		deVertex ZVec = {0,0,1};
		deVertex YVec = {0,1,0};
		ZVec = ZVec + translation;
//		YVec = YVec + translation;
		position.Translation = deMake_Vec3d(translation);

		if (!CurDriverEntry->Driver->SetCameraTransform(&translation, &ZVec, &YVec))
		{
			goto ReturnFail;
		}

//#pragma message("does this really need to alter the viewport?")
//		if(!CurDriverEntry->Driver->SetViewport(Rect->Left, Rect->Top, Rect->Right - Rect->Left, Rect->Bottom - Rect->Top, 0.0f, 1.0f))
//		{
//			delete VertexBuffer;
//			return deFALSE;
//		}

		//set the driver up for a 2d render
		if(!CurDriverEntry->Driver->SetOrthoClipping(Width, Height, 0.0f, 1000.0f))
		{
			goto ReturnFail;
		}
		
		//disable the Z buffer
		States[0].State = IdeDriver::RENDER_ZENABLE;
		States[0].Value = CurDriverEntry->Driver->GetRenderState(States[0].State);
		if(!CurDriverEntry->Driver->SetRenderState(IdeDriver::RENDER_ZENABLE, deFALSE))
		{
			goto ReturnFail;
		}

		//we want to be in solid render mode
		States[1].State = IdeDriver::RENDER_FILLMODE;
		States[1].Value = CurDriverEntry->Driver->GetRenderState(States[1].State);
		if(!CurDriverEntry->Driver->SetRenderState(IdeDriver::RENDER_FILLMODE, IdeDriver::FILL_SOLID))
		{
			goto ReturnFail;
		}

		//we want to do alpha blending
		States[2].State = IdeDriver::RENDER_ALPHABLENDENABLE;
		States[2].Value = CurDriverEntry->Driver->GetRenderState(States[2].State);
		if(!CurDriverEntry->Driver->SetRenderState(IdeDriver::RENDER_ALPHABLENDENABLE, deTRUE))
		{
			goto ReturnFail;
		}

		//set that we want to do alpha testing
		States[3].State = IdeDriver::RENDER_ALPHATESTENABLE;
		States[3].Value = CurDriverEntry->Driver->GetRenderState(States[3].State);
		if(!CurDriverEntry->Driver->SetRenderState(IdeDriver::RENDER_ALPHATESTENABLE, deTRUE))
		{
			goto ReturnFail;
		}

		//set the type of alpha function
		States[4].State = IdeDriver::RENDER_ALPHAFUNCTION;
		States[4].Value = CurDriverEntry->Driver->GetRenderState(States[4].State);
		if(!CurDriverEntry->Driver->SetRenderState(IdeDriver::RENDER_ALPHAFUNCTION, IdeDriver::COMPARE_GREATEREQUAL))
		{
			goto ReturnFail;
		}

		//set the value that alpha is starting to be combined
		States[5].State = IdeDriver::RENDER_ALPHAREF;
		States[5].Value = CurDriverEntry->Driver->GetRenderState(States[5].State);
		if(!CurDriverEntry->Driver->SetRenderState(IdeDriver::RENDER_ALPHAREF, 8))
		{
			goto ReturnFail;
		}

		//tell it the source of the blend
		States[6].State = IdeDriver::RENDER_SOURCEBLEND;
		States[6].Value = CurDriverEntry->Driver->GetRenderState(States[6].State);
		if(!CurDriverEntry->Driver->SetRenderState(IdeDriver::RENDER_SOURCEBLEND, IdeDriver::BLEND_SOURCEALPHA))
		{
			goto ReturnFail;
		}
		
		//and the destination of the blend
		States[7].State = IdeDriver::RENDER_DESTBLEND;
		States[7].Value = CurDriverEntry->Driver->GetRenderState(States[7].State);
		if(!CurDriverEntry->Driver->SetRenderState(IdeDriver::RENDER_DESTBLEND, IdeDriver::BLEND_INVERSESOURCEALPHA))
		{
			goto ReturnFail;
		}
		
		//set the render state for the driver
		if(!CurDriverEntry->Driver->SetRenderState(CurDriverEntry->TextureState))
		{
			goto ReturnFail;
		}

		//tell the driver to add the vertex buffer
		if(!CurDriverEntry->Driver->AddVertexBuffer(VertexBuffer))
		{
			goto ReturnFail;
		}

		//render it
		if(!CurDriverEntry->Driver->Render(VertexBuffer))
		{
			CurDriverEntry->Driver->RemoveVertexBuffer(VertexBuffer);
			goto ReturnFail;
		}

		if(!CurDriverEntry->Driver->RemoveVertexBuffer(VertexBuffer))
		{
			goto ReturnFail;
		}

		//loop thru and set the states back for the driver as we are nice
		for(Counter = 0; Counter < 8; Counter++)
			CurDriverEntry->Driver->SetRenderState(States[Counter].State, States[Counter].Value);
		
		//next driver entry
		ptr = m_DriverList.GetNext(ptr, CurDriverEntry);
	}

	//free the vertex buffer
//#pragma todo(I think we should keep the vertex buffer somehow... make it a brush?)
	VertexBuffer->Release();

	//return
	return deTRUE;

ReturnFail:
	if (VertexBuffer)
		VertexBuffer->Release();
	return deFALSE;
}
*/

deBoolean deFont::Print3D(deVertex *Position, deVertex *Rotation, deDouble Depth, int DisplayFlags, const char *Format, ...)
{
	//print 3d text on the screen with the attached drivers
#pragma todo(Print3D - unfinished function in deFont.cpp)
	Format = 0;
	DisplayFlags = 0;
	Depth = 0;
	Rotation = 0;
	Position = 0;
	return deFALSE;
}

void deFont::InitData()
{
	//reset all the params
	m_Color = 0x00FFFFFF;
	m_FontTexture = NULL;
	m_Scale = 1.0f;

	m_FontTexture = NULL;
	m_TextureState = NULL;
}

Ide2DObject * deFont::Print2D(IdeDriver * Driver, deRect *ClipRect, deDouble X, deDouble Y, deDouble Depth, int *pStringWidth, int *pStringHeight, int DisplayFlags, const char *Format, ...)
{
	char				PrintStr[4096];		//4KB for a string better be long enough
	va_list				VAList;
	deFloat				Width;
	deFloat				Height;
	deRect				UseRect;
	IdeRenderTexture *	TextureState = NULL;
	IdeVertexBuffer *	VertexBuffer = NULL;
	Ide2DObject *		RetObject = NULL;

	//print 2d text on the screen with the attached driver

	//if not font, exit
	if(!m_FontTexture)
		return NULL;
	if (!ClipRect)
	{
		UseRect = Driver->GetDisplayRect();
		ClipRect = &UseRect;
	}

	//create the string to display
	va_start(VAList, Format);
	vsprintf(PrintStr, Format, VAList);
	va_end(VAList);
	
	//take the height of the rect and divide by the height of the bitmap to figure out
	//a pixel to pixel ratio then multiply by the scale
	//do the same for width
	Width = (deFloat)(ClipRect->Right - ClipRect->Left);
	Height = (deFloat)(ClipRect->Bottom - ClipRect->Top);
	if (DisplayFlags & POSITION_PERCENT)
	{
		X = X * Width;
		Y = Y * Height;
	}
//	Y = Height - Y;
	Y -= 0.0625;		// hack to try and stop letters from "dropping" by a pixel

	//create the vertex buffer
	VertexBuffer = CreateVertexBuffer(X, Y, Depth, pStringWidth, pStringHeight, DisplayFlags, PrintStr);
	if(!VertexBuffer)
		return NULL;

	RetObject = Ide2DObject_Create2DObject(VertexBuffer, GetTextureState(), (deFloat)Depth);
	if (!RetObject)
		goto Cleanup;

	return RetObject;

Cleanup:
	if (VertexBuffer)
		VertexBuffer->Release();
	if (TextureState)
		TextureState->Release();
	if (RetObject)
		RetObject->Release();
	return NULL;
}

IdeVertexBuffer *deFont::CreateVertexBuffer(deDouble X, deDouble Y, deDouble Depth, int *pStringWidth, int *pStringHeight, int DisplayFlags, char *Buffer)
{
	long				PointCount;
	long				Counter;
	deVertex *			Position;
	long *				Color;
	deTexCoord *		Coords;
	deFloat				CurX, CurY;
	deFloat				LetterHeight;
	deDouble			Width, Height;
	deFloat				Scaler;
	deFloatRect *		TextCoordPtr;
	IdeVertexBuffer *	VertexList;
	int					NewLines;
	deDouble			StringWidth, StringHeight;

	//create a vertex buffer for the string passed in

	//go thru each character, add 2 tris for each letter that is above 0x20
	Counter = 0;
	PointCount = 0;
	while(Buffer[Counter])
	{
		//if above 0x19, increment tricount
		if(Buffer[Counter] > 0x19)
			PointCount++;

		//go to the next letter
		Counter++;
	}
	
	//multiply the tricount by 6 as 2 tris per letter, 3 points per tri
	PointCount = PointCount * 6;

	//check and see if we have anything to render, if not, exit
	//i check here as the string could just be full of spaces for instance
	if(!PointCount)
		return NULL;

	//now, allocate memory for the tri's
	VertexList = IdeVertexBuffer_CreateVBuffer();
	//VertexList = IdeVertexBuffer_CreateSubVBuffer(de2DCollection::s_CompositeFor2D);
	if(!VertexList)
		return NULL;

	//lock it
	VertexList->Lock();

	if(!VertexList->SetBufferSize(PointCount, deFALSE))
	{
		VertexList->Release();
		return NULL;
	}

	//allocate memory for the points
	if(!VertexList->CreateBuffer(IdeVertexBuffer::BUFFER_POSITION, deFALSE))
	{
		//something failed, destroy and exit
		VertexList->Release();
		return NULL;
	}

	//a buffer for the color
	if(!VertexList->CreateBuffer(IdeVertexBuffer::BUFFER_DIFFUSE, deFALSE))
	{
		VertexList->Release();
		return NULL;
	}

	//add in the UV coords for the texture
	if(!VertexList->CreateBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0, deFALSE))
	{
		//something failed, destroy and exit
		VertexList->Release();
		return NULL;
	}

	//grab the buffers now that we created and fill them
	Position = (deVertex *)VertexList->GetBuffer(IdeVertexBuffer::BUFFER_POSITION);
	Color = (long *)VertexList->GetBuffer(IdeVertexBuffer::BUFFER_DIFFUSE);
	Coords = (deTexCoord *)VertexList->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0);

	//loop thru the string until we hit the end
	if (DisplayFlags & POSITION_RIGHT)
		Counter = strlen(Buffer) - 1;
	else
		Counter = 0;
	CurX = (deFloat)X;
	CurY = (deFloat)Y;
	LetterHeight = (m_Coords[0].Bottom - m_Coords[0].Top) * m_FontTexture->GetHeight();
	Scaler = (deFloat)(m_Scale * m_FontTexture->GetWidth());
	Width = 0;
	Height = LetterHeight;
	PointCount = 0;
	NewLines = 0;
	StringWidth = 0;

	while(Counter >= 0 && Buffer[Counter])
	{
		//check the data
		if(Buffer[Counter] == 0x0A)
		{
			//new line, reset the X, update the Y to the next line
			CurX = (deFloat)X;
			if (DisplayFlags & POSITION_RIGHT)
				CurY = CurY - LetterHeight;
			else
				CurY = CurY + LetterHeight;
			NewLines++;

			if (DisplayFlags & POSITION_HCENTER)
			{
				int TempCount;
				deFloat TempWidth = (deFloat)Width/2;
				for (TempCount = Counter-NewLines; TempCount >= 0 && Buffer[TempCount+NewLines-1] != 0x0A; TempCount--)
				{
					Position[TempCount*6+0].x -= TempWidth;
					Position[TempCount*6+1].x -= TempWidth;
					Position[TempCount*6+2].x -= TempWidth;
					Position[TempCount*6+3].x -= TempWidth;
					Position[TempCount*6+4].x -= TempWidth;
					Position[TempCount*6+5].x -= TempWidth;
				}
				Width = 0;
			}

		}
		else
		{
			//normal text, include space as font may have something for spaces

			TextCoordPtr = &m_Coords[(unsigned char)Buffer[Counter] - 32];

			//setup the positions
			if (DisplayFlags & POSITION_RIGHT)
			{
				Position[PointCount+0].x = CurX - ((TextCoordPtr->Right - TextCoordPtr->Left) * Scaler);
				Position[PointCount+2].x = CurX;
				Position[PointCount+1].x = Position[PointCount+0].x;
			}
			else
			{
				Position[PointCount+0].x = CurX;
				Position[PointCount+2].x = CurX + ((TextCoordPtr->Right - TextCoordPtr->Left) * Scaler);
				Position[PointCount+1].x = CurX;
			}

			/*
			if (DisplayFlags & deFONT_POSITION_BOTTOM)
			{
				Position[PointCount+0].y = CurY - LetterHeight;
				Position[PointCount+2].y = Position[PointCount+0].y;
				Position[PointCount+1].y = CurY;
			}
			else
			*/
			{
				Position[PointCount+0].y = CurY;
				Position[PointCount+2].y = CurY;
				Position[PointCount+1].y = CurY + LetterHeight;
			}

			Position[PointCount+0].z = (deFloat)Depth;
			Position[PointCount+1].z = (deFloat)Depth;
			Position[PointCount+2].z = (deFloat)Depth;
			Position[PointCount+4].x = Position[PointCount+2].x;
			Position[PointCount+4].y = Position[PointCount+1].y;
			Position[PointCount+4].z = (deFloat)Depth;

			Position[PointCount+3] = Position[PointCount+1];
			Position[PointCount+5] = Position[PointCount+2];
			
			//set the color for the 3 points on the tri
			Color[PointCount+0] = m_Color;
			Color[PointCount+1] = m_Color;
			Color[PointCount+2] = m_Color;
			Color[PointCount+3] = m_Color;
			Color[PointCount+4] = m_Color;
			Color[PointCount+5] = m_Color;

			//set the U/V coords
			Coords[PointCount+0].u = TextCoordPtr->Left;
			Coords[PointCount+1].u = TextCoordPtr->Left;
			Coords[PointCount+2].u = TextCoordPtr->Right;
			Coords[PointCount+3].u = TextCoordPtr->Left;
			Coords[PointCount+4].u = TextCoordPtr->Right;
			Coords[PointCount+5].u = TextCoordPtr->Right;

			Coords[PointCount+0].v = TextCoordPtr->Top;
			Coords[PointCount+1].v = TextCoordPtr->Bottom;
			Coords[PointCount+2].v = TextCoordPtr->Top;
			Coords[PointCount+3].v = TextCoordPtr->Bottom;
			Coords[PointCount+4].v = TextCoordPtr->Bottom;
			Coords[PointCount+5].v = TextCoordPtr->Top;

			PointCount = PointCount + 6;		

			//increment the X
			if (DisplayFlags & POSITION_RIGHT)
				CurX = CurX - ((TextCoordPtr->Right - TextCoordPtr->Left) * Scaler);
			else
			{
				CurX = CurX + ((TextCoordPtr->Right - TextCoordPtr->Left) * Scaler);
				Width = max(Width, CurX-X);
				StringWidth = max(StringWidth, Width);
			}
		}

		if (DisplayFlags & POSITION_RIGHT)
		{
			Counter--;
		}
		else
		{
			Counter++;
		}

	}

	Height = (NewLines + 1) * LetterHeight;
	StringHeight = Height;

	// do this before modifying NewLines
	if (DisplayFlags & POSITION_RIGHT)
		Height -= LetterHeight;

	// do this before modifying Counter
	if (DisplayFlags & POSITION_HCENTER)
	{
		int TempCount;
		deFloat TempWidth = (deFloat)Width/2;
		NewLines++;
		for (TempCount = Counter-NewLines; TempCount >= 0 && Buffer[TempCount+NewLines-1] != 0x0A; TempCount--)
		{
			Position[TempCount*6+0].x -= TempWidth;
			Position[TempCount*6+1].x -= TempWidth;
			Position[TempCount*6+2].x -= TempWidth;
			Position[TempCount*6+3].x -= TempWidth;
			Position[TempCount*6+4].x -= TempWidth;
			Position[TempCount*6+5].x -= TempWidth;
		}
	}

	if (DisplayFlags & POSITION_BOTTOM)
	{
		if (DisplayFlags & POSITION_RIGHT)
		{
			Height -= LetterHeight;
			for (Counter = 0; Counter < PointCount; Counter++)
			{
				Position[Counter].y += (deFloat)Height;
			}
		}
		else
		{
			for (Counter = 0; Counter < PointCount; Counter++)
			{
				Position[Counter].y += (deFloat)Height;
			}
		}
	}
	else if (DisplayFlags & POSITION_VCENTER)
	{
		Height /= 2;
		if (DisplayFlags & POSITION_RIGHT)
		{
			Height -= LetterHeight/2;
			for (Counter = 0; Counter < PointCount; Counter++)
			{
				Position[Counter].y -= (deFloat)Height;
			}
		}
		else
		{
			for (Counter = 0; Counter < PointCount; Counter++)
			{
				Position[Counter].y += (deFloat)Height;
			}
		}
	}
	else if (DisplayFlags & POSITION_RIGHT)
	{
		Height -= LetterHeight/2;
		for (Counter = 0; Counter < PointCount; Counter++)
		{
			Position[Counter].y -= (deFloat)Height;
		}
	}

	//make sure the tricount is right
//	VertexList->SetTriCount(PointCount / 3);

	//make sure we are doing a tri list
	VertexList->SetRenderType(IdeVertexBuffer::RENDER_TRIANGLELIST);

	//make sure the buffers are dirtied
	VertexList->DirtyBuffer(IdeVertexBuffer::BUFFER_POSITION);
	VertexList->DirtyBuffer(IdeVertexBuffer::BUFFER_DIFFUSE);
	VertexList->DirtyBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0);

	VertexList->Unlock();

	if (pStringWidth)
		*pStringWidth = (int)(StringWidth + 0.5f);
	if (pStringHeight)
		*pStringHeight = (int)(StringHeight + 0.5f);

	//return it
	return VertexList;
}

/*
	// Test pattern for viewing entire font bitmap
	// goes in CreateVertexBuffer before the while loop
	// the vertex buffer needs to be made 6 items larger also
	Position[0].x = 0;
	Position[1].x = 800;
	Position[2].x = 0;
	Position[3].x = 800;
	Position[4].x = 800;
	Position[5].x = 0;
	Position[0].y = 600;
	Position[1].y = 600;
	Position[2].y = 0;
	Position[3].y = 600;
	Position[4].y = 0;
	Position[5].y = 0;
	Position[0].z = 1;
	Position[1].z = 1;
	Position[2].z = 1;
	Position[3].z = 1;
	Position[4].z = 1;
	Position[5].z = 1;
	Coords[0].u = 0;
	Coords[1].u = 1;
	Coords[2].u = 0;
	Coords[3].u = 1;
	Coords[4].u = 1;
	Coords[5].u = 0;
	Coords[0].v = 0;
	Coords[1].v = 0;
	Coords[2].v = 1;
	Coords[3].v = 0;
	Coords[4].v = 1;
	Coords[5].v = 1;
	Color[0] = m_Color;
	Color[1] = m_Color;
	Color[2] = m_Color;
	Color[3] = m_Color;
	Color[4] = m_Color;
	Color[5] = m_Color;
	PointCount = 6;
*/


IdeRenderTexture* deFont::GetTextureState()
{
	if (!m_TextureState)
	{
		m_TextureState = IdeRenderState_CreateRenderTexture();
		if (!m_TextureState)
			DEBUGRETURN(NULL, "GetTextureState: Failed to create m_TextureState");
		
		m_TextureState->Lock();
		
		m_TextureState->SetColorArg(0, 1, IdeRenderTexture::TEXTUREARG_TEXTURE);
		m_TextureState->SetColorArg(0, 2, IdeRenderTexture::TEXTUREARG_DIFFUSE);
		m_TextureState->SetColorOperation(0, IdeRenderTexture::TEXTUREOP_MODULATE); //MODULATE);
		
		m_TextureState->SetAlphaArg(0, 1, IdeRenderTexture::TEXTUREARG_TEXTURE);
		m_TextureState->SetAlphaOperation(0, IdeRenderTexture::TEXTUREOP_SELECTARG1); //MODULATE);
		
		m_TextureState->SetColorOperation(1, IdeRenderTexture::TEXTUREOP_DISABLE);
		m_TextureState->SetAlphaOperation(1, IdeRenderTexture::TEXTUREOP_DISABLE);
		
		m_TextureState->SetBitmap(0, m_FontTexture);
		
		m_TextureState->SetTextureMinFilter(0, IdeRenderTexture::TEXTUREFILTER_POINT);
		m_TextureState->SetTextureMagFilter(0, IdeRenderTexture::TEXTUREFILTER_POINT);
		m_TextureState->SetTextureMipFilter(0, IdeRenderTexture::TEXTUREFILTER_NONE);
		
		m_TextureState->Unlock();
	}

	return m_TextureState;
}
