#include "gosGml.hpp"

#define GmlDisplaySystem_ShowMessages

//===========================================================================//
// File:	 GmlDisplaySystem.cpp											 //
// Project:  gosGml				                                         //
// Contents:																 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 4/10/00   DP     Created													 //
//---------------------------------------------------------------------------//
// Copyright (C) 2000 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

extern HGOSFONT3D comic12;
extern HGOSFONT3D arial12;
extern HGOSFONT3D comic10;
extern HGOSFONT3D arial10;
extern HGOSFONT3D comic8;
extern HGOSFONT3D arial8;
extern __int64 TimeDrawImages;
extern __int64 TimeDraw;
extern __int64 TimeDrawLine;
extern __int64 TimeWrapLine;
extern __int64 TimeTextDrawGml;
extern __int64 TimegosStringLength;
extern __int64 TimeParse;
extern __int64 TimeCacheDraw;
extern __int64 TimeTotal;
extern __int64 TimeTextDraw;
extern __int64 TimeDisplayRegionFromBuffer;
extern __int64 TimeCheckForCacheHit;
extern __int64 TimeDrawQuads;

#include "GmlDisplaySystem.h"
#pragma warning( disable: 4706 )
#pragma warning( disable: 4701 )

void GmlDisplaySystem::Clear()
{
	//clear the Gml screen.
}

GmlDisplaySystem::GmlDisplaySystem(char* fontPath, char* imagePath, rect* DisplayTextArea, int tabwidth)
	:TabWidth(tabwidth),CachedText(0)
{
	TextArea=DisplayTextArea;
	if(TextArea->Left<0)
		TextArea->Left=0;
	if(TextArea->Right>Environment.screenWidth)
		TextArea->Right=Environment.screenWidth;
	if(TextArea->Top<0)
		TextArea->Top=0;
	if(TextArea->Bottom>Environment.screenHeight)
		TextArea->Bottom=Environment.screenHeight;
	SB=new Scrollbar(0,0,0,0);
	links=new Link(TextArea->Right);
	TDB = new TextDrawBuffer();
	DisplayQueue = new GQueue<GmlParsedString>;
	FontPath=new char[mstrlen(fontPath)+1];
	mstrcpy(FontPath,fontPath);
	ImagePath=new char[mstrlen(imagePath)+1];
	mstrcpy(ImagePath,imagePath);
	GmlP = new GmlParser(FontPath, ImagePath);
	ImageHash= new Hash<DWORD,char*>(983); //how many images do you NEED?
	FontHash= new Hash<DWORD,char*>(101); //if you use more than 30 fonts...
	LinkStack= new GStack<GMLHyperlink>; //I really don't expect to see very many of these
	APICache = new GQueue<CacheString>;
	FontPtrStack = new GStack<HGOSFONT3D>;
	ImagePtrStack = new GStack<DWORD>;
	VideoStack = new GStack<HGOSVIDEO>;
	RegisteredImageStack = new GStack<ImageData>;
	BrokenImage=new char[strlen(imagePath)+15+1];
	strcpy(BrokenImage,imagePath);
	strcpy(BrokenImage+strlen(imagePath),"BrokenImage.png");
	if(!gos_DoesFileExist(BrokenImage))
		STOP(("GMLDisplaySystem - ImagePath directory does not contain BrokenImage.png"));
}

GmlDisplaySystem::~GmlDisplaySystem()
{
	ClearCache(999);
	delete[] BrokenImage;
	delete[] FontPath;
	delete[] ImagePath;
	delete RegisteredImageStack;
	delete VideoStack;
	delete ImagePtrStack;
	delete FontPtrStack;
	delete APICache;
	delete DisplayQueue;
	delete LinkStack;
	delete FontHash;
	delete ImageHash;
	delete GmlP;
	delete TDB;
	delete links;
	delete SB;
}

void GmlDisplaySystem::SetParserCallback(ParserCallback* PCB)
{
	GmlP->SetCallback(PCB);
}

//void __stdcall _gos_TextSetAttributes( HGOSFONT3D FontHandle, DWORD Foreground, float Size, bool WordWrap, bool Proportional, bool Bold, bool Italic, DWORD WrapType=0 );
void GmlDisplaySystem::DisplayTextFromBuffer()
{
	#ifdef LAB_ONLY
		__int64 TimeTemp=GetCycles();
	#endif
	rect temp={2147483647,TextArea->Left,TextArea->Right,TextArea->Top};
	#ifdef GmlDisplaySystem_ShowMessages
	static int i=0;
	GmlParsedString* xps;

	if(i)
	{
		if(!arial8)
			arial8=gos_LoadFont("assets\\fonts\\arial8.d3f");
		_gos_TextSetAttributes(arial8,0xffffffff,1,true,true,false,false,0);
		while((xps=DisplayQueue->Pop())!=0)
		{
			char* Buffer=new char[33];
			char* Temp;
			int OX,OY;
			gos_TextGetPrintPosition(&OX,&OY);
			_gos_TextSetPosition(OX,OY);
			switch(xps->DType)
			{
				case 0:
					_gos_TextDraw("PlainText ");
					break;
				case 1:
					_gos_TextDraw("Bullet ");
					break;
				case 2:
					_gos_TextDraw("NumList ");
					break;
				case 3:
					_gos_TextDraw("Tab ");
					break;
				case 4:
					_gos_TextDraw("Link ");
					break;
				case 5:
					_gos_TextDraw("LineBreak ");
					break;
				case 6:
					_gos_TextDraw("Image ");
					break;
				default:
					_gos_TextDraw("Unknown Type ");
					break;
			}
			if(xps->Extra)
			{
				Temp=itoa(xps->Extra,Buffer,10);
				_gos_TextDraw(Temp);
				_gos_TextDraw(" ");
			}
			if(xps->LinkDest)
			{
				_gos_TextDraw(xps->LinkDest);
				_gos_TextDraw(" ");
			}
			if(xps->Font->Face)
			{
				_gos_TextDraw(xps->Font->Face);
				_gos_TextDraw("\n");
			}
			if(xps->Font->Color)
			{
				_gos_TextDraw(xps->Font->Color);
				_gos_TextDraw(" ");
			}
			if(xps->Font->Size)
			{
				_gos_TextDraw(xps->Font->Size);
				_gos_TextDraw("(");
				Temp=itoa(xps->Font->iSize,Buffer,10);
				_gos_TextDraw(Temp);
				_gos_TextDraw(")\n-->'");
			}
			else
				_gos_TextDraw("\n-->'");
			_gos_TextDraw(xps->Text);
			_gos_TextDraw("'\n");
			delete xps;
			delete[] Buffer;
		}
	}
	else
		DisplayRegionFromBuffer(temp,false);
	#endif
	#ifdef LAB_ONLY
		TimeDraw+=GetCycles()-TimeTemp;
	#endif
}

void GmlDisplaySystem::DisplayRegionFromBuffer(rect region, bool FreeLine)
{
	FreeLine;
	#ifdef LAB_ONLY
		__int64 TimeTemp=GetCycles();
	#endif
	GmlParsedString* xps;
	int* gf;
	GQueue<GmlParsedString>* DProcQueue = new GQueue<GmlParsedString>;
	unsigned long XPosL,YPosL;
	int XPos,YPos,BiggestFontThisLine,LastRealBiggestFont=LineBreakSize,LastFontSize=0;
	int MaxStringLength; //ending-starting
	int LineStartingX;
	int StringLength; //total length of all strings this line
	bool DisplayComplete=false,setgf=false;
	DWORD Tex;

	//group the strings into "lines" and pass them off for display.
	while(!DisplayComplete) //this only evaluates once per line
	{
		gos_TextGetPrintPosition(&XPos,&YPos);
		MaxStringLength=region.Right-region.Left;
		StringLength=0;
		LineStartingX=XPos;
		BiggestFontThisLine=LineBreakSize;

		while((xps=DisplayQueue->Pop())!=0)//this evaluates once per parsedstring
		{
			switch(xps->DType)
			{
				case GmlParsedString_PlainText:
					if(!_gos_LoadFont(xps->Font->Face))
					{
						delete[] xps->Text;
						xps->Text=new char[9];
						mstrcpy(xps->Text,"bad font");
					}
					_gos_TextSetAttributes(arial8,0xffffffff,1,false,true,false,false,0);
					_gos_TextStringLength( &XPosL, &YPosL, xps->Text);
					StringLength+=XPosL;
					LastFontSize=xps->Font->iSize;
					break;
				case GmlParsedString_Bullet: //TODO: add proper length for bullets
					StringLength+=TextTabEndingPos(StringLength+LineStartingX)-region.Left;
					setgf=true;
					gf=&xps->Font->iSize;
					break;
				case GmlParsedString_NumList: //TODO: add proper length for numbered lists
					StringLength+=TextTabEndingPos(StringLength+LineStartingX)-region.Left;
					setgf=true;
					gf=&xps->Font->iSize;
					break;
				case GmlParsedString_Tab:  //note: tab is independant of font size, is this a bug?
					StringLength+=TextTabEndingPos(StringLength+LineStartingX)-region.Left;
					break;
				case GmlParsedString_Link:
					_gos_TextSetAttributes(arial8,0xff0000ff,1,false,true,false,false,0);
					xps->Font->iSize=8; //hard coded font size here
					_gos_TextStringLength( &XPosL, &YPosL, xps->Text);
					StringLength+=XPosL;
					LastFontSize=8;
					break;
				case GmlParsedString_br:
					break;
				case GmlParsedString_Image:
					Tex=LoadTex(xps->Text);
					StringLength+=GetImageWidth(Tex)+(ImageBuffer<<1);
					if(xps->Extra==GmlParsedImage_AlignNone)
						xps->Font->iSize=GetImageHeight(Tex);  //I love this hack!!
					break;
			}
			if(xps->Font->iSize+LineBreakSize>BiggestFontThisLine)
				BiggestFontThisLine=xps->Font->iSize+LineBreakSize;
			if(StringLength>MaxStringLength || xps->DType==GmlParsedString_br || ( xps->DType==GmlParsedString_Image && xps->Extra!=GmlParsedImage_AlignNone ) )
			{
				if(YPos+BiggestFontThisLine>region.Bottom || (BiggestFontThisLine==LineBreakSize && YPos+LastRealBiggestFont>region.Bottom) || (xps->DType==GmlParsedString_Image && YPos+GetImageHeight(Tex) > region.Bottom))
				{
					DisplayQueue->RPush(xps);
					while(xps=DProcQueue->Pop())
						DisplayQueue->RPush(xps);
					delete DProcQueue;
					setgf=false;
					return;
				}
				if(xps->DType!=GmlParsedString_Image)
				{
					if(setgf)
					{
						*gf=BiggestFontThisLine-LineBreakSize;
						setgf=false;
					}
					if(!DProcQueue->IsEmpty())
						DisplayLineFromQueue(DProcQueue,MaxStringLength,BiggestFontThisLine);
					if(BiggestFontThisLine>LineBreakSize)
						DisplayWrapLine(DProcQueue,xps,MaxStringLength,BiggestFontThisLine, region);
					else
						DisplayWrapLine(DProcQueue,xps,MaxStringLength,LastFontSize, region);//LastRealBiggestFont
				}
				else
				{
					if(setgf)
					{
						if(GetImageHeight(Tex)<BiggestFontThisLine-LineBreakSize)
							*gf=BiggestFontThisLine-LineBreakSize;
						else
							*gf=GetImageHeight(Tex);
						setgf=false;
					}
					if(!DProcQueue->IsEmpty())
						DisplayLineFromQueue(DProcQueue,MaxStringLength,BiggestFontThisLine);
					gos_TextGetPrintPosition(&XPos,&YPos);
					if(BiggestFontThisLine>LineBreakSize)
						DisplayImage(xps->Text, xps->Alt, BiggestFontThisLine, xps->Extra, &region);
					else
						DisplayImage(xps->Text, xps->Alt, LastRealBiggestFont, xps->Extra, &region);
					delete xps;
				}
				//_gos_TextSetAttributes(arial8,0xffffffff,1,false,true,false,false,0);
				if(BiggestFontThisLine>LineBreakSize)
					LastRealBiggestFont=BiggestFontThisLine;
				BiggestFontThisLine=LineBreakSize;
				break; //get out of the loop
			}
			else
			{
				DProcQueue->Push(xps);
			}
		}
		if(xps==0)
		{
			DisplayLineFromQueue(DProcQueue,MaxStringLength,BiggestFontThisLine);
			DisplayComplete=true; //we're all done
		}
	}
	delete DProcQueue; //there should be nothing in DProcQueue, but it doesn't matter
	#ifdef LAB_ONLY
		TimeDisplayRegionFromBuffer+=GetCycles()-TimeTemp;
	#endif
}

void GmlDisplaySystem::DisplayImage(char* FileName, char* Alt, int BiggestFontThisLine, int DisplayPos, rect* region)
{

	#ifdef LAB_ONLY
		__int64 TimeTemp=GetCycles();
	#endif
	unsigned long OV1=0,OV2=0,OV3=Environment.screenWidth,OV4=Environment.screenHeight;
	int Left,Right,Top,Bottom,XPos,YPos;
	gos_VERTEX pVertices[4];
	DWORD Tex;
	Tex=ImageHash->FindHit(FileName); //this will always work, since the file was loaded in the function that called this
	pVertices[0].z		=	0.0f;
	pVertices[1].z		=	0.0f;
	pVertices[2].z		=	0.0f;
	pVertices[3].z		=	0.0f;

	pVertices[0].rhw	=	1.0f;
	pVertices[1].rhw	=	1.0f;
	pVertices[2].rhw	=	1.0f;
	pVertices[3].rhw	=	1.0f;

	pVertices[0].argb	=	0xffffffff;
	pVertices[1].argb	=	0xffffffff;
	pVertices[2].argb	=	0xffffffff;
	pVertices[3].argb	=	0xffffffff;

	pVertices[0].frgb	=	0;
	pVertices[1].frgb	=	0;
	pVertices[2].frgb	=	0;
	pVertices[3].frgb	=	0;

	pVertices[0].u		= 0.0f;
	pVertices[0].v		= 0.0f;
	pVertices[1].u		= 0.0f;
	pVertices[1].v		= 1.0f;
	pVertices[2].u		= 1.0f;
	pVertices[2].v		= 1.0f;
	pVertices[3].u		= 1.0f;
	pVertices[3].v		= 0.0f;

	gos_SetRenderState( gos_State_AlphaTest, 1 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_Clipping, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_WireframeMode, 0 );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
	gos_SetRenderState( gos_State_Fog, 0 );
	gos_SetRenderState( gos_State_Texture, Tex );
	gos_SetRenderState( gos_State_Clipping, 2 );

	//gos_GetViewport(&OV1,&OV2,&OV3,&OV4);
	gos_SetViewport(TextArea->Left,TextArea->Top,TextArea->Right-TextArea->Left,TextArea->Bottom-TextArea->Top, 0.0f, 1.0f);

	switch(DisplayPos)
	{
		case GmlParsedImage_AlignNone:
			{
				gos_TextGetPrintPosition(&XPos,&YPos);
				Top=YPos+ImageBuffer;
				Left=XPos+ImageBuffer;
				Bottom=Top+GetImageHeight(Tex);
				Right=Left+GetImageWidth(Tex);
				pVertices[0].x	=	(float)Left;
				pVertices[0].y	=		(float)Top;
				pVertices[1].x	=	(float)Left;
				pVertices[1].y	=		(float)Bottom;
				pVertices[2].x	=	(float)Right;
				pVertices[2].y	=		(float)Bottom;
				pVertices[3].x	=	(float)Right;
				pVertices[3].y	=		(float)Top;
				DrawImage( pVertices, Tex );
				RegisterImage(FileName, Alt, Tex, (int)pVertices[0].x, (int)pVertices[0].y);
				_gos_TextSetPosition(Right+ImageBuffer,YPos);
			}
			break;
		case GmlParsedImage_AlignLeft:
			{
				gos_TextGetPrintPosition(&XPos,&YPos);
				XPos=region->Left;
				YPos+=BiggestFontThisLine;
				Top=YPos+ImageBuffer;
				Left=region->Left;
				Right=Left+GetImageWidth(Tex);
				Bottom=Top+GetImageHeight(Tex);
				pVertices[0].x	=	(float)Left;
				pVertices[0].y	=		(float)Top;
				pVertices[1].x	=	(float)Left;
				pVertices[1].y	=		(float)Bottom;
				pVertices[2].x	=	(float)Right;
				pVertices[2].y	=		(float)Bottom;
				pVertices[3].x	=	(float)Right;
				pVertices[3].y	=		(float)Top;
				DrawImage( pVertices, Tex );
				RegisterImage(FileName, Alt, Tex, (int)pVertices[0].x, (int)pVertices[0].y);
				_gos_TextSetPosition(Right,YPos);
				rect DRegion={Bottom,Right+ImageBuffer,region->Right,Top};
				#ifdef LAB_ONLY
					TimeDrawImages+=GetCycles()-TimeTemp;
				#endif
				DisplayRegionFromBuffer(DRegion,true);
				#ifdef LAB_ONLY
					TimeTemp=GetCycles();
				#endif
				_gos_TextSetPosition(region->Left,Bottom+ImageBuffer);
			}
			break;
		case GmlParsedImage_AlignRight:
			{
				gos_TextGetPrintPosition(&XPos,&YPos);
				XPos=region->Left;
				YPos+=BiggestFontThisLine;
				Top=YPos+ImageBuffer;
				Right=region->Right;
				Left=Right-GetImageWidth(Tex);
				Bottom=Top+GetImageHeight(Tex);
				pVertices[0].x	=	(float)Left;
				pVertices[0].y	=		(float)Top;
				pVertices[1].x	=	(float)Left;
				pVertices[1].y	=		(float)Bottom;
				pVertices[2].x	=	(float)Right;
				pVertices[2].y	=		(float)Bottom;
				pVertices[3].x	=	(float)Right;
				pVertices[3].y	=		(float)Top;
				DrawImage( pVertices, Tex );
				RegisterImage(FileName, Alt, Tex, (int)pVertices[0].x, (int)pVertices[0].y);
				_gos_TextSetPosition(region->Left,YPos);
				rect DRegion={Bottom,region->Left,Left-ImageBuffer,Top};
				#ifdef LAB_ONLY
					TimeDrawImages+=GetCycles()-TimeTemp;
				#endif
				DisplayRegionFromBuffer(DRegion,true);
				#ifdef LAB_ONLY
					TimeTemp=GetCycles();
				#endif
				_gos_TextSetPosition(region->Left,Bottom+ImageBuffer);
			}
			break;
	}
	gos_SetViewport(OV1,OV2,OV3,OV4,0.0f,1.0f);
	#ifdef LAB_ONLY
		TimeDrawImages+=GetCycles()-TimeTemp;
	#endif
}

void GmlDisplaySystem::DisplayLineFromQueue(GQueue<GmlParsedString>* LineQueue, int MaxX, int BiggestFontThisLine)
{
	MaxX;
	#ifdef LAB_ONLY
		__int64 TimeTemp=GetCycles();
	#endif
	GmlParsedString* xps;
	int OX, OY, Dummy;
	while((xps=LineQueue->Pop())!=0)
	{
		switch(xps->DType)
		{
			case GmlParsedString_PlainText:
				gos_TextGetPrintPosition(&OX,&OY);
				_gos_TextSetPosition(OX,OY+BiggestFontThisLine-xps->Font->iSize);
				_gos_TextDraw(xps->Text);
				gos_TextGetPrintPosition(&OX,&Dummy);
				_gos_TextSetPosition(OX,OY);
				break;
			case GmlParsedString_Bullet: //TODO: bullets
				gos_TextGetPrintPosition(&OX,&OY);
				_gos_TextSetPosition(OX,OY+BiggestFontThisLine-xps->Font->iSize);
				_gos_TextDraw("-");
				gos_TextGetPrintPosition(&OX,&Dummy);
				_gos_TextSetPosition(OX,OY);
				TextTab();
				break;
			case GmlParsedString_NumList:
				{
					gos_TextGetPrintPosition(&OX,&OY);
					_gos_TextSetPosition(OX,OY+BiggestFontThisLine-xps->Font->iSize);
					char* Str=new char[33];
					itoa(xps->Extra,Str,10);
					_gos_TextDraw(Str);
					delete[] Str;
					_gos_TextDraw(".");
					gos_TextGetPrintPosition(&OX,&Dummy);
					_gos_TextSetPosition(OX,OY);
					TextTab();
				}
				break;
			case GmlParsedString_Tab:
				TextTab();
				break;
			case GmlParsedString_Link: //TODO: link image support
			{
				int x1, y1,x2,y2;
				gos_TextGetPrintPosition(&OX,&OY);
				_gos_TextSetPosition(OX,OY+BiggestFontThisLine-xps->Font->iSize);
				gos_TextGetPrintPosition(&x1,&y1);
				if(!strcmp(xps->Alt,"plaintext"))
					_gos_TextSetAttributes(arial8,0xffffffff,1,false,true,false,false,0);
				else
					_gos_TextSetAttributes(arial8,0xff0000ff,1,false,true,false,false,0);
				_gos_TextDraw(xps->Text);
				gos_TextGetPrintPosition(&x2,&y2);
				LinkBox* lb = new LinkBox;
				lb->Bottom=y2+xps->Font->iSize;
				lb->Left=x1;
				lb->Right=x2;
				lb->Top=y1;
				lb->Dest=xps->LinkDest;
				links->AddLink(*lb,xps->Font->iSize);
				GMLHyperlink* GHL = new GMLHyperlink;
				GHL->Alt=xps->Alt;
				GHL->Name=xps->Text;
				GHL->Target=xps->LinkDest;
				LinkStack->Push(GHL);
				delete lb;
				_gos_TextSetPosition(x2,OY);
				break;
			}
			case GmlParsedString_br: //this will never happen
				break;
			case GmlParsedString_Image: //this will be a non aligned image, aka one big font character
				{
					rect* temprect=new rect;
					DisplayImage(xps->Text, xps->Alt, BiggestFontThisLine, xps->Extra, temprect);
					delete temprect;
					break;
				}
		}
		delete xps;
	}
	#ifdef LAB_ONLY
		TimeDrawLine+=GetCycles()-TimeTemp;
	#endif
}

#pragma warning(disable:4018)
#pragma warning(disable:4800)
void GmlDisplaySystem::DisplayWrapLine(GQueue<GmlParsedString>* LineQueue, GmlParsedString* Line, int MaxX, int BiggestFontThisLine, rect region)
{
	LineQueue;
	#ifdef LAB_ONLY
		__int64 TimeTemp=GetCycles();
	#endif
	//void __stdcall _gos_TextSetAttributes( HGOSFONT3D FontHandle, DWORD Foreground, float Size, bool WordWrap, bool Proportional, bool Bold, bool Italic, DWORD WrapType=0 );
	_gos_TextSetAttributes(arial8,0xffffffff,1,false,true,false,false,0); //the ONLY point of this is to set the linewrap. arial8 is just a placeholder
	char* Str=Line->Text;
	char* FontStr;
	int OX,OY, Dummy;
	switch(Line->DType)
	{
		case GmlParsedString_PlainText: //TODO: wrapping plaintext
		{
			//notes:
			//almost all strings will have the /font=<asdf>/size=<##>/color=FFFFFFFF at the beginning.
			gos_TextGetPrintPosition(&OX,&OY);
			_gos_TextSetPosition(OX,OY+BiggestFontThisLine-Line->Font->iSize);
			MaxX-=OX-region.Left;
			FontStr=GrabHeaders(Str);
			_gos_TextDraw(FontStr); //set the font
			Str+=mstrlen(FontStr);
			bool BoldOn=false, ItalicOn=false;

			unsigned long x,y,Pos=0, OPos;
			char* TStr = new char[mstrlen(Str)+1];
			memset(TStr,0,mstrlen(Str)+1);
			do
			{
				if(*(Str+Pos)=='/')
					if(!memcmp(Str+Pos+1,"bold=",5) && !((*(Str+Pos+6)-48)&0xFFFFFFFE)) //*(Str+6)=='0' || *(Str+6)=='1')
					{
						BoldOn=*(Str+Pos+6)-48;
						memcpy(TStr+Pos,Str+Pos,7);
						Pos+=7;
						continue;
					}
						else
							if(!memcmp(Str+Pos+1,"italic=",7) && !((*(Str+Pos+8)-48)&0xFFFFFFFE))// *(Str+8)=='0' || *(Str+8)=='1')
							{
								ItalicOn=*(Str+Pos+8)-48;
								memcpy(TStr+Pos,Str+Pos,9);
								Pos+=9;
								continue;
							}
				mbpncpy(TStr+Pos,Str+Pos,1); //copy 1 character
				_gos_TextStringLength( &x, &y, TStr);
				Pos=(unsigned long)mbpinc(Str+Pos)-(unsigned long)Str; //advance Pos 1 character
			}while(x<MaxX);
			OPos=Pos;
			if(x!=MaxX)
				Pos=(unsigned long)mbpdec(Str+Pos)-(unsigned long)Str;
			if(!_ismbcspace(*(TStr+Pos+_mbclen((const unsigned char*)(TStr+Pos))))) //if Pos+1 != whitespace
				while(!_ismbcspace(*(TStr+Pos)))									//while Pos != whitespace
					Pos=(unsigned long)mbpdec(Str+Pos)-(unsigned long)Str;			//Pos--
			if(_mbclen((const unsigned char*)(TStr+Pos))==2)
				*(TStr+Pos+1)=0;
			*(TStr+Pos)=0;
			_gos_TextDraw(TStr); //print the text that fits on this line

			while(_ismbcspace(*(Str+Pos)))											//while Pos == whitespace
				Pos=(unsigned long)mbpinc(Str+Pos)-(unsigned long)Str;				//Pos++
			_gos_TextSetPosition(region.Left,OY+BiggestFontThisLine);
			Dummy=mstrlen(FontStr)+mstrlen(Str+Pos)+1;
			if(ItalicOn)
				Dummy+=9;
			if(BoldOn)
				Dummy+=7;
			char* Temp=new char[Dummy];
			mstrcpy(Temp,FontStr);
			Dummy=mstrlen(FontStr);
			if(ItalicOn)
			{
				memcpy(Temp+Dummy,"/italic=1",9);
				Dummy+=9;
			}
			if(BoldOn)
			{
				memcpy(Temp+Dummy,"/bold=1",7);
				Dummy+=7;
			}
			mstrcpy(Temp+Dummy,(Str+Pos));
			delete[] Line->Text;
			Line->Text=Temp;
			DisplayQueue->RPush(Line); //return the remainder of the string to the queue to be processed
			delete[] FontStr;
			delete[] TStr;
			break;
		}
		case GmlParsedString_Bullet: //TODO: add proper length for bullets
		case GmlParsedString_NumList: //TODO: add proper length for numbered lists
		case GmlParsedString_Tab:  //note: tab is independant of font size, is this a bug? probably
			gos_TextGetPrintPosition(&Dummy,&OY);
			_gos_TextSetPosition(region.Left,OY+BiggestFontThisLine);
			delete Line;
			break;
		case GmlParsedString_Link:
			{
			gos_TextGetPrintPosition(&OX,&OY);
			_gos_TextSetPosition(OX,OY+BiggestFontThisLine-Line->Font->iSize);
			if(!strcmp(Line->Alt,"plaintext"))
				_gos_TextSetAttributes(arial8,0xffffffff,1,false,true,false,false,0);
			else
				_gos_TextSetAttributes(arial8,0xff0000ff,1,false,true,false,false,0);
			MaxX-=OX-region.Left;
			unsigned long x,y,Pos=0, OPos;
			char* TStr = new char[mstrlen(Str)+1];
			memset(TStr,0,mstrlen(Str)+1);
			do
			{
				mbpncpy(TStr+Pos,Str+Pos,1); //copy 1 character
				_gos_TextStringLength( &x, &y, TStr);
				Pos=(unsigned long)mbpinc(Str+Pos)-(unsigned long)Str;
			}while(x<MaxX);
			OPos=Pos;
			if(x!=MaxX)
				Pos=(unsigned long)mbpdec(Str+Pos)-(unsigned long)Str;
			if(!_ismbcspace(*(TStr+Pos+_mbclen((const unsigned char*)(TStr+Pos))))) //if Pos+1 != whitespace
				while(!_ismbcspace(*(TStr+Pos)))									//while Pos != whitespace
					Pos=(unsigned long)mbpdec(Str+Pos)-(unsigned long)Str;			//Pos--
			if(_mbclen((const unsigned char*)(TStr+Pos))==2)
				*(TStr+Pos+1)=0;
			*(TStr+Pos)=0;
			int x1,y1,x2,y2;
			gos_TextGetPrintPosition(&x1,&y1);
			_gos_TextDraw(TStr); //print the text that fits on this line
			gos_TextGetPrintPosition(&x2,&y2);
			gos_TextGetPrintPosition(&OX,&Dummy);
			_gos_TextSetPosition(OX,OY);

			LinkBox* lb = new LinkBox;
			lb->Bottom=y2+Line->Font->iSize;
			lb->Left=x1;
			lb->Right=x2;
			lb->Top=y1;
			lb->Dest=Line->LinkDest;
			links->AddLink(*lb,Line->Font->iSize);
			GMLHyperlink* GHL = new GMLHyperlink;
			GHL->Alt=Line->Alt;
			GHL->Name=Line->Text;
			GHL->Target=Line->LinkDest;
			LinkStack->Push(GHL);
			delete lb;

			while(_ismbcspace(*(Str+Pos)))											//while Pos == whitespace
				Pos=(unsigned long)mbpinc(Str+Pos)-(unsigned long)Str;				//Pos++
			gos_TextGetPrintPosition(&Dummy,&OY);
			_gos_TextSetPosition(region.Left,OY+BiggestFontThisLine);
			//_gos_TextSetAttributes(arial12,0xffffffff,(float)BiggestFontThisLine/(float)12,true,true,false,false,0);
			//_gos_TextDraw("\n");
			char* Temp=new char[mstrlen(Str+Pos)+1];
			mstrcpy(Temp,(Str+Pos));
			delete[] Line->Text;
			Line->Text=Temp;
			DisplayQueue->RPush(Line); //return the remainder of the string to the queue to be processed
			delete[] TStr;
			break;
			}
		case GmlParsedString_br:
			gos_TextGetPrintPosition(&Dummy,&OY);
			_gos_TextSetPosition(region.Left,OY+BiggestFontThisLine);
			delete Line;
			break;
		case GmlParsedString_Image: //this will be a non aligned image, aka one big font character
			{
				rect* temprect=new rect;
				DisplayImage(Line->Text, Line->Alt, BiggestFontThisLine, Line->Extra, temprect);
				delete temprect;
				break;
			}
	}
	#ifdef LAB_ONLY
		TimeWrapLine+=GetCycles()-TimeTemp;
	#endif
}
#pragma warning(default:4800)
#pragma warning(default:4018)

char* GmlDisplaySystem::GrabHeaders(char* Text)
{
	char* TextO=Text;
	while( *Text=='/' ) //this is all in order so... this should only evaluate as true once, but if the Textings are out of order then it WILL decrease performance.
	{
		if(!strncmp(Text,"/font",5))
		{
			Text+=5;
			while(*Text!='>')
				Text=mbpinc(Text);
			Text=mbpinc(Text);
		}
		if(!strncmp(Text,"/size",5))
		{
			Text+=5;
			while(*Text!='>')
				Text=mbpinc(Text);
			Text=mbpinc(Text);
		}
		if(!strncmp(Text,"/color",6))
			Text+=15;
		if(!strncmp(Text,"/bold",5) || !strncmp(Text,"/italic",7))
			break;
	}
	char* FontText=new char[Text-TextO+1];
	memcpy(FontText,TextO,Text-TextO);
	*(char*)(FontText+(int)(Text-TextO))=(char)0;
	return FontText;
}

void GmlDisplaySystem::TextTab()
{
	int x,y;
	gos_TextGetPrintPosition(&x,&y);
	_gos_TextSetPosition(TextTabEndingPos(x),y);
}

int GmlDisplaySystem::TextTabEndingPos(int XPos)
{
	return ((XPos+TabWidth-TextArea->Left)/TabWidth)*TabWidth+TextArea->Left;
}

int GmlDisplaySystem::TextDrawGml(char* TextData)
{
	#ifdef LAB_ONLY
		__int64 temp=GetCycles();
	#endif

	/*
	static RemoteM* rmp=0;
	if(!rmp)
	{
		rmp = new RemoteM(false);
		delete rmp;
	}
	*/

	if(CheckForCacheHit(TextData))
	{
		#ifdef LAB_ONLY
			TimeCheckForCacheHit+=GetCycles()-temp;
		#endif
		return DealWithCacheHit();
	}
	else
		ClearCache(1);


	GmlFont* Default=new GmlFont; //it's the business of the GStack to delete this
	Default->Size=new char[2];
	Default->Color=new char[9];
	Default->Face=new char[11+mstrlen(FontPath)];
	mstrcpy(Default->Size,"1");
	mstrcpy(Default->Color,"FFFFFFFF");
	mstrcpy(Default->Face,FontPath);
	mstrcpy(Default->Face+mstrlen(FontPath),"arial8.d3f");
	Default->iSize=8;
	TDB->ClearBuffer();
	links->Clear();
	GmlP->Parse(TextData,DisplayQueue,TDB,Default);
	DisplayTextFromBuffer();

#ifdef GmlFastCache
		memcpy(TextData,"|<++>|",6);
#else
		CachedText=new char[mstrlen(TextData)+1];
		mstrcpy(CachedText,TextData);
#endif
	int x,y;
	gos_TextGetPrintPosition(&x,&y);
	#ifdef LAB_ONLY
		static int times=0;
		TimeTextDrawGml+=GetCycles()-temp;
		if(!times)
		{
			TimeTextDraw=0;
			TimeCheckForCacheHit=0;
			TimeDrawImages=0;
			TimeDraw=0;
			TimeDrawLine=0;
			TimeWrapLine=0;
			TimeTextDrawGml=0;
			TimegosStringLength=0;
			TimeParse=0;
			TimeCacheDraw=0;
			TimeDisplayRegionFromBuffer=0;
			TimeDrawQuads=0;
			times++;
		}
	#endif
	return y;
}

char* GmlDisplaySystem::TestForHit(int iRelX,int iRelY)
{
	XYPair* p = new XYPair;
	p->x=iRelX;
	p->y=iRelY;
	char* dest=links->TestForHit(*p);
	delete p;
	return dest;
}

bool GmlDisplaySystem::CheckForCacheHit(char* Text)
{
#ifdef GmlFastCache
		if(!memcmp(Text,"|<++>|",6))
#else
	if(CachedText)
		if(!mstrcmp(CachedText,Text))
#endif
			return true;
	return false;
}

void GmlDisplaySystem::ClearCache(int level)
{
	if(CachedText)
		delete[] CachedText;
	APICache->Clear();
	if(level)
	{
		DWORD* ImageP;
		HGOSVIDEO VideoP;
		HGOSFONT3D* FontP;
		RegisteredImageStack->Clear();
		//ImageData* IDP;
		//while ((IDP=RegisteredImageStack->Pop()))
		//{
		//	delete[] IDP->Name;
		//	delete IDP;
		//}
		while ((VideoP=(HGOSVIDEO)VideoStack->Pop()))
		{
			gosVideo_DestroyResource(&VideoP);
		}
		while((ImageP=ImagePtrStack->Pop()))
		{
			gos_DestroyTexture((DWORD)ImageP);
		}
		ImageHash->Clear();
		LinkStack->Clear();
		if(level>1)
		{
			FontHash->Clear();
			while(FontP=FontPtrStack->Pop())
			{
				gos_DeleteFont(*FontP);
				delete FontP;
			}
		}
	}
}

int GmlDisplaySystem::DealWithCacheHit()
{
	CacheString* CS;
	#ifdef LAB_ONLY
		__int64 temp=GetCycles();
	#endif
	int dummy,y=0,Oy;
	gos_SetViewport(TextArea->Left,TextArea->Top,TextArea->Right-TextArea->Left,TextArea->Bottom-TextArea->Top, 0.0f, 1.0f);
	gos_TextGetPrintPosition(&dummy,&y);
	Oy=y;
	GQueue<CacheString>* APICache2=new GQueue<CacheString>;
	while(CS=APICache->Pop())
	{
		switch(CS->Type)
		{
			case CacheString_DrawImage:
				{
					int Height=GetImageHeight(CS->Image);
					int Width=GetImageWidth(CS->Image);
					int My=CS->y+Oy-TextArea->Top;
					gos_VERTEX pVertices[4];

					pVertices[0].x	= (float)CS->x;
					pVertices[0].y	= (float)My;
					pVertices[1].x	= (float)CS->x;
					pVertices[1].y	= (float)My+Height;
					pVertices[2].x	= (float)CS->x+Width;
					pVertices[2].y	= (float)My+Height;
					pVertices[3].x	= (float)CS->x+Width;
					pVertices[3].y	= (float)My;

					if(!(My+Height>TextArea->Top && My<TextArea->Bottom))
						break;

					pVertices[0].z		=	0.0f;
					pVertices[1].z		=	0.0f;
					pVertices[2].z		=	0.0f;
					pVertices[3].z		=	0.0f;

					pVertices[0].rhw	=	1.0f;
					pVertices[1].rhw	=	1.0f;
					pVertices[2].rhw	=	1.0f;
					pVertices[3].rhw	=	1.0f;

					pVertices[0].argb	=	0xffffffff;
					pVertices[1].argb	=	0xffffffff;
					pVertices[2].argb	=	0xffffffff;
					pVertices[3].argb	=	0xffffffff;

					pVertices[0].frgb	=	0;
					pVertices[1].frgb	=	0;
					pVertices[2].frgb	=	0;
					pVertices[3].frgb	=	0;

					pVertices[0].u		= 0.0f;
					pVertices[0].v		= 0.0f;
					pVertices[1].u		= 0.0f;
					pVertices[1].v		= 1.0f;
					pVertices[2].u		= 1.0f;
					pVertices[2].v		= 1.0f;
					pVertices[3].u		= 1.0f;
					pVertices[3].v		= 0.0f;

					gos_SetRenderState( gos_State_AlphaTest, 1 );
					gos_SetRenderState( gos_State_Perspective, 0 );
					gos_SetRenderState( gos_State_Specular, 0 );
					gos_SetRenderState( gos_State_Dither, 0 );
					gos_SetRenderState( gos_State_Filter, gos_FilterNone );
					gos_SetRenderState( gos_State_Clipping, 0 );
					gos_SetRenderState( gos_State_ZWrite, 0 );
					gos_SetRenderState( gos_State_ZCompare, 0 );
					gos_SetRenderState( gos_State_WireframeMode, 0 );
					gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
					gos_SetRenderState( gos_State_Fog, 0 );
					gos_SetRenderState( gos_State_Texture, CS->Image );
					gos_SetRenderState( gos_State_Clipping, 2 );
					gos_DrawQuads( pVertices, 4 );
				}
				break;
			case CacheString_DrawText:
				{
					gos_TextGetPrintPosition(&dummy,&y);
					if(y>TextArea->Top&&y<TextArea->Bottom)
						gos_TextDraw(CS->Text);
				}
				break;
			case CacheString_ChangeTextPosition:
				{
					gos_TextSetPosition(CS->x,CS->y+Oy-TextArea->Top);
				}
				break;
			case CacheString_SetTextAtt:
				{
					gos_TextSetAttributes((HGOSFONT3D)CS->Font,CS->Color,(float)CS->Size,CS->Wrap,true,false,false,0);
				}
		}
		APICache2->Push(CS);
	}
	delete APICache;
	APICache=APICache2;
	gos_TextGetPrintPosition(&dummy,&y);
	gos_SetViewport(0,0,Environment.screenWidth,Environment.screenHeight,0.0f,1.0f);
	#ifdef LAB_ONLY
		TimeCacheDraw+=GetCycles()-temp;
		//static int HitCount=0;
		//if(HitCount==2500)
		//{
		//	STOP(("TEST CODE HERE"));
		//	TimeTextDraw=0;
		//	TimeCheckForCacheHit=0;
		//	TimeDrawImages=0;
		//	TimeDraw=0;
		//	TimeDrawLine=0;
		//	TimeWrapLine=0;
		//	TimeTextDrawGml=0;
		//	TimegosStringLength=0;
		//	TimeParse=0;
		//	TimeCacheDraw=0;
		//	TimeDisplayRegionFromBuffer=0;
		//	TimeDrawQuads=0;
		//	HitCount=0;
		//}
		//HitCount++;
	#endif
	return y;
}

void GmlDisplaySystem::_gos_TextDraw( const char *Message)
{
	CacheString* CS = new CacheString(CacheString_DrawText, Message);
	APICache->Push(CS);
	#ifdef LAB_ONLY
		__int64 temp=GetCycles();
	#endif
	gos_TextDraw(Message);
	#ifdef LAB_ONLY
		TimeTextDraw+=GetCycles()-temp;
	#endif
}

void GmlDisplaySystem::_gos_TextSetPosition( int XPosition, int YPosition )
{//usage: CacheString(Type,x,y,Image,char* Text)
	CacheString* CS = new CacheString(CacheString_ChangeTextPosition, XPosition,YPosition);
	APICache->Push(CS);
	gos_TextSetPosition(XPosition,YPosition);
}

void GmlDisplaySystem::DrawImage( gos_VERTEX* Vertices, DWORD Image )
{//usage: CacheString(Type,x,y,Image,char* Text)
	#ifdef LAB_ONLY
		__int64 temp=GetCycles();
	#endif
	CacheString* CS = new CacheString(CacheString_DrawImage, (int)Vertices[0].x, (int)Vertices[0].y, Image);
	APICache->Push(CS);
	if(Vertices[0].y<TextArea->Bottom && Vertices[1].y>TextArea->Top)
		gos_DrawQuads( Vertices, 4 );
	#ifdef LAB_ONLY
		TimeDrawQuads+=GetCycles()-temp;
	#endif
}

void GmlDisplaySystem::_gos_TextStringLength( DWORD* Width, DWORD* Height, const char *Message )
{
	#ifdef LAB_ONLY
		__int64 temp=GetCycles();
	#endif
	gos_TextStringLength( Width, Height, Message );
	#ifdef LAB_ONLY
		 TimegosStringLength+=GetCycles()-temp;
	#endif
}

void GmlDisplaySystem::_gos_TextSetAttributes( HGOSFONT3D FontHandle, DWORD Foreground, float Size, bool WordWrap, bool Proportional, bool Bold, bool Italic, DWORD WrapType, bool DisableEmbeddedCodes )
{
	CacheString* CS = new CacheString(CacheString_SetTextAtt, (int)FontHandle, Foreground, WordWrap, (int)Size);
	APICache->Push(CS);
	gos_TextSetAttributes( FontHandle, Foreground, Size, WordWrap, Proportional, Bold, Italic, WrapType, DisableEmbeddedCodes );
}

inline bool GmlDisplaySystem::_gos_LoadFont(char* LoadName)
{
	if(!FontHash->FindHit(LoadName))
	{
		if(!gos_DoesFileExist(LoadName))
			return false;
		char* StrPtr=new char[mstrlen(LoadName)+1];
		mstrcpy(StrPtr,LoadName);
		HGOSFONT3D* FontTemp=new HGOSFONT3D(gos_LoadFont(StrPtr));
		NameValuePair<DWORD,char*>* nvpT=new NameValuePair<DWORD,char*>(StrPtr,1);
		FontHash->AddHash(nvpT);
		FontPtrStack->Push(FontTemp);
		delete nvpT;
	}
	return true;
}

inline DWORD GmlDisplaySystem::LoadTex(char* Text)
{
	DWORD Tex;
	if(!(Tex=ImageHash->FindHit(Text)))
	{
		if(!gos_DoesFileExist(Text) && !mstrcmp(Text,BrokenImage)) //if you deleted BrokenImage at runtime, die
			LoadTex(BrokenImage);
		else
			if(!strncmp(Text+strlen(Text)-4,".avi",4))
			{
				HGOSVIDEO HGVTemp=0;
				gosVideo_CreateResourceAsTexture( &HGVTemp, &Tex, Text );
				gosVideo_SetPlayMode( HGVTemp, gosVideo_Loop );
				VideoStack->Push((HGOSVIDEO*)HGVTemp);
			}
			else
			{
				Tex=gos_NewTextureFromFile( gos_Texture_Detect, Text);
				ImagePtrStack->Push((unsigned long*)Tex);
			}
		NameValuePair<DWORD,char*>* TempHI=new NameValuePair<DWORD,char*>(NULL,NULL);
		TempHI->IKey=new char[mstrlen(Text)+1];
		mstrcpy(TempHI->IKey,Text);
		TempHI->Data=Tex;
		ImageHash->AddHash(TempHI);
		delete TempHI;
	}
	return Tex;
}

inline int GmlDisplaySystem::GetImageHeight(DWORD TexHandle)
{
	TEXTUREPTR Tex;
	gos_LockTexture(TexHandle, 0, true, &Tex);
	gos_UnLockTexture( TexHandle );
	return Tex.Height;
}

inline int GmlDisplaySystem::GetImageWidth(DWORD TexHandle)
{
	TEXTUREPTR Tex;
	gos_LockTexture(TexHandle, 0, true, &Tex);
	gos_UnLockTexture( TexHandle );
	return Tex.Width;
}

void GmlDisplaySystem::RegisterImage(char* Name, char* Alt, DWORD Handle, int x, int y)
{
	ImageData* ID = new ImageData;
	ID->Name=Name;
	ID->Alt=Alt;
	ID->Handle=Handle;
	ID->x=x;
	ID->y=y;
	RegisteredImageStack->Push(ID);
}

ExportImageData* GmlDisplaySystem::ExportImageList()
{
	ImageData* ID;
	ExportImageData* ExpHead=new ExportImageData;
	ExportImageData* Exp=ExpHead;
	GStack<ImageData>* TempStack=new GStack<ImageData>;
	while(ID=RegisteredImageStack->Pop())
	{
		Exp->Next=new ExportImageData;
		Exp=Exp->Next;
		Exp->Image=ID;
		Exp->Next=NULL;
		TempStack->Push(ID);
	}
	delete RegisteredImageStack;
	RegisteredImageStack=TempStack;
	Exp=ExpHead->Next;
	delete ExpHead; //it was a dummy
	return Exp;
}
ExportHyperlink* GmlDisplaySystem::ExportHyperlinks()
{
	GMLHyperlink* HL;
	ExportHyperlink* ExpHead=new ExportHyperlink;
	ExportHyperlink* Exp=ExpHead;
	GStack<GMLHyperlink>* TempStack=new GStack<GMLHyperlink>;
	while(HL=LinkStack->Pop())
	{
		Exp->Next=new ExportHyperlink;
		Exp=Exp->Next;
		Exp->HLink=HL;
		Exp->Next=NULL;
		TempStack->Push(HL);
	}
	while(HL=TempStack->Pop())	//this has to end up in the right order
		LinkStack->Push(HL);
	Exp=ExpHead->Next;
	delete ExpHead; //it was a dummy
	return Exp;
}

void GmlDisplaySystem::ManageVideo(HGOSVIDEO I)
{
	if(I)
		VideoStack->Push(&I);
}

void GmlDisplaySystem::ManageImage(DWORD I)
{
	if(I)
		ImagePtrStack->Push(&I);
}

#pragma warning( default: 4701 )
#pragma warning( default: 4706 )
