//text.cpp
//texture-based OpenGL font drawing code

#include <OpenGL/gl.h>
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include "fileio.h"
#include "gamemem.h"
#include "text.h"
#include "config.h"
#include "parser.h"
#include "textures.h"
#include "renderframe.h"
#include "gameframe.h"
#include "environment.h"
#include "screen.h"
#include "gameinitexit.h"


#define kMessageBufferSize 128

#define kMessageSize 512

//one element of the Message Buffer, which will be drawn to the screen
//when TextPrintBuffer is called
typedef struct{
	char text[kMessageSize];//the actual string
	float r,g,b,a;			//color to draw text in
	tVector3 pos;			//position on screen
	float size;				//size of text
	int startFadeIn;
	int startFade,endFade;	//
	int align;				//alignment of text
	float tilt;
} tMessage;

tMessage gMessageBuffer[kMessageBufferSize];
int gMessagePos=0;
tFontInfo *gTextFont;
float gTextSize=0.03;
float gTextLineSpacing=0;
tVector2 gTextBufferScale=Vector(1,1);
float gTextOpacity=1;

//loads the font described by the file referenced to by fontRef
void TextLoadFont(int fontRef)
{
	gTextFont=(tFontInfo*)FileGetParsedDataPtr(fontRef,kParserTypeFontInfoDesc,sizeof(tFontInfo));
	static int s=false;
	if(!s){
	for(int i=0;i<gTextFont->numChars;i++)
	{
		gTextFont->chars[i].x1+=0.003;
		gTextFont->chars[i].x2-=0.003;
	}s=true;}
}


void TextDrawSimple(char *text,float maxWidth,float size,int align)
{
	TexturesSelectTex(gTextFont->fontTexture);
	//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	
	glPushAttrib(GL_COLOR_BUFFER_BIT);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);	
	
	int l=0;
	float xPos=0;
	float yPos=0;
	char *printPos=text;
	float totalWidth=0;
	while(*printPos!='\0')
	{
		if(*printPos=='\255')
		{
			do{
				printPos++;
				l++;
			}while(*printPos!='\255'&&*printPos!='\0');
			if(*printPos=='\0')
			{
				printPos--;
			}
			else
				l++;
		}
		if(*printPos>=gTextFont->startChar&&*printPos<gTextFont->startChar+gTextFont->numChars)
		{
			char ch=*printPos;
			if(ch>='a'&&ch<='z')ch+='A'-'a';
	
			tFontCharInfo *cInfo=gTextFont->chars+(ch-gTextFont->startChar);
			if(cInfo->y2-cInfo->y1>0)
				if(totalWidth+(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size<maxWidth)
				{
					totalWidth+=(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size;
					l++;
				}
		}
		printPos++;
	}
	
	printPos=text;
	
	if(align==kTextAlignRight)
		xPos-=totalWidth;
	else if(align==kTextAlignMiddle)
		xPos-=totalWidth/2;
			
	int i=0;	
	while(printPos<text+l)
	{
		if(*printPos=='\255')
		{
			do{
				printPos++;
			}while(*printPos!='\255'&&printPos<text+l);
		}
		if(*printPos>=gTextFont->startChar&&*printPos<gTextFont->startChar+gTextFont->numChars)	
		{
			char ch=*printPos;
			if(ch>='a'&&ch<='z')ch+='A'-'a';

			tFontCharInfo *cInfo=gTextFont->chars+(ch-gTextFont->startChar);
			if(cInfo->y2-cInfo->y1>0)
			{
				float width=(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size;

				glBegin(GL_TRIANGLE_STRIP);								
				glTexCoord2d(cInfo->x2,cInfo->y2); glVertex2f(xPos+width,yPos-size);		
				glTexCoord2d(cInfo->x2,cInfo->y1); glVertex2f(xPos+width,yPos); 	
				glTexCoord2d(cInfo->x1,cInfo->y2); glVertex2f(xPos,yPos-size); 	
				glTexCoord2d(cInfo->x1,cInfo->y1); glVertex2f(xPos,yPos); 	
				glEnd();			

				#ifdef __POLYCOUNT
				gPolyCount+=2;
				#endif
				xPos+=width;
			}
		}
		printPos++;
	}

	glPopAttrib();
}

float TextWidth(char *str,float size)
{
	float width=0;
	while(*str!='\0')
	{
		if(*str>=gTextFont->startChar&&*str<gTextFont->startChar+gTextFont->numChars)
		{
			tFontCharInfo *cInfo=gTextFont->chars+(*str-gTextFont->startChar);
			if(cInfo->y2-cInfo->y1>0)
				width+=(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size;
		}
		if(*str=='\255')
		{
			str++;
			char iconFile[256];
			int chi=0;
			while(*str!='\255'&&*str!='\0')
				iconFile[chi++]=*(str++);
			iconFile[chi]='\0';
			if(iconFile[0]!='#')
				width+=size;
		}
		str++;
	}
	return width;
}
//#include "error.h"
#define kTabSpaces 10
//draws all the messages in the text buffer to the screen
void TextPrintBuffer(float opacity,int useInstrumentColor)
{
	gTexturesQualityModifier=-255;
	int tmpFileError=gFileErrorReporting;
	gFileErrorReporting=false;
	glLoadIdentity();
	glTranslatef(0.0f,0.0f,0.0f);
	glScalef(gTextBufferScale.x,gTextBufferScale.y,1);
	
	TexturesSelectTex(gTextFont->fontTexture);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
	
	glPushAttrib(GL_LIGHTING_BIT+GL_DEPTH_BUFFER_BIT+GL_COLOR_BUFFER_BIT+GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);	
	glDisable(GL_LIGHTING);
	glDisable(GL_DEPTH_TEST);
	glDepthMask(GL_FALSE);
	
	for(int i=gMessagePos-1;i>=0;i--)
		if(gMessageBuffer[i].a>0)
		{
			float xPos=gMessageBuffer[i].pos.x;
			float yPos=gMessageBuffer[i].pos.y;
			float zPos=gMessageBuffer[i].pos.z;
			if(fabs(zPos)>10)
				glEnable(GL_DEPTH_TEST);
			else
				glDisable(GL_DEPTH_TEST);
			if(zPos!=-1.0)
				SetupAspect(1.0+~gCameraEntity->velo*0.0015*((gGameInfo->arcade!=3&&gGameInfo->arcade!=0)?(gGameInfo->arcade+1):0));
			else
				SetupAspect(kNormalFOVY);

			float size=gMessageBuffer[i].size;
			char *printPos=gMessageBuffer[i].text;
			float totalWidth=TextWidth(printPos,size);
			
			printPos=gMessageBuffer[i].text;
			if((gMessageBuffer[i].align&kTextAlignMask)==kTextAlignRight)
				xPos-=totalWidth;
			else if((gMessageBuffer[i].align&kTextAlignMask)==kTextAlignMiddle)
				xPos-=totalWidth/2;
						
			float fade=1;
			if(gMessageBuffer[i].startFade!=-1)
			{
				if(gFrameCount>gMessageBuffer[i].startFade)
					fade=1-(gFrameCount-gMessageBuffer[i].startFade)/(float)(gMessageBuffer[i].endFade-gMessageBuffer[i].startFade);
				else if(gFrameCount<gMessageBuffer[i].startFadeIn+0.2*kFPS)
					fade=(gFrameCount-gMessageBuffer[i].startFadeIn)/(float)(0.2*kFPS);				
			}
			
			if(useInstrumentColor)
				glColor4f(gEnvironment->instrumentColor.x*gMessageBuffer[i].r
						,gEnvironment->instrumentColor.y*gMessageBuffer[i].g
						,gEnvironment->instrumentColor.z*gMessageBuffer[i].b
						,fade*gMessageBuffer[i].a*opacity);
			else
				glColor4f(gMessageBuffer[i].r
						,gMessageBuffer[i].g
						,gMessageBuffer[i].b
						,fade*gMessageBuffer[i].a*opacity);
		//	PrintConsoleString(gMessageBuffer[i].text);					
			while(*printPos!='\0')
			{
				if(*printPos>=gTextFont->startChar&&*printPos<gTextFont->startChar+gTextFont->numChars)	
				{
					tFontCharInfo *cInfo=gTextFont->chars+(*printPos-gTextFont->startChar);
					if(cInfo->y2-cInfo->y1>0)
					{
						float width=(cInfo->x2-cInfo->x1)/(cInfo->y2-cInfo->y1)*size;

						glBegin(GL_TRIANGLE_STRIP);								
						glTexCoord2d(cInfo->x2,cInfo->y2); glVertex3f(xPos+width,yPos-size,zPos);		
						glTexCoord2d(cInfo->x2,cInfo->y1); glVertex3f(xPos+size*gMessageBuffer[i].tilt+width,yPos,zPos); 	
						glTexCoord2d(cInfo->x1,cInfo->y2); glVertex3f(xPos,yPos-size,zPos); 	
						glTexCoord2d(cInfo->x1,cInfo->y1); glVertex3f(xPos+size*gMessageBuffer[i].tilt,yPos,zPos); 	
						glEnd();			

						#ifdef __POLYCOUNT
						gPolyCount+=2;
						#endif
						xPos+=width;
					}
				}
				else if(*printPos=='\t')
					xPos=ceil(xPos*kTabSpaces/2)/(kTabSpaces/2);
				else if(*printPos=='\255')
				{
					printPos++;
					char iconFile[256];
					int chi=0;
					while(*printPos!='\255'&&*printPos!='\0'&&chi<255)
						iconFile[chi++]=*(printPos++);
					//printPos++;
					iconFile[chi]='\0';
					if(iconFile[0]=='#')
					{
						float r,g,b;
						switch(iconFile[1])
						{
							case 'w': r=1;g=1;b=1; break;
							case 'r': r=1;g=0;b=0; break;
							case 'g': r=0;g=1;b=0; break;
							case 'b': r=0;g=0;b=1; break;
							case 'y': r=1;g=1;b=0; break;
							case 'c': r=0;g=1;b=1; break;
							case 'm': r=1;g=0;b=1; break;
							case 'a': r=0.8;g=0.8;b=0.8; break;
							case 'd': r=0.6;g=0.6;b=0.6; break;
							case 'l': r=0;g=0;b=0; break;
							case 'n': r=gMessageBuffer[i].r;g=gMessageBuffer[i].g;b=gMessageBuffer[i].b; break;
							case '0': r=0;g=0;b=0; break;
						}
						if(useInstrumentColor)
							glColor4f(gEnvironment->instrumentColor.x*r
									,gEnvironment->instrumentColor.y*g
									,gEnvironment->instrumentColor.z*b
									,fade*gMessageBuffer[i].a*opacity);
						else
							glColor4f(r,g,b,fade*gMessageBuffer[i].a*opacity);

					}
					else
					{
						tFileRef icon=FileGetReference(iconFile);
						if(icon!=kFileErr)
						{
							char *extension=FileGetExtension(icon);
							if(!(_stricmp(extension,".pct")&&_stricmp(extension,".tif")&&_stricmp(extension,".png")&&_stricmp(extension,".txr")))
							{
								glPushAttrib(GL_CURRENT_BIT);
								glColor4f(1,1,1,fade*gMessageBuffer[i].a*opacity);
								TexturesSelectTex(icon);
								
								GLint width,height;
								glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
								glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
								float ratio=height/(float)width;

								glBegin(GL_TRIANGLE_STRIP);								
								glTexCoord2d(1,1); glVertex3f(xPos+size,yPos-size*ratio,zPos);		
								glTexCoord2d(1,0); glVertex3f(xPos+size,yPos,zPos); 	
								glTexCoord2d(0,1); glVertex3f(xPos,yPos-size*ratio,zPos); 	
								glTexCoord2d(0,0); glVertex3f(xPos,yPos,zPos); 	
								glEnd();							
								glPopAttrib();
								TexturesSelectTex(gTextFont->fontTexture);
								
								xPos+=size;
							}
						}
					}
				}
				if(gMessageBuffer[i].align==(kTextAlignLeft|kTextAutoWrap))
					if(xPos>0.43&&*printPos==' ')
					{
						xPos=gMessageBuffer[i].pos.x;
						yPos-=size;
					}
				if(gMessageBuffer[i].align==(kTextAlignLeft|kTextAutoCut))
					if(xPos>0.49)
						xPos=10;
				if((*printPos=='\n'||*printPos=='\r')&&(gMessageBuffer[i].align&kTextAlignMask)==kTextAlignLeft)
				{
					xPos=gMessageBuffer[i].pos.x;
					yPos-=size;
				}
				if(*printPos!='\0')
					printPos++;
			}
		}
	glPopAttrib();
	gFileErrorReporting=tmpFileError;
	gTexturesQualityModifier=0;
}

void TextClearBuffer()
{
	int fadeCount=0;
	for(int i=0;i<gMessagePos;i++)
		if(gMessageBuffer[i].startFade!=-1&&gFrameCount<=gMessageBuffer[i].endFade)
		{
			gMessageBuffer[fadeCount]=gMessageBuffer[i];
			fadeCount++;
		}
	gMessagePos=fadeCount;
}

void TextClearBufferFading()
{
	gMessagePos=0;
}

void TextPrintfToBuffer(const char *fmt, ...)
{
	if(gMessagePos>=kMessageBufferSize)
		return;
	va_list		ap;					// Pointer To List Of Arguments

	if (fmt == NULL)					// If There's No Text
			return;						// Do Nothing

	va_start(ap, fmt);					// Parses The String For Variables
	gMessageBuffer[gMessagePos].pos=Vector(0,0,-1);
	gMessageBuffer[gMessagePos].size=gTextSize;	
	gMessageBuffer[gMessagePos].startFade=-1;	
	gMessageBuffer[gMessagePos].endFade=-1;	
	gMessageBuffer[gMessagePos].r=1;	
	gMessageBuffer[gMessagePos].g=1;	
	gMessageBuffer[gMessagePos].b=1;	
	gMessageBuffer[gMessagePos].a=gTextOpacity;	
	gMessageBuffer[gMessagePos].align=kTextAlignLeft;	
	gMessageBuffer[gMessagePos].tilt=0;	
	vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap);				// And Converts Symbols To Actual Numbers
	va_end(ap);						// Results Are Stored In Text
}

void TextPrintfToBufferFormated(tVector2 pos,float size,int align,const char *fmt, ...)
{
	if(gMessagePos>=kMessageBufferSize)
		return;
	va_list		ap;					// Pointer To List Of Arguments

	if (fmt == NULL)					// If There's No Text
			return;						// Do Nothing

	va_start(ap, fmt);					// Parses The String For Variables
	gMessageBuffer[gMessagePos].pos.x=pos.x*kTextXPos;
	gMessageBuffer[gMessagePos].pos.y=pos.y*kTextYPos;
	gMessageBuffer[gMessagePos].pos.z=-1;
	gMessageBuffer[gMessagePos].size=size;	
	gMessageBuffer[gMessagePos].startFade=-1;	
	gMessageBuffer[gMessagePos].endFade=-1;	
	gMessageBuffer[gMessagePos].r=1;	
	gMessageBuffer[gMessagePos].g=1;	
	gMessageBuffer[gMessagePos].b=1;	
	gMessageBuffer[gMessagePos].a=gTextOpacity;	
	gMessageBuffer[gMessagePos].align=align;	
	gMessageBuffer[gMessagePos].tilt=0;	
	vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap);				// And Converts Symbols To Actual Numbers
	va_end(ap);						// Results Are Stored In Text
}

void TextPrintfToBufferFormatedVector3(tVector3 pos,float size,int align,const char *fmt, ...)
{
	if(gMessagePos>=kMessageBufferSize)
		return;
	va_list		ap;					// Pointer To List Of Arguments

	if (fmt == NULL)					// If There's No Text
			return;						// Do Nothing

	va_start(ap, fmt);					// Parses The String For Variables
	pos.z=-pos.z;
	pos.x=-pos.x;
	gMessageBuffer[gMessagePos].pos=pos;
	gMessageBuffer[gMessagePos].size=size;	
	gMessageBuffer[gMessagePos].startFade=-1;	
	gMessageBuffer[gMessagePos].endFade=-1;	
	gMessageBuffer[gMessagePos].r=1;	
	gMessageBuffer[gMessagePos].g=1;	
	gMessageBuffer[gMessagePos].b=1;	
	float dist=fabs(pos.z)/20.0;
	if(dist>1)dist=1;
	gMessageBuffer[gMessagePos].a=gTextOpacity*dist;	
	gMessageBuffer[gMessagePos].align=align;	
	gMessageBuffer[gMessagePos].tilt=0;	
	vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap);				// And Converts Symbols To Actual Numbers
	va_end(ap);						// Results Are Stored In Text
}

void TextPrintfToBufferFormatedColored(tVector2 pos,float size,int align,float r,float g,float b,float a,const char *fmt, ...)
{
	if(gMessagePos>=kMessageBufferSize)
		return;
	va_list		ap;					// Pointer To List Of Arguments

	if (fmt == NULL)					// If There's No Text
			return;						// Do Nothing

	va_start(ap, fmt);					// Parses The String For Variables
	gMessageBuffer[gMessagePos].pos.x=pos.x*kTextXPos;
	gMessageBuffer[gMessagePos].pos.y=pos.y*kTextYPos;
	gMessageBuffer[gMessagePos].pos.z=-1;
	gMessageBuffer[gMessagePos].size=size;	
	gMessageBuffer[gMessagePos].startFade=-1;	
	gMessageBuffer[gMessagePos].endFade=-1;	
	gMessageBuffer[gMessagePos].r=r;	
	gMessageBuffer[gMessagePos].g=g;	
	gMessageBuffer[gMessagePos].b=b;	
	gMessageBuffer[gMessagePos].a=a*gTextOpacity;	
	gMessageBuffer[gMessagePos].align=align;	
	gMessageBuffer[gMessagePos].tilt=0;	
	vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap);				// And Converts Symbols To Actual Numbers
	va_end(ap);						// Results Are Stored In Text
}

void TextPrintfToBufferFormatedColoredTilted(tVector2 pos,float size,int align,float tilt,float r,float g,float b,float a,const char *fmt, ...)
{
	if(gMessagePos>=kMessageBufferSize)
		return;
	va_list		ap;					// Pointer To List Of Arguments

	if (fmt == NULL)					// If There's No Text
			return;						// Do Nothing

	va_start(ap, fmt);					// Parses The String For Variables
	gMessageBuffer[gMessagePos].pos.x=pos.x*kTextXPos;
	gMessageBuffer[gMessagePos].pos.y=pos.y*kTextYPos;
	gMessageBuffer[gMessagePos].pos.z=-1;
	gMessageBuffer[gMessagePos].size=size;	
	gMessageBuffer[gMessagePos].startFade=-1;	
	gMessageBuffer[gMessagePos].endFade=-1;	
	gMessageBuffer[gMessagePos].r=r;	
	gMessageBuffer[gMessagePos].g=g;	
	gMessageBuffer[gMessagePos].b=b;	
	gMessageBuffer[gMessagePos].a=a*gTextOpacity;	
	gMessageBuffer[gMessagePos].align=align;	
	gMessageBuffer[gMessagePos].tilt=tilt;	
	vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap);				// And Converts Symbols To Actual Numbers
	va_end(ap);						// Results Are Stored In Text
}

void TextPrintfToBufferFormatedFading(tVector2 pos,float size,int align,float startFade,float endFade,const char *fmt, ...)
{
	if(gMessagePos>=kMessageBufferSize)
		return;

	va_list		ap;					// Pointer To List Of Arguments

	if (fmt == NULL)					// If There's No Text
		return;							// Do Nothing

	va_start(ap, fmt);					// Parses The String For Variables
	gMessageBuffer[gMessagePos].pos.x=pos.x*kTextXPos;
	gMessageBuffer[gMessagePos].pos.y=pos.y*kTextYPos;
	gMessageBuffer[gMessagePos].pos.z=-1;
	gMessageBuffer[gMessagePos].size=size;	
	gMessageBuffer[gMessagePos].startFade=gFrameCount+startFade*kFPS;	
	gMessageBuffer[gMessagePos].endFade=gFrameCount+endFade*kFPS;
	gMessageBuffer[gMessagePos].startFadeIn=gFrameCount;
	gMessageBuffer[gMessagePos].r=1;	
	gMessageBuffer[gMessagePos].g=1;	
	gMessageBuffer[gMessagePos].b=1;	
	gMessageBuffer[gMessagePos].a=gTextOpacity*0.7;	
	gMessageBuffer[gMessagePos].align=align;	
	gMessageBuffer[gMessagePos].tilt=0;	
	vsprintf(gMessageBuffer[gMessagePos++].text,fmt,ap);				// And Converts Symbols To Actual Numbers
	va_end(ap);						// Results Are Stored In Text
}

