/*********************************************************************//**
*	Fonts library.
*	Databaze fontu a definice objektu DString, coz predstavuje
*	retezec, ktery lze rovnou pomoci OpenGL vykreslit se zadanym fontem.
*
*	author: Michal Jirous
*	date: 14.7.2008
*	file: fonts.cpp
**********************************************************************/

#include "fonts.h"

#include "lexan.h"
#include <list>
#include "parsing.h"
using namespace parsing;
using namespace std;
list<Font*> g_fontList;
#include "sys_console.h"
#include "sys_config.h"
using namespace systemConsole;

void fontLibrary::loadFonts( string filename )
{
	console << "Loading fonts" << endline;
	IFSTREAM_OPEN( fin, filename, ios::in );

	if( !fin.is_open() )
		return;
	string data;
	
	while(  !fin.eof() )
	{
		data += fin.get();
	
	}
	CLexan lexan;
	lexan.loadString( data);

	lexan.setSkippingEmptyCharacters( true );
	lexan.setDefaultEmptyCharactersSettings(true);

	for(int i = 32; i < 128; i++)
			lexan.setAllowedCharacter(i);

	lexan.createDefaultNode();
	lexan.setDefaultReturnValue( FONT_IDENT );

	for( int i = 0; i < LEXAN_FIELD_SIZE; i++ )
	{
		if(i > 31 && i < 128)
		lexan.setCycleForChar( i );	//vsechny znaky (tj. klavesy mohou byt ident
	}

	lexan.setEndCharacters(",={}");
	lexan.init();

	lexan.addSubject("{", FONT_LEFT_VINCULUM, true );
	lexan.addSubject("}", FONT_RIGHT_VINCULUM, true );
	lexan.addSubject("=", FONT_EQUAL, true );
	lexan.addSubject(",", FONT_COMMA, true );
	map<string,string> parametersMap;
	string sFontName;

	while( lexan.show() != END_OF_FILE )
	{
		parametersMap.clear();
		if( lexan.show() == FONT_IDENT )
		{
			lexan.compare( lexan.show() );
			
			sFontName = lexan.getSymbolName();
			
			if(!lexan.compare( FONT_LEFT_VINCULUM ) )
				return;

			while( lexan.show() != END_OF_FILE && lexan.show() != FONT_RIGHT_VINCULUM )
			{
				if( lexan.show() != FONT_IDENT)
					return;
				string ident = lexan.getSymbolName();
				lexan.compare( lexan.show() );
				
				if( !lexan.compare( FONT_EQUAL ) )
					return;
				
				if( lexan.show() != FONT_IDENT)
					return;
				string value = lexan.getSymbolName();
				lexan.compare( lexan.show() );

				parametersMap.insert( make_pair( ident, value ) );

				if( lexan.show() == FONT_COMMA)
					lexan.compare( lexan.show() );
				else
					break;			
			}

			map<string, string>::iterator mapIter = parametersMap.find("type");
			if( mapIter != parametersMap.end() )
			{
				console << "\tAdding font '" << sFontName  << "' to database." << endline;

				if( mapIter->second == "texture" )
				{
					TextureFont *newFont = new TextureFont();
					if( newFont )
					{
						newFont->m_sName = sFontName;
						newFont->setParameters( parametersMap );
						if( newFont->compile() )
							g_fontList.push_back( newFont );
					}
				}
				else if( mapIter->second == "ttf" )
				{
					FreeTypeFont *newFont = NULL;
					try
					{
						newFont = new FreeTypeFont();
						if( newFont )
						{
							newFont->m_sName = sFontName;
							newFont->setParameters( parametersMap );
							if( newFont->compile() )
								g_fontList.push_back( newFont );
						}
					}
					catch( std::runtime_error e )
					{
					
					}
					
				
				}
			}
			if( !lexan.compare( FONT_RIGHT_VINCULUM ) )
					return;
		}
		else
			return;
	
	}
	console << "Fonts loading finnished" << endline;
}


void fontLibrary::destructor()
{
	for( list<Font*>::iterator i = g_fontList.begin(); i != g_fontList.end(); ++i )
	{
		(*i)->decompile();
		delete *i;
	}
}

Font * fontLibrary::getFont( string name )
{
	for( list<Font*>::iterator i = g_fontList.begin(); i != g_fontList.end(); ++i )
		if( (*i)->m_sName == name )
			return (*i);
	return NULL;
}


//Vykreslovani
void fontLibrary::drawText( string &text, float x_pos, float y_pos, int ALIGN, string name )
{
	fontLibrary::drawText( text, x_pos, y_pos, ALIGN, fontLibrary::getFont( name ) );
}

void fontLibrary::drawText( string &text, float x_pos, float y_pos, int ALIGN, Font *font )
{
	if( font == NULL )
		return;

	DString dStr( text, font, ALIGN );
	drawText( dStr, x_pos, y_pos );
}

void fontLibrary::drawText( DString &ds, float x_pos, float y_pos )
{
	ds.draw( (int)x_pos, (int)y_pos );
}

void fontLibrary::DString::draw( int x_pos, int y_pos )
{
	int x = static_cast<int>(x_pos);
	int y = static_cast<int>(y_pos);
	if( m_pLineList )
	{
		glPushMatrix();
		glTranslatef( (GLfloat)x, (GLfloat)y, 0 );
		
		for( list<DString::TextLine>::iterator iter = m_pLineList->begin(); iter != m_pLineList->end(); iter++ )
			glCallList( (*iter).m_uiDListIndex );
		
		glPopMatrix();
	}
}

void fontLibrary::DString::draw( float x_pos, float y_pos )
{
	draw( (int)x_pos, (int)y_pos );
}

int fontLibrary::simpleDrawText( string &line, float x_pos, float y_pos, Font *font )
{
	//int x = static_cast<int>(x_pos);
	//int y = static_cast<int>(y_pos);
	if( font  )
		return font->draw( line, x_pos, y_pos );
	return 0;
}

int fontLibrary::simpleDrawText( string &line, float x_pos, float y_pos, string fontName )
{
	return simpleDrawText( line, x_pos, y_pos, fontLibrary::getFont( fontName ) );
}

int fontLibrary::simpleDrawTextFloated( string &line, float x_pos, float y_pos, Font *font )
{
	if( font  )
		return font->draw( line, x_pos, y_pos );
	return 0;
}



void fontLibrary::DString::add( string text, int iMaxWidth )
{
	if( m_pLineList )
		create( text, m_pFont, m_iAlign, iMaxWidth, true );
}


void fontLibrary::DString::add( const char c, int iMaxWidth )
{
	if( m_pLineList )
		create( string() + c, m_pFont, m_iAlign, iMaxWidth, true );
}


float fontLibrary::DString::TextLine::compile( int ALIGN, Font *font )
{
	if( font == NULL )
		return 0;

	switch( ALIGN )
	{
		case FONT_ALIGN_CENTER:
			start_x = -width / 2;
			break;
		case FONT_ALIGN_RIGHT:
			start_x = -width;
			break;
		case FONT_ALIGN_LEFT:
		default:
			start_x = 0;
	}
	if( m_uiDListIndex != 0 )
		glDeleteLists( m_uiDListIndex, 1 );
	m_uiDListIndex = glGenLists( 1 );

	glNewList( m_uiDListIndex, GL_COMPILE );
		glTranslatef( 0, -( (float)(font->m_iLetterHeight/2) ),0);
		font->draw( m_sText, (float)start_x, 0 );
		glTranslatef( 0, -(float)(font->m_iLetterHeight)*LINE_HEIGHT_MULTIPLIKATOR, 0 );
	glEndList();
	return (float)(font->m_iLetterHeight) * LINE_HEIGHT_MULTIPLIKATOR + (float)(font->m_iLetterHeight/2);
}



//Kopie
fontLibrary::DString &fontLibrary::DString::operator=( const fontLibrary::DString & str )
{
	if( this == &str )
		return *this;

	if( m_pLineList && m_iNumReferences)
	{
		(*m_iNumReferences)--;
		if( !(*m_iNumReferences) )
			destroy();
	}
	m_pLineList = str.m_pLineList;
	m_iAlign = str.m_iAlign;
	m_pFont = str.m_pFont;
	m_iHeight = str.m_iHeight;
	(*str.m_iNumReferences)++;
	m_iNumReferences = str.m_iNumReferences;
	return *this;
}

void fontLibrary::DString::destroy()
{
	for( list<fontLibrary::DString::TextLine>::iterator iter = m_pLineList->begin(); iter != m_pLineList->end(); iter++ )
	{
		glDeleteLists( (*iter).m_uiDListIndex, 1 );
	}
	delete m_pLineList;
	m_pLineList = NULL;
	delete m_iNumReferences;
	m_iNumReferences = NULL;
}

//Porovnani
bool fontLibrary::DString::operator ==( fontLibrary::DString & str )
{
	list<fontLibrary::DString::TextLine>::iterator iter2 = str.m_pLineList->begin();
	for( list<fontLibrary::DString::TextLine>::iterator iter = m_pLineList->begin(); iter != m_pLineList->end(); iter++ )
	{
		if( iter2 == str.m_pLineList->end() )
			return false;

		if( (*iter).m_sText != (*iter2).m_sText )
			return false;
		iter2++;
	}

	return true;
}


void fontLibrary::DString::TextLine::reset()
{
	width = 0;
	start_x = 0;
	m_sText.clear();
	m_uiDListIndex = 0;
}



//Vytvareni
fontLibrary::DString &fontLibrary::DString::operator=( const std::string text )
{	
	create( text, "REGULAR", FONT_ALIGN_LEFT );
	return *this;
}

void fontLibrary::DString::create( std::string text, string fontName, int ALIGN, int iMaxWidth, bool addonly )
{
	create( text, fontLibrary::getFont( fontName ), ALIGN, iMaxWidth, addonly  );
}

void fontLibrary::DString::deepCopy()
{
	if( (*m_iNumReferences) > 1 )
	{
		(*m_iNumReferences)--;
		m_iNumReferences = new int();
		(*m_iNumReferences) = 1;
		list<TextLine> *tmp = m_pLineList;
		m_pLineList = new list<TextLine>();
		*m_pLineList = *tmp;
		for( list<fontLibrary::DString::TextLine>::iterator iter = m_pLineList->begin(); iter != m_pLineList->end(); iter++ )
		{
			(*iter).m_uiDListIndex = glGenLists( 1 );
			(*iter).compile( m_iAlign, m_pFont );
		}
	}
}

void fontLibrary::DString::resize(int iNewMaxWidth)
{
	if( !m_pLineList )
		return;
	list<TextLine> *tmpList = m_pLineList;
	m_pLineList = new list<TextLine>();
	bool bDeleteOldList = true;

	if( (*m_iNumReferences) > 1 )
	{
		(*m_iNumReferences)--;
		m_iNumReferences = new int();
		(*m_iNumReferences) = 1;
		bDeleteOldList = false;
		
	}

	m_iHeight = 0;

	for( list<TextLine>::iterator iter = tmpList->begin(); iter != tmpList->end(); iter++ )
	{
		add( (*iter).m_sText, iNewMaxWidth );
	}

	
	if( bDeleteOldList )
	{
		tmpList->clear();
		delete tmpList;
	}

	

}



void fontLibrary::DString::create( std::string text, Font *font, int ALIGN, int iMaxWidth, bool addonly )
{
	if( font == NULL || !m_pLineList )
		return;

	deepCopy();

	m_iAlign = ALIGN;

	bool bNoLimit = false;
	if( iMaxWidth < 0 )
			bNoLimit = true;
	string word;

	int iWordWidth = 0;


	TextLine thisLine;
	if( addonly && !empty() && m_pFont )
	{
		thisLine = m_pLineList->back();
		m_pLineList->pop_back();
		m_iHeight -= (float)(m_pFont->getLineWidth());
	}
	else
	{
		m_pLineList->clear();
		m_iHeight = 0;
		m_pFont = font;
	}

	if( !m_pFont )
		return;
	
	bool end = false;
	for( size_t i = 0; i <= text.size(); i++ )
	{
		unsigned char c = 0;
		int iCharSize = 0;
		if( i == text.size()  )
		{
			c = ' ';
			end = true;
		}
		else if( i < text.size() )
		{
			c = (unsigned char)text.at( i );
			iCharSize = font->getCharacterWidth( c );
			word += c;
			iWordWidth += iCharSize;
		}
		
		if( isSpace( c )  )
		{
			if( ( thisLine.width + iWordWidth > iMaxWidth ) && !bNoLimit  )
			{
				if( iWordWidth > iMaxWidth )
				{
					if( !thisLine.m_sText.empty() )
					{
						m_iHeight += thisLine.compile( ALIGN, m_pFont );
						m_pLineList->push_back( thisLine );

						thisLine.reset();
					}
					
					thisLine.m_sText = word;
					thisLine.width = iWordWidth;
					m_iHeight += thisLine.compile( ALIGN, m_pFont );
					m_pLineList->push_back( thisLine );
					
					thisLine.reset();

					iWordWidth = 0;
					word.clear();
					
					continue;
				}
				else
				{
					m_iHeight += thisLine.compile( ALIGN, m_pFont );
					m_pLineList->push_back( thisLine );

					thisLine.reset();
					
					thisLine.m_sText = word;
					thisLine.width = iWordWidth;
					iWordWidth = 0;
					word.clear();

				}
			
			}
			else
			{
				thisLine.m_sText += word;
				thisLine.width += iWordWidth;
				iWordWidth = 0;
				word.clear();
			}

			if( ( iCharSize + thisLine.width > iMaxWidth ) && !bNoLimit)
			{
				m_iHeight += thisLine.compile( ALIGN, m_pFont );
				m_pLineList->push_back( thisLine );

				thisLine.reset();
			}


			if( c == '\n' || end)
			{	
				m_iHeight += thisLine.compile( ALIGN, m_pFont );
				m_pLineList->push_back( thisLine );

				thisLine.reset();
				iWordWidth = 0;
				word.clear();

				continue;
			}

			continue;
		}

	}

}



void fontLibrary::DString::init()
{ 
	m_iNumReferences = new int();	
	(*m_iNumReferences) = 1; 
	m_pLineList = new list<TextLine>();
	m_iAlign = 0;
	m_iHeight = 0;
	m_pFont = 0;

}


/* Konstruktory */
fontLibrary::DString::DString(std::string text, string fontName, int ALIGN, int iMaxWidth )
{
	init();
	create( text, fontLibrary::getFont( fontName ), ALIGN, iMaxWidth );
}

fontLibrary::DString::DString(std::string text, Font *font, int ALIGN, int iMaxWidth )
{
	init();
	create( text, font, ALIGN, iMaxWidth );
}

fontLibrary::DString::DString( std::string text )
{
	init();
	create( text, "REGULAR", FONT_ALIGN_LEFT );
}
