#include "CBitmapFontManager.h"

#include "CSGD_TextureManager.h"

#include "tinyXML.h"

#include <assert.h>
#include <fstream>

CBitmapFontData::CBitmapFontData(void)
{
	SetFontImageID(-1);
	SetNumCols(0);
	SetCharWidth(0);
	SetCharHeight(0);
	SetStartChar(' ');
}

CBitmapFontData::~CBitmapFontData(void)
{

}

RECT CBitmapFontData::CalculateRect(int id)
{
	RECT rCell;
	rCell.left	= (id % GetNumCols()) * GetCharWidth();
	rCell.top	= (id / GetNumCols()) * GetCharHeight();

	rCell.right		= rCell.left + GetCharWidth();
	rCell.bottom	= rCell.top + GetCharHeight();

	return rCell;
}

void CBitmapFontData::LoadKerning(const char* szFilename)
{
	if(szFilename == NULL) return;

	m_kerning.clear();

	short nKerningAmount;
	std::ifstream fin(szFilename, std::ios_base::in | std::ios_base::binary);
	
	if(!fin.good()) return;

	while(true)
	{
		fin.read((char*)(&nKerningAmount),sizeof(nKerningAmount));

		if(fin.eof())
			break;

		m_kerning.push_back(nKerningAmount);
	}

	fin.close();
}

bool CBitmapFontData::Load(const char* szXmlFile)
{
	TiXmlDocument doc;

	if (!doc.LoadFile(szXmlFile))
		return false;

	m_strFileName = szXmlFile; // remember the file name

	TiXmlElement* pRoot = doc.RootElement();

	TiXmlElement* pImage = pRoot->FirstChildElement("image");

	if (pImage->GetText())
	{
		char buffer[MAX_PATH];
		strcpy_s(buffer, _countof(buffer), pImage->GetText());

		int r, g, b;
		r = g = b = -1;

		pImage->Attribute("r", &r);
		pImage->Attribute("g", &g);
		pImage->Attribute("b", &b);

		DWORD dwKeyColor;

		if (r == -1 && g == -1 && b == -1)
			dwKeyColor = 0;
		else
			dwKeyColor = D3DCOLOR_XRGB(r, g, b);

		CSGD_TextureManager* pTM = CSGD_TextureManager::GetInstance();

		SetFontImageID( pTM->LoadTexture(buffer, dwKeyColor) );
	}

	TiXmlElement* pCharacter = pRoot->FirstChildElement("character");
	
	if (pCharacter)
	{
		int width	= -1;
		int height	= -1;

		pCharacter->Attribute("width", &width);

		if (width > 0)
			SetCharWidth(width);

		pCharacter->Attribute("height", &height);

		if (height > 0)
			SetCharHeight(height);
	}

	TiXmlElement* pSheetInfo = pRoot->FirstChildElement("sheetinfo");
	
	if (pSheetInfo)
	{
		int		nNumCols	= -1;
		int		nStartChar	= 32; // ' ' = 32

		pSheetInfo->Attribute("numcols", &nNumCols);

		if (nNumCols > 0)
			SetNumCols( nNumCols );

		pSheetInfo->Attribute("startchar", &nStartChar);

		SetStartChar( (char)nStartChar );
	}

	doc.Clear();

	return true;
}

void CBitmapFontData::DrawString(const char* szText, int nPosX, int nPosY, float fScaleX, float fScaleY, DWORD dwColor)
{
	bool bIsKerning = true;
	if(m_kerning.empty()) bIsKerning = false;

	CSGD_TextureManager* pTM = CSGD_TextureManager::GetInstance();

	//	iterate through the string 1 character at a time
	int nLength = (int)strlen(szText);

	int nOffsetX = nPosX;
	int nOffsetY = nPosY;
	int nNewOffset = 0;

	for (int i=0; i < nLength; i++)
	{
		//	get ascii value of character
		char ch = szText[i];

		if(bIsKerning)
			nNewOffset = (int)((GetCharWidth() - m_kerning[ch]) * fScaleX);

		//	if it is space skip drawing it
		if (ch == ' ')
		{
			if(bIsKerning)
				nOffsetX += nNewOffset;
			else nOffsetX += (int)(GetCharWidth() * fScaleX);

			continue;
		}
		//	if it is newline, skip down one line
		else if (ch == '\n')
		{
			nOffsetX = nPosX; // reset x position

			nOffsetY += (int)(GetCharWidth() * fScaleY);

			continue;
		}

		//	make sure character is uppercase
		ch = toupper(ch);

		//	calculate the id on the bitmap using the start char
		int id = ch - GetStartChar();

		//	Make a rect based on an ID
		RECT rLetter = CalculateRect(id);

		//	Draw it to the screen
		pTM->DrawTexture(GetFontImageID(), (float)nOffsetX, (float)nOffsetY, 0.0f, fScaleX, fScaleY, &rLetter, 0.0f, 0.0f, 0.0f, dwColor);

		//	Move over one letter
		if(bIsKerning)
			nOffsetX += nNewOffset;
		else nOffsetX += (int)(GetCharWidth() * fScaleX);
	}
}

//////////////////////////////////////////////////////////////////////////////////
//	CBitmapFontManager
//////////////////////////////////////////////////////////////////////////////////

CBitmapFontManager::CBitmapFontManager(void)
{

}

CBitmapFontManager::~CBitmapFontManager(void)
{
	UnloadAllTemplates();
}

int CBitmapFontManager::CheckIfAlreadyExists(const char* szFileName)
{
	for (unsigned int i=0; i < m_vpData.size(); i++)
	{
		if (_stricmp(m_vpData[i]->GetFileName(), szFileName)==0)
			return i;
	}

	return -1;
}

CBitmapFontManager* CBitmapFontManager::GetInstance(void)
{
	static CBitmapFontManager instance;
	return &instance;
}

int CBitmapFontManager::LoadTemplate(const char* szFileName)
{
	int nAlreadyExistingID = CheckIfAlreadyExists(szFileName);

	if (nAlreadyExistingID > -1)
		return nAlreadyExistingID;

	CBitmapFontData* pData = new CBitmapFontData;
	pData->Load(szFileName);

	int nIndex = -1;

	// Insert it into an open slot that was previously used
	if (m_vnPrevUsedIDs.size() > 0)
	{
		nIndex = m_vnPrevUsedIDs[ m_vnPrevUsedIDs.size() - 1 ]; 
		m_vnPrevUsedIDs.pop_back();

		if ( m_vpData[ nIndex ] == NULL )
			m_vpData[ nIndex ] = pData;
	}
	else
	{
		m_vpData.push_back(pData);
		nIndex =  (int)m_vpData.size() - 1;
	}

	return nIndex;
}

void CBitmapFontManager::UnloadTemplate(int nID)
{
	assert(nID > -1 && nID < (int)m_vpData.size());

	delete m_vpData[nID];
	m_vpData[nID] = NULL;

	//m_vnPrevUsedIDs.insert(nID);
	m_vnPrevUsedIDs.push_back(nID); // for now
}

CBitmapFont CBitmapFontManager::CreateTemplateInstance(int nID)
{
	assert(nID > -1 && nID < (int)m_vpData.size());

	CBitmapFont font( m_vpData[nID] );

	return font;
}

void CBitmapFontManager::UnloadAllTemplates(void)
{
	for (unsigned int i=0; i < m_vpData.size(); i++)
		delete m_vpData[i];

	m_vpData.clear();
}