//////////////////////////////////////////////////////////////////////
//
//  CryFont Source Code
//
//  File: FFont.cpp
//  Description: Font class.
//
//  History:
//  - August 20, 2001: d by Alberto Demichelis
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "FFont.h"
#include "FBitmap.h"
#include "CryFont.h"
//#include <Image.h>
#ifdef PS2
#include "PS2Font.h"
#endif

#include "FontTexture.h"
#ifdef WIN64
#undef GetCharWidth
#undef GetCharHeight
#endif
///////////////////////////////////////////////
CFFont::CFFont(struct ISystem *pISystem, class CCryFont *pCryFont, const char *pszName)
{
	m_bRealPixels=false;
	m_fWidthScale = 1.0f;
	m_bSameSize = false;
	//	m_pBitmap = NULL;
	m_pISystem = pISystem;
	m_pCryFont = pCryFont;
	m_szName = pszName;
	m_vSize.set(16, 16);
	m_iTextureID = -1;
	m_vCharSize=vector2f(-1,-1);

	m_fClipX = m_fClipY = 0.0f;
	m_fClipR = m_fClipB = 0.0f;

	m_bClipEnabled = 0;
	m_pFontBuffer = 0;

	m_vColorTable[0]=0xff000000;
	m_vColorTable[1]=0xffffffff;
	m_vColorTable[2]=0xffff0000;
	m_vColorTable[3]=0xff00ff00;
	m_vColorTable[4]=0xff0000ff;
	m_vColorTable[5]=0xffffff00;
	m_vColorTable[6]=0xff00ffff;
	m_vColorTable[7]=0xffff00ff;
	m_vColorTable[8]=0xff0080ff;
	m_vColorTable[9]=0xff8f8f8f;

	// create the default effect
	SEffect *pEffect = NewEffect();
	pEffect->strName = "default";
	pEffect->NewPass();
	SetEffect("default");

}

///////////////////////////////////////////////
CFFont::~CFFont()
{
	if (m_pCryFont)
	{
		m_pCryFont->UnregisterFont(m_szName);
		m_pCryFont = 0;
	}
	Free();
}

void CFFont::Reset()
{
	m_fWidthScale = 1.0f;
	m_bClipEnabled = 0;
	m_vSize = vector2f(16.0f, 16.0f);
	
	m_bSameSize = 0;
	m_bRealPixels = 0;
	m_vCharSize = vector2f(-1.0f, -1.0f);
}


///////////////////////////////////////////////
// Release the memory...
void CFFont::Release()
{
	delete this;
}

///////////////////////////////////////////////
// Load a font from a CImage
/*bool CFFont::Load(class CImage *pImg)
{
Free();

m_pBitmap = new CFBitmap(pImg);
if(!m_pBitmap)
return false;

for(int i = 0; i < 256; ++i)
m_TexCooMap[i].z = 1.0f;

return RenderInit();
}*/

///////////////////////////////////////////////
// Load a font from a TTF file
bool CFFont::Load(const char *szFile, unsigned long nWidth, unsigned long nHeight, unsigned long nTTFFlags)
{
	Free();

	ICryPak *pPak = m_pISystem->GetIPak();

	string szFullFile;
	if (pPak->IsAbsPath(szFile))
		szFullFile = szFile;
	else
		szFullFile = m_szCurPath + szFile;	

	int i = 0;
	
#ifdef PS2
	// [marco] for ps2 use the game path to prepend 
	// the file name or load from pack as shown below	
	if((m_pBitmap=LoadXtfFont(szFullFile.c_str(), &m_TexCooMap[0])))
	{
		m_bOK = true;
		return RenderInit();
	}
#endif


	int iSmoothMethod = (nTTFFlags & TTFFLAG_SMOOTH_MASK) >> TTFFLAG_SMOOTH_SHIFT;
	int iSmoothAmount = (nTTFFlags & TTFFLAG_SMOOTH_AMOUNT_MASK) >> TTFFLAG_SMOOTH_AMOUNT_SHIFT;

	FILE *pFile = pPak->FOpen(szFullFile.c_str(),"rb");

	if (!pFile)
		return false;

	pPak->FSeek(pFile, 0, SEEK_END); 
	int nSize = pPak->FTell(pFile); 
	pPak->FSeek(pFile, 0, SEEK_SET); 

	if (!nSize)
	{
		pPak->FClose(pFile); 

		return false;
	}	

	unsigned char *pBuffer = new unsigned char[nSize];

	if (!pPak->FReadRaw(pBuffer, nSize, 1, pFile))
	{
		pPak->FClose(pFile);

		delete[] pBuffer;

		return false;
	}

	pPak->FClose(pFile);

	if (!m_pFontTexture.CreateFromMemory(pBuffer, nSize, nWidth, nHeight, iSmoothMethod, iSmoothAmount))
	{
		delete[] pBuffer;

		return false;
	}

	m_pFontBuffer = pBuffer;

	//------------------------------------------------------------------------------------------------- 

	m_bOK = true;
#ifdef PS2
	ConvertXtfFont(m_pBitmap, m_pBitmap->GetWidth(),m_pBitmap->GetHeight(), (uchar *)m_pBitmap->m_pData);
	SaveXtfFont(szFile, m_pBitmap, &m_TexCooMap[0], m_pBitmap->GetWidth(),m_pBitmap->GetHeight());
#endif	
	return true;
}

///////////////////////////////////////////////
// Free the memory
void CFFont::Free()
{
	RenderCleanup();

	if (m_pFontBuffer)
	{
		delete[] m_pFontBuffer;
	}
	m_pFontBuffer = 0;

	m_bOK = false;
}

///////////////////////////////////////////////
// Set the current effect to use
void CFFont::SetEffect(const char *szEffect)
{
	m_pCurrentEffect = NULL;
	if(!szEffect)
		szEffect = "default";

	for(int i = 0; i < (int)m_vEffects.size(); ++i)
	{
		if(strcmp(m_vEffects[i].strName.c_str(), szEffect) == 0)
		{
			m_pCurrentEffect = &m_vEffects[i];
			return;
		}
	}

	if(!m_pCurrentEffect)
	{
		m_pCurrentEffect = &m_vEffects[0];
	}
}

// Set clipping rectangle
void CFFont::SetClippingRect(float fX, float fY, float fX2, float fY2)
{
	m_fClipX = fX;
	m_fClipY = fY;
	m_fClipR = fX2;
	m_fClipB = fY2;
}

// Enable / Disable clipping (off by default)
void CFFont::EnableClipping(bool bEnable)
{
	m_bClipEnabled = bEnable;
}


///////////////////////////////////////////////
void CFFont::SetColor(const ColorF& col, int nPass)
{
	SRenderingPass *pPass;
	if(nPass < 0)
	{
		for(std::vector<SRenderingPass>::iterator i = m_pCurrentEffect->vPass.begin();
			i != m_pCurrentEffect->vPass.end(); ++i)
		{
			pPass = &(*i);
			pPass->SetColor(col);
		}
	}
	else
	{
		if(nPass >= (int)m_pCurrentEffect->vPass.size())
			return;
		pPass = &m_pCurrentEffect->vPass[nPass];
		pPass->SetColor(col);
	}
}

///////////////////////////////////////////////
// Set the characters base size
void CFFont::SetSize(const vector2f &vSize)
{
	m_vCharSize = vector2f(-1.0f, -1.0f);
	m_vSize = vSize;
}


///////////////////////////////////////////////
// Set the same size flag
void CFFont::SetSameSize(bool bSameSize)
{
	m_bSameSize = bSameSize;
	m_vCharSize = vector2f(-1.0f, -1.0f);
}

///////////////////////////////////////////////
// Get the same size flag
bool CFFont::GetSameSize()
{
	return m_bSameSize;
}

///////////////////////////////////////////////
// Return the seted size
vector2f &CFFont::GetSize()
{
	return m_vSize;
}


///////////////////////////////////////////////
// Return the char width
float CFFont::GetCharWidth()
{
	IRenderer *pRenderer = m_pISystem->GetIRenderer();
	assert(pRenderer);

	if (m_vCharSize.x == -1.0f)
	{
		float fMaxW = m_vSize.x;

		if (m_pCurrentEffect)
		{
			
			for(int i = m_pCurrentEffect->vPass.size()-1; i >= 0; --i)
			{
				SRenderingPass *Pass = &m_pCurrentEffect->vPass[i];

				float fScale = Pass->vSizeScale.x;
				float fOffset = Pass->vPosOffset.x;
				float fPassW = fScale * m_vSize.x + fOffset;

				if (!m_bRealPixels)
				{
					fPassW = pRenderer->ScaleCoordX(fScale * m_vSize.x) + fOffset;
				}

				if (m_bSameSize)
				{
					fPassW *= m_fWidthScale;
				}

				if (m_bRealPixels)

				if (fPassW > fMaxW)
				{
					fMaxW = fPassW;
				}
			}
		}

		return fMaxW;
	}

	return m_vCharSize.x;
}

///////////////////////////////////////////////
// Return the char height
float CFFont::GetCharHeight()
{
	IRenderer *pRenderer = m_pISystem->GetIRenderer();
	assert(pRenderer);

	if (m_vCharSize.y == -1.0f)
	{
		float fMaxH = m_vSize.y;

		if (m_pCurrentEffect)
		{

			for(int i = m_pCurrentEffect->vPass.size()-1; i >= 0; --i)
			{
				SRenderingPass *Pass = &m_pCurrentEffect->vPass[i];

				float fScale = Pass->vSizeScale.y;
				float fOffset = Pass->vPosOffset.y;
				float fPassH = fScale * m_vSize.y + fOffset;

				if (!m_bRealPixels)
				{
					fPassH = pRenderer->ScaleCoordY(fScale * m_vSize.y) + fOffset;
				}

				if (fPassH > fMaxH)
				{
					fMaxH = fPassH;
				}
			}
		}

		return fMaxH;
	}

	return m_vCharSize.y;
}

///////////////////////////////////////////////
void CFFont::SetCharWidthScale(float fScale)
{
	m_fWidthScale = fScale;
}

///////////////////////////////////////////////
float CFFont::GetCharWidthScale()
{
	return m_fWidthScale;
}

/*
#define FONT_RGBA(r, g, b, a) \
(   (((long)((a) * 255)) << 24) | (((long)((r) * 255)) << 16) \
|   (((long)((g) * 255)) << 8) | (long)((b) * 255) \
)
*/

#define FONT_RGBA(r, g, b, a) \
	(   (((long)((a) * 255)) << 24) | (((long)((b) * 255)) << 16) \
	|   (((long)((g) * 255)) << 8) | (long)((r) * 255) \
	)

ILINE DWORD COLCONV (DWORD clr)
{
	return ((clr & 0xff00ff00) | ((clr & 0xff0000)>>16) | ((clr & 0xff)<<16));
}

///////////////////////////////////////////////
// Draw a formated string

void CFFont::DrawString( float fBaseX, float fBaseY, const char *szMsg, const bool bASCIIMultiLine )
{
	if (!szMsg)
	{
		return;
	}

	int iSize = min(1023, (int)strlen(szMsg));

	static wchar_t szwMsg[1024];

	szwMsg[iSize] = 0;
	while (iSize--)
	{
		szwMsg[iSize] = (unsigned char)szMsg[iSize];
	}

	DrawStringW(fBaseX, fBaseY, szwMsg, bASCIIMultiLine);
}

///////////////////////////////////////////////
// Draw a formated string, taking z into account

void CFFont::DrawString( float fBaseX, float fBaseY, float fBaseZ, const char *szMsg, const bool bASCIIMultiLine )
{
	if (!szMsg)
	{
		return;
	}

	int iSize = min(1023, (int)strlen(szMsg));

	static wchar_t szwMsg[1024];

	szwMsg[iSize] = 0;
	while (iSize--)
	{
		szwMsg[iSize] = (unsigned char)szMsg[iSize];
	}

	DrawStringW(fBaseX, fBaseY, fBaseZ, szwMsg, bASCIIMultiLine);
}

void CFFont::DrawStringW(float fBaseX, float fBaseY, const wchar_t *szMsg, const bool bASCIIMultiLine)
{
	DrawStringW(fBaseX,fBaseY,1.0f,szMsg,bASCIIMultiLine);
}

void CFFont::DrawStringW(float fBaseX, float fBaseY, float fBaseZ, const wchar_t *szMsg, const bool bASCIIMultiLine)
{
	// please terminate your strings with a '\0'
	// if you want to draw a string with more than 682 char, tell me (marcio)
	// and will allocate two buffers
	//assert(wcslen(szMsg) <= 682);

	if (!m_pCurrentEffect || m_pCurrentEffect->vPass.empty())
		return;

	IRenderer *pRenderer = m_pISystem->GetIRenderer();
	assert(pRenderer);

	if (!szMsg)
	{
		return;
	}

  if (m_iTextureID <= 0)
    RenderInit();

	Prepare(szMsg);

	float fTexHeight = m_pFontTexture.GetCellHeight() / (float)m_pFontTexture.GetHeight();
	bool	bRGB = (pRenderer->GetFeatures() & RFT_RGBA) != 0;
	float fAlpha = m_pCurrentEffect->vPass[0].cColor.a;
	struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F *pVertex = 0;
	int		iVertexOffset = 0;
	int		iTextLength = GetTextLengthW(szMsg);

	pRenderer->FontSetTexture(m_iTextureID, FILTER_TRILINEAR);
	pRenderer->FontSetRenderingState(0, 0);

	for(int i = m_pCurrentEffect->vPass.size()-1; i >= 0; --i)
	{
		if (!i)
		{
			fAlpha = 1.0f;
		}		

		SRenderingPass *Pass = &m_pCurrentEffect->vPass[i];
		
		// gather pass data
		vector2f	vBaseXY = vector2f(fBaseX, fBaseY);
		vector2f	vOffset = Pass->vPosOffset;
		vector2f	vSize = vector2f(m_vSize.x * Pass->vSizeScale.x, m_vSize.y * Pass->vSizeScale.y);
		DWORD			dwPassColor = 0;
		DWORD			dwColor = 0;
		int				iVBLen = 0;
		
		if (!m_bRealPixels)
		{
			vSize.x = pRenderer->ScaleCoordX(vSize.x);
			vSize.y = pRenderer->ScaleCoordY(vSize.y);
			vBaseXY.x = pRenderer->ScaleCoordX(vBaseXY.x);
			vBaseXY.y = pRenderer->ScaleCoordY(vBaseXY.y);
		}

		float			fRcpCellWidth = (1.0f / (float)m_pFontTexture.GetCellWidth()) * vSize.x;
		float			fCharX = vBaseXY.x + vOffset.x;
		float			fCharY = vBaseXY.y + vOffset.y;

		if (bRGB)
		{
			dwPassColor = FONT_RGBA(Pass->cColor.r, Pass->cColor.g, Pass->cColor.b, Pass->cColor.a * fAlpha);
		}
		else
		{
			dwPassColor = FONT_RGBA(Pass->cColor.b, Pass->cColor.g, Pass->cColor.r, Pass->cColor.a * fAlpha);
		}

#if defined(NEED_ENDIAN_SWAP)
		SwapEndian(dwPassColor);
#endif

		dwColor = dwPassColor;
		
		pRenderer->FontSetBlending(Pass->blendSrc, Pass->blendDest);
		pVertex = (struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F *)pRenderer->GetDynVBPtr(iTextLength * 6, iVertexOffset, 0);
		assert(pVertex);

		wchar_t *pcChar = (wchar_t *)szMsg;
		wchar_t ch;

		// parse the string, ignoring control characters
		while (ch = *pcChar++)
		{
			switch(ch)
			{
			case L'\\':
				{
					if (*pcChar != L'n' || !bASCIIMultiLine)
					{
						break;
					}
					++pcChar;
				}
			case L'\n':
				{
					fCharX = vBaseXY.x + vOffset.x;
					fCharY += vSize.y;
					continue;
				}
				break;
			case L'\r':
				{
					fCharX = vBaseXY.x + vOffset.x;

					continue;
				}
				break;
			case L'\t':
				{
					if (m_bSameSize)
					{
						fCharX += 4 * vSize.x * m_fWidthScale;
					}
					else
					{
						fCharX += FONT_SPACE_SIZE * 4 * vSize.x;
					}

					continue;
				}
				break;
			case L' ':
				{
					if (m_bSameSize)
					{
						fCharX += vSize.x * m_fWidthScale;
					}
					else
					{
						fCharX += FONT_SPACE_SIZE * vSize.x;
					}
					
					continue;
				}
				break;
			case L'$':
				{
					if (*pcChar == L'$')
					{
						++pcChar;
					}
					else if(isdigit(*pcChar))
					{
						if (!i)
						{
							int iColorIndex = (*pcChar) - L'0';

							if (bRGB)
							{
								dwColor = m_vColorTable[iColorIndex];
							}
							else
							{
								dwColor = COLCONV(m_vColorTable[iColorIndex]);
							}

							// apply the correct alpha
							dwColor = (dwColor & 0x00ffffff) | ((long)((Pass->cColor.a * fAlpha) * 255.0f)) << 24;
#if defined(NEED_ENDIAN_SWAP)
							SwapEndian(dwColor);
#endif
						}

						++pcChar;

						continue;
					}
					else if (*pcChar == L'O' || *pcChar == L'o')
					{
						if (!i)
						{
							dwColor = dwPassColor;
						}

						++pcChar;

						continue;
					}
					else if (*pcChar)
					{
						++pcChar;

						continue;
					}
				}
				break;
			default:
				break;
			}

			int iCharWidth = m_pFontTexture.GetCharWidth(ch);
			float fWidth = iCharWidth * fRcpCellWidth;
			float fAdvance = (iCharWidth + 1) * fRcpCellWidth;
			float fTexWidth = (iCharWidth + 1) / (float)m_pFontTexture.GetWidth();

			// get texture coordinates
			float vTexCoord[4];

			m_pFontTexture.GetTextureCoord(ch, &vTexCoord[0], &vTexCoord[1]);

			vTexCoord[2] = vTexCoord[0] + fTexWidth;
			vTexCoord[3] = vTexCoord[1] + fTexHeight;

			float fX = fCharX;
			float fY = fCharY;

			if (m_bSameSize)
			{						
				fX = fCharX + ((vSize.x * m_fWidthScale) - fWidth) * 0.5f;
				fAdvance = vSize.x * m_fWidthScale;
			}

			float fR = fX + fWidth;
			float fB = fY + vSize.y;

			// compute clipping
			float fNewX = fX;
			float fNewY = fY;
			float fNewR = fR;
			float fNewB = fB;
			
			if (m_bClipEnabled)
			{
				// clip non visible
				if ((fX >= m_fClipR) || (fY >= m_fClipB) || (fR < m_fClipX) || (fB < m_fClipY))
				{
					fCharX += fAdvance;

					continue;
				}
				// clip partially visible
				else
				{
					if ((fWidth <= 0.0f) || (vSize.y <= 0.0f))
					{
						fCharX += fAdvance;

						continue;
					}

					// clip the image to the scissor rect
					fNewX = max(m_fClipX, fX);
					fNewY = max(m_fClipY, fY);
					fNewR = min(m_fClipR, fR);
					fNewB = min(m_fClipB, fB);

					float fRcpWidth = 1.0f / fWidth;
					float fRcpHeight = 1.0f / vSize.y;

					float	fTexW = vTexCoord[2] - vTexCoord[0];
					float fTexH = vTexCoord[3] - vTexCoord[1];

					// clip horizontal
					vTexCoord[0] = vTexCoord[0] + (fTexW * ((fNewX - fX) * fRcpWidth));
					vTexCoord[2] = vTexCoord[2] + (fTexW * ((fNewR - (fX + fWidth)) * fRcpWidth));

					// clip vertical
					vTexCoord[1] = vTexCoord[1] + (fTexH * ((fNewY - fY) * fRcpHeight));
					vTexCoord[3] = vTexCoord[3] + (fTexH * ((fNewB - (fY + vSize.y)) * fRcpHeight));
				}
			}

			int iOffset = iVBLen * 6;

			// define char quad
			pVertex[iOffset].xyz.x = fNewX;
			pVertex[iOffset].xyz.y = fNewY;
			pVertex[iOffset].xyz.z = fBaseZ;
			pVertex[iOffset].color.dcolor = dwColor;
			pVertex[iOffset].st[0] = vTexCoord[0];
			pVertex[iOffset++].st[1] = vTexCoord[1];

			pVertex[iOffset].xyz.x = fNewR;
			pVertex[iOffset].xyz.y = fNewY;
			pVertex[iOffset].xyz.z = fBaseZ;
			pVertex[iOffset].color.dcolor = dwColor;
			pVertex[iOffset].st[0] = vTexCoord[2];
			pVertex[iOffset++].st[1] = vTexCoord[1];

			pVertex[iOffset].xyz.x = fNewR;
			pVertex[iOffset].xyz.y = fNewB;
			pVertex[iOffset].xyz.z = fBaseZ;
			pVertex[iOffset].color.dcolor = dwColor;
			pVertex[iOffset].st[0] = vTexCoord[2];
			pVertex[iOffset++].st[1] = vTexCoord[3];

			pVertex[iOffset].xyz.x = fNewR;
			pVertex[iOffset].xyz.y = fNewB;
			pVertex[iOffset].xyz.z = fBaseZ;
			pVertex[iOffset].color.dcolor = dwColor;
			pVertex[iOffset].st[0] = vTexCoord[2];
			pVertex[iOffset++].st[1] = vTexCoord[3];

			pVertex[iOffset].xyz.x = fNewX;
			pVertex[iOffset].xyz.y = fNewB;
			pVertex[iOffset].xyz.z = fBaseZ;
			pVertex[iOffset].color.dcolor = dwColor;
			pVertex[iOffset].st[0] = vTexCoord[0];
			pVertex[iOffset++].st[1] = vTexCoord[3];

			pVertex[iOffset].xyz.x = fNewX;
			pVertex[iOffset].xyz.y = fNewY;
			pVertex[iOffset].xyz.z = fBaseZ;
			pVertex[iOffset].color.dcolor = dwColor;
			pVertex[iOffset].st[0] = vTexCoord[0];
			pVertex[iOffset++].st[1] = vTexCoord[1];

			if (iVBLen >= 682)
			{
				break;
			}

			++iVBLen;

			fCharX += fAdvance;
		}

		// draw this pass
		pRenderer->DrawDynVB(iVertexOffset, 0, iVBLen * 6);
	}

	// restore the old states	
	pRenderer->FontRestoreRenderingState();	
}

void CFFont::DrawWrappedStringW( float fBaseX, float fBaseY, float w, const wchar_t *szMsg, const bool bASCIIMultiLine )
{
	wstring szWrapped;

	WrapText(szWrapped, w, szMsg);
	DrawStringW(fBaseX, fBaseY, szWrapped.c_str(), bASCIIMultiLine);
}

vector2f CFFont::GetWrappedTextSizeW(const wchar_t *swStr, float w, const bool bASCIIMultiLine)
{
	wstring szWrapped;

	WrapText(szWrapped, w, swStr);
	return GetTextSizeW(szWrapped.c_str(), bASCIIMultiLine);
}

vector2f CFFont::GetTextSize(const char *szMsg, const bool bASCIIMultiLine)
{
	if (!szMsg)
	{
		return vector2f(0.0f, 0.0f);
	}

	int iSize = min(1023, (int)strlen(szMsg));

	static wchar_t szwMsg[1024];

	szwMsg[iSize] = 0;
	while (iSize--)
	{
		szwMsg[iSize] = (unsigned char)szMsg[iSize];
	}

	return GetTextSizeW(szwMsg,bASCIIMultiLine);
}

///////////////////////////////////////////////
// Compute the text size
vector2f CFFont::GetTextSizeW(const wchar_t *szMsg, const bool bASCIIMultiLine)
{
	IRenderer *pRenderer = m_pISystem->GetIRenderer();
	assert(pRenderer);

	if (wcslen(szMsg) == 1 && *szMsg == L'$')
	{
		int x = 0;
	}

	if (!szMsg)
	{
		return vector2f(0,0);
	}

  if (m_iTextureID <= 0)
    RenderInit();

  Prepare(szMsg);

	float fMaxW = 0.0f;
	float fMaxH = 0.0f;

	for(int i = m_pCurrentEffect->vPass.size()-1; i >= 0; --i)
	{
		SRenderingPass *Pass = &m_pCurrentEffect->vPass[i];

		// gather pass data
		vector2f	vOffset = Pass->vPosOffset;
		vector2f	vSize = vector2f(m_vSize.x * Pass->vSizeScale.x, m_vSize.y * Pass->vSizeScale.y);

		if (!m_bRealPixels)
		{
			vSize.x = pRenderer->ScaleCoordX(vSize.x);
			vSize.y = pRenderer->ScaleCoordY(vSize.y);
		}

		float			fRcpCellWidth = (1.0f / (float)m_pFontTexture.GetCellWidth()) * vSize.x;
		float			fCharX = vOffset.x;
		float			fCharY = vOffset.y + vSize.y;

		if (fCharY > fMaxH)
		{
			fMaxH = fCharY;
		}

		wchar_t *pcChar = (wchar_t *)szMsg;
		wchar_t ch;

		// parse the string, ignoring control characters
		while (ch = *pcChar++)
		{
			switch(ch)
			{
			case L'\\':
				{
					if (*pcChar != L'n' || !bASCIIMultiLine)
					{
						break;
					}
					++pcChar;
				}
			case L'\n':
				{
					if (fCharX > fMaxW)
					{
						fMaxW = fCharX;
					}

					fCharX = vOffset.x;
					fCharY += vSize.y;

					if (fCharY > fMaxH)
					{
						fMaxH = fCharY;
					}

					continue;
				}
				break;
			case L'\r':
				{
					if (fCharX > fMaxW)
					{
						fMaxW = fCharX;
					}

					fCharX = vOffset.x;

					continue;
				}
				break;
			case L'\t':
				{
					fCharX += FONT_SPACE_SIZE * 4 * vSize.x;

					continue;
				}
				break;
			case L' ':
				{
					fCharX += FONT_SPACE_SIZE * vSize.x;

					continue;
				}
				break;
			case L'$':
				{
					if (*pcChar == L'$')
					{
						++pcChar;
					}
					else if (*pcChar)
					{
						++pcChar;

						continue;
					}
				}
				break;
			default:
				break;
			}

			int iCharWidth = m_pFontTexture.GetCharWidth(ch);
			float fAdvance = (iCharWidth + 1) * fRcpCellWidth;

			fCharX += fAdvance;
		}

		if (fCharX > fMaxW)
		{
			fMaxW = fCharX;
		}
	}

	return vector2f(fMaxW, fMaxH);
}

///////////////////////////////////////////////
int CFFont::GetTextLengthW(const wchar_t *szMsg, const bool bASCIIMultiLine)
{
	int iLength = 0;

	wchar_t *pcChar = (wchar_t *)szMsg;
	wchar_t ch;

	// parse the string, ignoring control characters
	while (ch = *pcChar++)
	{
		switch(ch)
		{
		case L'\\':
			{
				if (*pcChar != L'n' || !bASCIIMultiLine)
				{
					break;
				}
				++pcChar;
			}
		case L'\n':
		case L'\r':
		case L'\t':
			{
				continue;
			}
			break;
		case L'$':
			{
				if (*pcChar == L'$')
				{
					++pcChar;
				}
				else if (*pcChar)
				{
					++pcChar;

					continue;
				}
			}
			break;
		default:
			break;
		}
		++iLength;
	}

	return iLength;
}

///////////////////////////////////////////////
int CFFont::GetTextLength(const char *szMsg, const bool bASCIIMultiLine)
{
	int iLength = 0;

	char *pcChar = (char *)szMsg;
	char ch;

	// parse the string, ignoring control characters
	while (ch = *pcChar++)
	{
		switch(ch)
		{
		case '\\':
			{
				if (*pcChar != L'n' || !bASCIIMultiLine)
				{
					break;
				}
				++pcChar;
			}
		case '\n':
		case '\r':
		case '\t':
			{
				continue;
			}
			break;
		case '$':
			{
				if (*pcChar == L'$')
				{
					++pcChar;
				}
				else if (*pcChar)
				{
					++pcChar;

					continue;
				}
			}
			break;
		default:
			break;
		}
		++iLength;
	}

	return iLength;
}

///////////////////////////////////////////////
// Push a new effect in the vector and return a pointer to it
CFFont::SEffect* CFFont::NewEffect()
{
	SEffect effect;
	m_vEffects.push_back(effect);
	return &m_vEffects[m_vEffects.size()-1];
}

///////////////////////////////////////////////
// Return the current effect
CFFont::SEffect* CFFont::GetCurrentEffect()
{
	return m_pCurrentEffect;
}

///////////////////////////////////////////////
bool CFFont::RenderInit()
{
	m_iTextureID = m_pISystem->GetIRenderer()->FontCreateTexture(m_pFontTexture.GetWidth(), m_pFontTexture.GetHeight(), (byte*)m_pFontTexture.GetBuffer(), 
#ifdef FONT_USE_32BIT_TEXTURE
		eTF_A8R8G8B8);
#else
		eTF_A8);
#endif

	if (m_iTextureID < 0)
	{
		return false;
	}

  wchar_t szCH[256];
  for (int i=0; i<255; i++)
  {
    szCH[i] = i+1;
  }
  szCH[255] = 0;
  Prepare(szCH);

	return true;
}

///////////////////////////////////////////////
void CFFont::RenderCleanup()
{
	if (m_iTextureID > -1)
	{
		m_pISystem->GetIRenderer()->RemoveTexture(m_iTextureID);
	}

	m_pFontTexture.Release();
}

void CFFont::GetMemoryUsage (class ICrySizer* pSizer)
{
	if (!pSizer->Add (*this))
		return;

	// the following could be done more correct

#ifdef FONT_USE_32BIT_TEXTURE
	pSizer->AddObject(&m_pFontTexture, m_pFontTexture.GetWidth() * m_pFontTexture.GetHeight() * 4);
#else // FONT_USE_32BIT_TEXTURE
	pSizer->AddObject(&m_pFontTexture, m_pFontTexture.GetWidth() * m_pFontTexture.GetHeight() * 1);
#endif // FONT_USE_32BIT_TEXTURE

	// <<FIXME>> glyph cache bitmaps should also be added here

	pSizer->AddString (m_szName);
	pSizer->Add (&m_vEffects[0],m_vEffects.size());
	for (VecEffect::iterator it = m_vEffects.begin(); it != m_vEffects.end(); ++it)
	{
		pSizer->Add (it->strName.c_str(), it->strName.capacity()+1);
		pSizer->Add (&it->vPass[0], it->vPass.size());
	}
}

//------------------------------------------------------------------------------------------------- 
void CFFont::Prepare(const wchar_t *szString)
{
	static int n = 0;
	if (m_pFontTexture.PreCacheString(szString) == 1)
	{
		m_pISystem->GetIRenderer()->FontUpdateTexture(m_iTextureID, 0, 0, m_pFontTexture.GetWidth(), m_pFontTexture.GetHeight(), (unsigned char *)m_pFontTexture.GetBuffer());
	}
}

//------------------------------------------------------------------------------------------------- 
void CFFont::WrapText(wstring &szResult, float fMaxWidth, const wchar_t *szString)
{
	szResult = szString;

	if (!m_bRealPixels)
	{
		fMaxWidth = m_pISystem->GetIRenderer()->ScaleCoordX(fMaxWidth);
	}

	vector2f vStringSize = GetTextSizeW(szResult.c_str());

	if (vStringSize.x <= fMaxWidth)
	{
		return;
	}

	int		iLastSpace = -1;
	float	fLastSpaceWidth = 0.0f;

	float fCurrentCharWidth = 0.0f;
	float fCurrentLineWidth = 0.0f;
	float fBiggestLineWidth = 0.0f;
	float fWidthSum = 0.0f;

	int				iCurrentChar = 0;
	wchar_t		*pChar = (wchar_t *)szResult.c_str();
	wchar_t		szChar[2] = {0, 0};

	while(szChar[0] = *pChar++)
	{
		// ignore color codes
		if (szChar[0] == L'$')
		{
			if (*pChar)
			{
				++pChar;
				++iCurrentChar;

				if ((*pChar) != L'$')
				{
					++iCurrentChar;

					continue;
				}
				szChar[0] = *pChar;
			}
		}

		// get char width and sum it to the line width
		fCurrentCharWidth = GetTextSizeW(szChar).x;

		// keep track of spaces
		// they are good for spliting the string :D
		if (szChar[0] == L' ')
		{
			iLastSpace = iCurrentChar;
			fLastSpaceWidth = fCurrentLineWidth + fCurrentCharWidth;
		}

		// if line exceed allowed width, split it
		if ((fCurrentLineWidth + fCurrentCharWidth >= fMaxWidth) && (*pChar))
		{
			if ((iLastSpace > 0) && ((iCurrentChar - iLastSpace) < 16) && (iCurrentChar - iLastSpace > 0)) // 16 is the default treshold
			{
#if 0 // this inserts a newline, but the next line will start with a space...
				szResult.insert(iLastSpace + 1, 1, L'\n');

				++iCurrentChar;
#else // this replaces the space with a newline
				pChar = (wchar_t *)(szResult.c_str());
				pChar[iLastSpace] = L'\n';
#endif
				pChar = (wchar_t *)(szResult.c_str() + iCurrentChar + 1);

				if (fLastSpaceWidth > fBiggestLineWidth)
				{
					fBiggestLineWidth = fLastSpaceWidth;
				}

				fCurrentLineWidth = fCurrentLineWidth - fLastSpaceWidth + fCurrentCharWidth;
				fWidthSum += fCurrentLineWidth;
			}
			else
			{
#if 0 // this inserts a newline, but the next line will start with a space...
				szResult.insert(iCurrentChar, 1, L'\n');

				++iCurrentChar;
#else // this replaces the space with a newline
				pChar = (wchar_t *)(szResult.c_str());
				pChar[iCurrentChar] = L'\n';
#endif
				pChar = (wchar_t *)(szResult.c_str() + iCurrentChar + 1);

				if (fCurrentLineWidth > fBiggestLineWidth)
				{
					fBiggestLineWidth = fCurrentLineWidth;
				}

				fWidthSum += fCurrentLineWidth;
				fCurrentLineWidth = fCurrentCharWidth;
			}

			// if we don't need any more line breaks, then just stop
			if (vStringSize.x - fWidthSum <= fMaxWidth)
			{
				break;
			}

			fLastSpaceWidth = 0;
			iLastSpace = 0;
		}
		else
		{
			fCurrentLineWidth += fCurrentCharWidth;
		}

		++iCurrentChar;
	}
}


