/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 
#include "font.h"

#define ITERATE(ttype,target,it) for(list <ttype>::iterator it = target.begin(); it != target.end(); it++)
#define ITERCONST(ttype,target,it) for(list <ttype>::const_iterator it = target.begin(); it != target.end(); it++)

bool verbose = false;

/*FONT::FONT()
{
	error_log.open((settings.GetSettingsDir() + "/logs/font.log").c_str());
	loaded = false;
}

extern bool verbose_output;
FONT::~FONT()
{
	if (verbose_output)
		cout << "font deinit" << endl;
	
	error_log.close();
	
	if (loaded)
	{
		glDeleteLists(base, 256);
		//glDeleteTextures( 1, &texture );
		//textures.Delete(texture);
		//textures.Delete(settings.GetDataDir() + "/" + settings.GetSkinPath() + "/textures/newfontt3.png");
		texture.Unload();
	}
}



void FONT::Load()
{
	if (loaded)
	{
		glDeleteLists(base, 256);
		//glDeleteTextures( 1, &texture );
		//textures.Delete(settings.GetDataDir() + "/" + settings.GetSkinPath() + "/textures/newfontt3.png");
		texture.Unload();
	}
	
	loaded = true;
	
	base = glGenLists(256);
	
	ifstream spf;
	spf.open(settings.GetFullDataPath("lists/fontspacing.1").c_str());
	int c;
	for (c = 0; c < 128; c++)
	{
		spf >> spacing[0][c];
	}
	spf.close();
	
	spf.open(settings.GetFullDataPath("lists/fontspacing.2").c_str());
	for (c = 0; c < 128; c++)
	{
		spf >> spacing[1][c];
	}
	spf.close();
	
	//texture = utility.TexLoad("gui/newfontt3.png", GL_RGBA, true);
	texture.Load( settings.GetDataDir() + "/" + settings.GetSkinPath() + "/textures/newfontt3.png", true );
	
	// *** Create Display Lists ***
	
	float cx;
	float cy;
	
	//glBindTexture(GL_TEXTURE_2D, texture);
	texture.Activate();
	
	// Loop Through All 256 Lists
	for ( int loop = 0; loop < 256; loop++ )
	{
		cx = 1 - ( float )( loop % 16 ) / 16.0f;
		cy = 1 - ( float )( loop / 16 ) / 16.0f;

		//compute size of character
		int maxx;
		int loopr = ( 255 - loop );
		if (loopr > 127)
		  maxx = spacing[1][loopr-128];
		else
		  maxx = spacing[0][loopr];
		float xscale = 1.0f/4.3f;
		//float space = ((float)maxx+15)*xscale;
		float space = ((float)maxx)*xscale;
		
		float texcspace = 0.0625f;
		//float offsetamountx = 0.01f;
		//float offsetamounty = 0.0025f;
		
		//float offsetamountx = -texcspace / 8.0f;
		//float offsetamounty = -texcspace / 8.0f;
		
		float offsetamountx = 0.002;
		float offsetamounty = 0.001;
		//float texcspace = 0.06f;
		//texcspace = (float)maxx/(float)FONT_RES;
		//cout << texcspace << " ";

		glNewList( base + ( 255 - loop ), GL_COMPILE );
		glBegin( GL_QUADS );
			glTexCoord2f( cx - texcspace-offsetamountx, cy -offsetamounty);
			glVertex2i( 0, 0 );
	
			glTexCoord2f( cx-offsetamountx, cy-offsetamounty);
			glVertex2i( 16, 0 );
	
			glTexCoord2f( cx-offsetamountx, cy - texcspace -offsetamounty);
			glVertex2i( 16, 16 );
	
			glTexCoord2f( cx - texcspace-offsetamountx, cy - texcspace -offsetamounty);
			glVertex2i( 0, 16 );
		glEnd( );

		glTranslated( space+1, 0, 0 );
		//cout << maxx*xscale << " ";
		glEndList( );
	}
}

void FONT::Print( float px, float py, const char *string, int set, int size, float r, float g, float b )
{
	Print(px, py, string, set, size, r, g, b, 1);
}

void FONT::Print( float px, float py, const char *string, int set, int size, float r, float g, float b, float trans )
{
	int sx, sy;
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	if ( set > 1 )
	set = 1;
	
	GetSizing(size, sx, sy);
	
	int x = (int) (px*(float)sx);
	int y = (int) ((1.0-py)*(float)sy)-20;
	
	// Select our texture
	//glBindTexture( GL_TEXTURE_2D, texture );
	texture.Activate();

	// Disable depth testing 
	glDisable( GL_DEPTH_TEST );
	//and lighting...
	glDisable( GL_LIGHTING);
	glBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR);
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable( GL_BLEND );
	
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

	// Select The Projection Matrix
	glMatrixMode( GL_PROJECTION );
	// Store The Projection Matrix
	glPushMatrix( );

	// Reset The Projection Matrix 
	glLoadIdentity( );
	// Set Up An Ortho Screen 
	glOrtho( 0, sx, 0, sy, -1, 1 );

	// Select The Modelview Matrix
	glMatrixMode( GL_MODELVIEW );
	// Stor the Modelview Matrix
	glPushMatrix( );
	// Reset The Modelview Matrix
	glLoadIdentity( );

	// Position The Text (0,0 - Bottom Left)
	glTranslated( x, y, 0 );

	int num_crs = 0;
	
	glColor4f(r,g,b,trans);
	
	for (unsigned int i = 0; i < strlen(string); i++)
	{
		if (string[i] == 32)
			glTranslated( 10, 0, 0 );
		else if (string[i] == '\n')
		{
			num_crs++;
			glLoadIdentity( );
			glTranslated(x,y-20*num_crs,0);
		}
		else
		{
			int pos = base - 32 + (128*set) + string[i];
			if (pos < 0)
				pos = 0;
			glCallList(pos);
		}
	}

	// Select The Projection Matrix
	glMatrixMode( GL_PROJECTION );
	// Restore The Old Projection Matrix
	glPopMatrix( );
	
	// Select the Modelview Matrix
	glMatrixMode( GL_MODELVIEW );
	// Restore the Old Projection Matrix
	glPopMatrix( );

	// Re-enable Depth Testing
	glEnable( GL_DEPTH_TEST );
	//and lighting...
	glEnable( GL_LIGHTING);
	glDisable(GL_BLEND);
	
	glPopAttrib();
}

void FONT::Print( float px, float py, const char *string, int set, int size, float opacity )
{
	Print( px, py, string, set, size, 1,1,1, opacity);
}

float FONT::Width(const char * str, int set, int size)
{
	int sx, sy;
	float max_chars = 0;
	float num_chars = 0;
	
	bool verbose = false;
	
	GetSizing(size, sx, sy);
	
	if (verbose)
		cout << str;
	
	for (unsigned int i = 0; i < strlen(str); i++)
	{
		if (str[i] == '\n')
		{
			if (num_chars > max_chars)
				max_chars = num_chars;
			num_chars = 0;
		}
		else if (str[i] == ' ')
		{
			//num_chars += 5.0;
			//num_chars += 10.0/4.3f;
			num_chars += 7.0;
			//num_chars += 10.0;
		}
		else
		{
			int pos = (128*set) + str[i] - 32;
			int maxx;
			//int loopr = ( 255 - pos );
			int loopr = pos;
			if (loopr > 127)
				maxx = spacing[1][loopr-128];
			else
				maxx = spacing[0][loopr];
			
			//int maxx = spacing[set][str[i]-32];
			
			float xscale = 1.0f/4.3f;
			float space = 0.0;
			if( strlen( str ) == 1 )
			{
				space = ((float)maxx)*xscale;
				num_chars += space + 1;
			}
			else
			{
				xscale = 1.0f/5.3f;
				space = ((float)maxx)*xscale;
				num_chars += space + 0.5;
			}
			//float space = ((float)maxx+15)*xscale;
			
		}
		
		if (verbose)
			cout << "," << num_chars;
	}
	
	if (num_chars > max_chars)
		max_chars = num_chars;
	
	if (verbose)
		cout << endl;

	return (max_chars)/(float) sy;
}

float FONT::Height(const char * str, int set, int size)
{
	int sx, sy;
	int num_crs = 1;
	
	GetSizing(size, sx, sy);
	
	for (unsigned int i = 0; i < strlen(str); i++)
	{
		if (str[i] == '\n')
		{
			num_crs++;
		}
	}
	
	return ((float) num_crs * 25.0)/(float) sy;
}

void FONT::GetSizing(int size, int & sx, int & sy)
{
	if (size < 0)
		size = 0;
	
	if (size > 11)
		size = 11;
	
	if (size == 0)
	{
		sx = 1600;
		sy = 1200;
	}
	else if (size == 1)
	{
		sx = 1440;
		sy = 1080;
	}
	else if (size == 2)
	{
		sx = 1280;
		sy = 960;
	}
	else if (size == 3)
	{
		sx = 1138;
		sy = 853;
	}
	else if (size == 4)
	{
		sx = 1024;
		sy = 768;
	}
	else if (size == 5)
	{
		sx = 931;
		sy = 698;
	}
	else if (size == 6)
	{
		sx = 640;
		sy = 480;
	}
	else if (size == 7)
	{
		sx = 480;
		sy = 360;
	}
	else if (size == 8)
	{
		sx = 320;
		sy = 240;
	}
	else if (size == 9)
	{
		sx = 213;
		sy = 160;
	}
	else if (size == 10)
	{
		sx = 142;
		sy = 107;
	}
	else if (size == 11)
	{
		sx = 60;
		sy = 45;
	}
	else
	{
		sx = 1138;
		sy = 853;
	}
}*/

const string FONTMANAGER::GetBackwardCompatibilityFontName(const int set) const
{
	string fontname = "default";
	if (set == 0)
		fontname = "digital";
	return fontname;
}

const float FONTMANAGER::GetBackwardCompatibilityScale(const int size) const
{
	return (((float) size - 7.0f) * 0.25f) + 1.0f;
}

void FONTMANAGER::Print( float px, float py, const char *str, int set, int size, float r, float g, float b, float trans )
{
	string tempstr = str;
	string fontname = GetBackwardCompatibilityFontName(set);
	float scale = GetBackwardCompatibilityScale(size);
	Print(fontname, px, py, tempstr, scale, r, g, b, trans);
}

const float FONTMANAGER::Width(const char * str, int set, int size)
{
	string tempstr = str;
	string fontname = GetBackwardCompatibilityFontName(set);
	float scale = GetBackwardCompatibilityScale(size);
	return PrintWidth(fontname, tempstr, scale);
}

float FONTMANAGER::Height(const char * str, int set, int size)
{
	string fontname = GetBackwardCompatibilityFontName(set);
	float scale = GetBackwardCompatibilityScale(size);
	return GetFontHeight(fontname, scale);
}

const float FONTMANAGER::GetFontHeight(const string & name, const float scale)
{
	FONT * f = GetFont(name);
	if (!f) return 0.0;
	//float retheight = f->fontsize * 0.0008 * scale;
	//cout << retheight << endl;
	//float retheight = (1.0/(float)f->totaly)*f->th*0.0008*scale*dest.GetRatio();
	//float retheight = (1.0/(float)f->totaly)*f->th*0.0008*scale;
	//cout << retheight << "*" << endl;
	
	//return retheight;
	
	return f->GetFontSize()*0.0008;
}

const bool FONT::Load(const string & newfontname)
{
	if (loaded)
		return true;
	
	fontname = newfontname;
	
	ifstream f;
	string infopath;
	//infopath = settings.GetFullDataPath("lists/fontinfo/"+newfontname+".txt");
	infopath = settings.GetDataDir() + "/" + settings.GetSkinPath() + "/fonts/"+newfontname+".txt";
	//cout << infopath << endl;
	f.open(infopath.c_str());
	if (!f)
	{
		cerr << __FILE__ << "," << __LINE__ << ": " << "Can't load font info file " + infopath + " for font " + newfontname << endl;
		//throw EXCEPTION(__FILE__, __LINE__, "Can't load font info file " + infopath + " for font " + newfontname);
		return false;
	}
	
	string tstr;
	//int tint;
	
	f >> tstr;
	
	if (tstr != "VDriftFontV1")
	{
		//throw EXCEPTION(__FILE__, __LINE__, "Can't load font info file " + infopath + " for font " + newfontname);
		cerr << __FILE__ << "," << __LINE__ << ": " << "Can't load font info file " + infopath + " for font " + newfontname << endl;
		return false;
	}
	
	//read header
	int pages;
	f >> pages;
	f >> totalx >> totaly;
	f >> tw >> th;
	f >> fontsize;
	
	float minminy, maxmaxy;
	minminy = 0;
	maxmaxy = 0;
	
	for (int i = 32; i < totalx*totaly; i++)
	{
		int c;
		f >> c;
		charinfo[i-32].character = i;
		f >> charinfo[i-32].miny;
		f >> charinfo[i-32].maxy;
		f >> charinfo[i-32].minx;
		f >> charinfo[i-32].maxx;
		
		if (charinfo[i-32].miny < minminy)
			minminy = charinfo[i-32].miny;
		
		if (charinfo[i-32].maxy > maxmaxy)
			maxmaxy = charinfo[i-32].maxy;
	}
	
	averagecenter = (maxmaxy + minminy)/2.0;
	
	//TODO:  change this to "data" ... etc
	//fonttexture.Load(game.pathmanager.GetPath("data","font","default","texture","font"+newfontname+".png"));
	//fonttexture.Load("font/"+newfontname+".png"));
	//tinfo.SetName(game.pathmanager.GetTexturePathFromName("font/"+newfontname+".png"));
	//fonttexture.Load(tinfo);
	fonttexture.Load(settings.GetDataDir() + "/" + settings.GetSkinPath() + "/fonts/"+newfontname+".png", true);
	
	loaded = true;
	
	return true;
}

const bool FONTMANAGER::LoadFont(const string & newfontname)
{
	ITERATE(FONT, fontlist, i)
	{
		if (i->GetFontName() == newfontname)
			return true;
	}
	
	{
		//FONT * newfontptr = fontlist.DB_Add();
		FONT newfontref;
		fontlist.push_back(newfontref);
	}
	FONT & newfontref = fontlist.back();
	return newfontref.Load(newfontname);
	//newfontptr->Load(newfontname);
}

const float FONT::Print(const float px, const float py, const string & str, const float scale, const float r, const float g, const float b, const float a, const bool draw)
{
	float textwidth = 0.0;
	
	if (draw)
	{
		fonttexture.Activate();
	}
	
	for (unsigned int i = 0; i < str.length(); i++)
	{
		//cout << px+textwidth << "," << py << endl;
		textwidth += DrawChar(str[i], scale, draw, px+textwidth, py);
		//cout << textwidth << endl;
	}
	
	return textwidth;
}

//float FONTMANAGER::Print(string & name, float px, float py, string & str, float scale, float r, float g, float b, float a)
const float FONTMANAGER::Print(const string & name, const float px, const float py, const string & str, const float scale, const float r, const float g, const float b, const float a)
{
	//int w = game.graphics.GetW();
	//int h = game.graphics.GetH();
	//int w = SCREEN_WIDTH;
	//int h = SCREEN_HEIGHT;
	/*RENDERRECT defaultrect;
	defaultrect.Set2D(0, 0, w, h, 0);*/
	
	//setup flags
	{
		//float x = (px*(float)w);
		//float y = ((1.0-py)*(float)h);//-48*scale;
		
		{
			//comment the following back in
			/*game.graphics.DepthTest(false);
			game.graphics.Lighting(false);
			game.graphics.Blend(true);
			game.graphics.BlendOnAlpha();
			game.graphics.TextureClamp();
			game.graphics.SetupOrthoTarget(w, h);
			game.graphics.SetColor(r,g,b,a);*/
		}
	}
	
	float retval = 0;
	//game.graphics.BeginFont(r,g,b,a);
	{
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_LIGHTING);
		glEnable(GL_BLEND);
		glEnable(GL_TEXTURE_2D);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		//glOrtho( 0, w, 0, h, -1, 1 );
		glOrtho( 0, 1, 0, 1, -1, 1 );
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		
		glColor4f(r,g,b,a);
	}
	
	//float retval = Print(name, px, py, str, scale, r, g, b, a, defaultrect);
	FONT * f = GetFont(name);
	if (!f) return 0.0;
	//retval = f->Print(px, (1.0-py), str, scale, r, g, b, a, true);
	retval = f->Print(px, (1.0-py)-GetFontHeight(name,scale), str, scale, r, g, b, a, true);
	
	//game.graphics.EndFont();
	{
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
		
		glColor4f(1,1,1,1);
		
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_LIGHTING);
		glDisable(GL_BLEND);
	}
	
	//clear flags
	{
		//comment the following back in
		/*
		game.graphics.ClearOrthoTarget();
		
		game.graphics.DepthTest(true);
		game.graphics.Lighting(true);
		game.graphics.Blend(false);*/
	}
	
	return retval;
}

const float FONTMANAGER::PrintWidth(const string & name, const string & str, const float scale)
{
	/*RENDERRECT nullrect;
	nullrect.Set2D(0,0,1,1,0);*/
	FONT * f = GetFont(name);
	if (!f) return 0.0;
	return f->Print(0, 0, str, scale, 1,1,1,1, false);
}


extern int SCREEN_WIDTH;
extern int SCREEN_HEIGHT;

//float FONT::DrawChar(char curchar, float scale, bool draw, float & px, float & py, RENDERRECT & dest)
const float FONT::DrawChar(const char curchar, const float scale, const bool draw, const float px, const float py)
{
	float charwidth = 0;
	
	int c = curchar;
	c -= 32;
	if (c < 0 || c > 96)
	{
		//throw EXCEPTION(__FILE__, __LINE__, "Character out of range");
		if (verbose)
			cerr << __FILE__ << "," << __LINE__ << ": " << "Character out of range: " << (int) c << endl;
		return 0.0;
	}
	int x = c % totalx;
	int y = c / totalx;
	
	float u1 = (float) x / (float) totalx;
	float v1 = (float) y / (float) totaly;
	
	float u2 = (float) (x+1) / (float) totalx;
	float v2 = (float) (y+1) / (float) totaly;
	
	float w = (1.0/(float)totalx)*tw; //w is the texture size in pixels
	float h = (1.0/(float)totaly)*th; //h is the texture size in pixels
	
	float realscale = 0.0008*scale; //scale things down to the 0.0 to 1.0 range
	//float realscale = 0.8*scale;
	//float hwratio = (float) game.graphics.GetH()/game.graphics.GetW();
	float hwratio = (float) SCREEN_WIDTH/SCREEN_HEIGHT;
	//cout << "*******" << hwratio << endl;
	
	if (c == 0)
	{
		//if (draw)
			//game.graphics.Translate(8.0*scale,0,0);
		//return 8.0*scale;
		
		//px += 8.0*realscale;
		return 8.0*realscale;
	}
	
	float offsetx = (charinfo[c].maxx+charinfo[c].minx)/2-w/2;
	float offsety = (charinfo[c].maxy+charinfo[c].miny)/2-h/2;
	
	float space = charinfo[c].maxx + 2.0;
	
	float x1, x2, y1, y2;
	x1 = px + offsetx*realscale;
	y1 = py + offsety*realscale*hwratio;
	x2 = x1+realscale*w;
	y2 = y1+realscale*h*hwratio;
	
	float vcorners[4][3];
	for (int i = 0; i < 4; i++)
		vcorners[i][2] = 0;
	vcorners[0][0] = x1; vcorners[0][1] = y1;
	vcorners[1][0] = x2; vcorners[1][1] = y1;
	vcorners[2][0] = x2; vcorners[2][1] = y2;
	vcorners[3][0] = x1; vcorners[3][1] = y2;
	
	/*for (int i = 0; i < 4; i++)
	{
		vcorners[i][0] = vcorners[i][0]*game.graphics.GetW();
		vcorners[i][1] = vcorners[i][1]*game.graphics.GetH();
	}*/
	
	float uvs[4][3];
	for (int i = 0; i < 4; i++)
		uvs[i][2] = 0;
	uvs[0][0] = u1; uvs[0][1] = v1;
	uvs[1][0] = u2; uvs[1][1] = v1;
	uvs[2][0] = u2; uvs[2][1] = v2;
	uvs[3][0] = u1; uvs[3][1] = v2;
	
	/*for (int i = 0; i < 4; i++)
	{
		cout << i << ". " << vcorners[i][0] << "," << vcorners[i][1] << " -uv- " << uvs[i][0] << "," << uvs[i][1] << endl;
	}*/
	
	if (draw)
	{
		//game.graphics.Translate( offsetx*scale, offsety*scale, 0 );
		
		//game.graphics.DrawBox(0, 0, scale*w, scale*h, u1, v1, u2, v2);
		
		//game.graphics.DrawBox(vcorners, uvs);
		glBegin( GL_QUADS );
		for (int i = 0; i < 4; i++)
		{
			glTexCoord2f(uvs[i][0], uvs[(i+2)%4][1]);
			glVertex3fv(vcorners[i]);
		}
		glEnd( );
		
		//game.graphics.Translate( (space-offsetx)*scale, -offsety*scale, 0 );
	}
	
	//charwidth += offsetx*realscale+(space-offsetx)*realscale;
	charwidth += space*realscale;
	
	//px += space*realscale;
	
	return charwidth;
}

const float FONTMANAGER::GetAverageVerticalPosition(const string & name)
{
	FONT * f = GetFont(name);
	if (!f) return 0.0;
	return f->GetAverageCenter();
}

FONT * const FONTMANAGER::GetFont(const string & name)
{
	//for (GAMEDB::NODELIST_ITERATOR <FONT> i (fontlist); i.NotEnd(); i.Inc())
	ITERATE(FONT, fontlist, i)
	{
		if (i->GetFontName() == name)
		{
			return &(*i);
		}
	}
	
	//throw EXCEPTION(__FILE__, __LINE__, "Font not found: "+name);
	if (verbose)
		cerr << __FILE__ << "," << __LINE__ << ": " << "Font not found: "+name << endl;
	
	return NULL;
}

const int FONTMANAGER::GetFontSize(const string & name)
{
	return GetFont(name)->GetFontSize();
}

void FONTMANAGER::Load()
{
	ClearFonts();
	
	//cout << "***Loading fonts..." << endl;
	if (!LoadFont("default"))
	{
		cerr << __FILE__ << "," << __LINE__ << ": " << "Error loading font" << endl;
	}
	
	if (!LoadFont("digital"))
	{
		cerr << __FILE__ << "," << __LINE__ << ": " << "Error loading font" << endl;
	}
	
	/*if (!LoadFont("default"))
	{
		cerr << __FILE__ << "," << __LINE__ << ": " << "Error loading font" << endl;
	}*/
}
