#include ".\pfrogconsole.h"


// initialize pointer
PFrogConsole* PFrogConsole::instance_ptr_ = NULL;


PFrogConsole::~PFrogConsole(void)
{
}

#ifdef CONSOLE_DEBUG

const static unsigned char charsdata[]={
	0x41,0x00,0x60,0x90,0xF0,0x90,0x90,0x00,0x04,
	0x42,0x00,0xE0,0x90,0xF0,0x90,0xE0,0x00,0x04,
	0x43,0x00,0x60,0x90,0x80,0x90,0x60,0x00,0x04,
	0x44,0x00,0xE0,0x90,0x90,0x90,0xE0,0x00,0x04,
	0x45,0x00,0xE0,0x80,0xE0,0x80,0xE0,0x00,0x03,
	0x46,0x00,0xE0,0x80,0xE0,0x80,0x80,0x00,0x03,
	0x47,0x00,0x70,0x80,0xB0,0x90,0x60,0x00,0x04,
	0x48,0x00,0x90,0x90,0xF0,0x90,0x90,0x00,0x04,
	0x49,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x01,
	0x4A,0x00,0x10,0x10,0x10,0x90,0x60,0x00,0x04,
	0x4B,0x00,0x90,0xA0,0xC0,0xA0,0x90,0x00,0x04,
	0x4C,0x00,0x80,0x80,0x80,0x80,0xE0,0x00,0x03,
	0x4D,0x00,0x88,0xD8,0xA8,0x88,0x88,0x00,0x05,
	0x4E,0x00,0x88,0xC8,0xA8,0x98,0x88,0x00,0x05,
	0x4F,0x00,0x60,0x90,0x90,0x90,0x60,0x00,0x04,
	0x50,0x00,0xE0,0x90,0xE0,0x80,0x80,0x00,0x04,
	0x51,0x00,0x60,0x90,0x90,0x90,0x60,0x00,0x04,
	0x52,0x00,0xE0,0x90,0xE0,0xA0,0x90,0x00,0x04,
	0x53,0x00,0x70,0x80,0x60,0x10,0xE0,0x00,0x04,
	0x54,0x00,0xE0,0x40,0x40,0x40,0x40,0x00,0x03,
	0x55,0x00,0x90,0x90,0x90,0x90,0x60,0x00,0x04,
	0x56,0x00,0x88,0x88,0x50,0x50,0x20,0x00,0x05,
	0x57,0x00,0x88,0xA8,0xA8,0xA8,0x50,0x00,0x05,
	0x58,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x05,
	0x59,0x00,0x88,0x50,0x20,0x20,0x20,0x00,0x05,
	0x5A,0x00,0xE0,0x20,0x40,0x80,0xE0,0x00,0x03,
	0x31,0x00,0xC0,0x40,0x40,0x40,0xE0,0x00,0x03,
	0x32,0x00,0xE0,0x10,0x60,0x80,0xF0,0x00,0x04,
	0x33,0x00,0xE0,0x10,0x60,0x10,0xE0,0x00,0x04,
	0x34,0x00,0xA0,0xA0,0xF0,0x20,0x20,0x00,0x04,
	0x35,0x00,0xF0,0x80,0xE0,0x10,0xE0,0x00,0x04,
	0x36,0x00,0x60,0x80,0xE0,0x90,0x60,0x00,0x04,
	0x37,0x00,0xF0,0x10,0x20,0x40,0x40,0x00,0x04,
	0x38,0x00,0x60,0x90,0x60,0x90,0x60,0x00,0x04,
	0x39,0x00,0x60,0x90,0x70,0x10,0x60,0x00,0x04,
	0x30,0x00,0x60,0x90,0x90,0x90,0x60,0x00,0x04,
	0x21,0x00,0x80,0x80,0x80,0x00,0x80,0x00,0x01,
	0x40,0x00,0x70,0xA8,0xB0,0x80,0x70,0x00,0x05,
	0x23,0x00,0x50,0xF8,0x50,0xF8,0x50,0x00,0x05,
	0x24,0x20,0x70,0x80,0x60,0x10,0xE0,0x40,0x04,
	0x25,0x00,0xD0,0xD0,0x20,0x58,0x58,0x00,0x05,
	0x5E,0x40,0xA0,0x00,0x00,0x00,0x00,0x00,0x03,
	0xA3,0x20,0x70,0x80,0x60,0x80,0x70,0x20,0x04,
	0x2A,0x00,0x20,0xA8,0x70,0xA8,0x20,0x00,0x05,
	0x28,0x00,0x40,0x80,0x80,0x80,0x40,0x00,0x02,
	0x29,0x00,0x80,0x40,0x40,0x40,0x80,0x00,0x02,
	0x5F,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x04,
	0x2B,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x05,
	0x60,0x00,0x80,0x40,0x00,0x00,0x00,0x00,0x02,
	0x22,0x00,0x50,0xA0,0x00,0x00,0x00,0x00,0x04,
	0x5B,0x00,0xC0,0x80,0x80,0x80,0xC0,0x00,0x02,
	0x5D,0x00,0xC0,0x40,0x40,0x40,0xC0,0x00,0x02,
	0x5C,0x00,0x80,0x80,0x40,0x20,0x20,0x00,0x03,
	0x7B,0x00,0x60,0x40,0x80,0x40,0x60,0x00,0x03,
	0x7D,0x00,0xC0,0x40,0x20,0x40,0xC0,0x00,0x03,
	0x7C,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x01,
	0x3B,0x00,0x00,0x40,0x00,0x40,0x80,0x00,0x02,
	0x3A,0x00,0x00,0x80,0x00,0x80,0x00,0x00,0x01,
	0x27,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x01,
	0x22,0x00,0xA0,0xA0,0x00,0x00,0x00,0x00,0x03,
	0x2C,0x00,0x00,0x00,0x00,0x00,0x40,0x80,0x02,
	0x2E,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x01,
	0x2F,0x00,0x20,0x20,0x40,0x80,0x80,0x00,0x03,
	0x3C,0x00,0x20,0x40,0x80,0x40,0x20,0x00,0x03,
	0x3E,0x00,0x80,0x40,0x20,0x40,0x80,0x00,0x03,
	0x3F,0x00,0xE0,0x10,0x60,0x00,0x40,0x00,0x04,
	0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,
	0x2D,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x04,
	0
};
#endif

void PFrogConsole::destroy()
{
#ifdef CONSOLE_DEBUG
	// free console lines
	struct lines *tmp;
	while (lineshead) {
		tmp=lineshead;
		lineshead=lineshead->next;
		delete tmp;
		--linescount;
	}
	struct fixedlines *tmpf;
	while (fixedlineshead) {
		tmpf=fixedlineshead;
		fixedlineshead=fixedlineshead->next;
		delete tmpf;
		--fixedlinescount;
	}
	// destroy created chars
	for(int c=0; c < 256; c++) 
		if (carsurf[c])
			delete carsurf[c];
#endif
}

void PFrogConsole::init(unsigned char curlogLevel, Display *display, Pixel charcolor, bool logOnFile, bool addLineNumber)
{
#ifdef CONSOLE_DEBUG
	_displayWidth=display->GetWidth();
	_addLineNumber=addLineNumber;
	consoleAutoIncNumber=0;
	drawStartLine=0;
	tickMouseCounter=0;
	_VersionTimeout=-100;
	LOGLEVEL=curlogLevel;
	stylusDownOnUpArrow=false;
	stylusDownOnDownArrow=false;
	stylusDownOnBackBar=false;
	consoleAddLineEnabled=true;
	unsigned char car, line[8], charwidth;
	int y,c,x;
	SESSIONID=rand()%999999999;
	consoleopened=false;
	consoleWidth=0;
	this->logHeader=true;
	// no console lines
	lineshead=NULL;
	lastline=NULL;
	linescount=0;
	fixedlineshead=NULL;
	fixedlinescount=0;
	// init charset
	for(c=0; c < 256; c++) 
		carsurf[c]=NULL;
	// extract chars info
	c=0;
	while (charsdata[c] != NULL) {
		car=charsdata[c++];
		for (y=0; y < CHARHEIGHT; y++)
			line[y]=charsdata[c++];
		charwidth=charsdata[c++];
		// for each char create surface
		carsurf[car]=display->CreateSurface(charwidth, CHARHEIGHT);
		display->SetRenderTarget(carsurf[car]);
		for (y=0; y < CHARHEIGHT; y++)
			for (x=0; x < charwidth; x++)
				display->SetPixel(x,y,Color(255,0,255));
		// build line
		for (y=0; y < CHARHEIGHT; y++) {
			if ((line[y] & 128)==128) display->SetPixel(0,y,charcolor);
			if ((line[y] & 64)==64) display->SetPixel(1,y,charcolor);
			if ((line[y] & 32)==32) display->SetPixel(2,y,charcolor);
			if ((line[y] & 16)==16) display->SetPixel(3,y,charcolor);
			if ((line[y] & 8)==8) display->SetPixel(4,y,charcolor);
			if ((line[y] & 4)==4) display->SetPixel(5,y,charcolor);
			if ((line[y] & 2)==2) display->SetPixel(6,y,charcolor);
			if ((line[y] & 1)==1) display->SetPixel(7,y,charcolor);
		} 
		// unlock surface
		display->SetRenderTarget(0);
		carsurf[car]->SetColorMask(Color(255,0,255));
	}

	// get possible log filename
	TCHAR lpFileName[512],lpFileName2[512];

	// extract exe path
	GetModuleFileName(NULL, lpFileName, 255);

	// get the last point
	for (int i=_tcslen(lpFileName)-1; i>=0; i--) {
		if ( lpFileName[i] == _T('.') ) {
			_stprintf(lpFileName2, _T("%.*s"), i+1, lpFileName); 
			break;
		}
	}

	// append .log
	_tcscat(lpFileName2, _T("log"));

	// save filename
	_tcscpy(filename, lpFileName2);

	logonfile=logOnFile;
#endif
}

void PFrogConsole::OpenConsole(bool open)
{
#ifdef CONSOLE_DEBUG
	this->consoleopened=open;
#endif
}


int PFrogConsole::drawchar(Display *display, int x, int y, char car)
{
#ifdef CONSOLE_DEBUG
	unsigned char newcar=(unsigned)toupper(car);
	if (!carsurf[newcar])
		return 0;	// char not found
	display->Blit(Point(x,y),carsurf[newcar]);
	return carsurf[newcar]->GetWidth();
#else
	return 0;
#endif
}


void PFrogConsole::drawtext(Display *display, int x, int y, char *pattern, ...)
{
#ifdef CONSOLE_DEBUG
	va_list vl;
	va_start(vl, pattern);

	char text[LINEBUFFER];
	vsprintf(text, pattern, vl);

	int xpos=x;
	if (text && strlen(text) > 0)
		for(unsigned int c=0; c < strlen(text); c++)
			xpos+=drawchar(display, xpos, y, text[c])+1;

	va_end(vl);
#endif
}

void PFrogConsole::SetLogHeader(bool s)
{
#ifdef CONSOLE_DEBUG
	this->logHeader=s;
#endif
}

void PFrogConsole::logfile(unsigned char level, char *pattern, ...)
{
#ifdef CONSOLE_DEBUG
	// don't log on file if not required
	if (!this->logonfile)
		return;

	if (LOGLEVEL < level)
		return;	// do nothing if log level specified is too low

	// open file	
	FILE *fp = _tfopen(filename, _T("a") );

	// can't open file
	if (!fp)
		return;


	va_list vl;
	va_start(vl, pattern);

	SYSTEMTIME timeInfo;
    GetLocalTime(&timeInfo);

	char text[LINEBUFFER], text2[LINEBUFFER];
	char ts[250];
	
	if (this->logHeader)
		sprintf(ts, "%09ld - [%02d/%02d/%d %02d:%02d:%02d:%03d] ", SESSIONID, timeInfo.wDay,timeInfo.wMonth,timeInfo.wYear,
		timeInfo.wHour,timeInfo.wMinute,timeInfo.wSecond,timeInfo.wMilliseconds);
	else
		strcpy(ts, "");

	vsprintf(text, pattern, vl);

	sprintf(text2, "%s%s\n", ts, text);
	fprintf(fp, text2);

	va_end(vl);

	// close file
	fclose(fp);
#endif
}

// change text for fixed line
void PFrogConsole::setfixedline(unsigned short ID, char *pattern, ...)
{
#ifdef CONSOLE_DEBUG
	// find fixed line
	struct fixedlines *tmp=fixedlineshead;
	while (tmp) {
		if (tmp->ID==ID)
			break;
		tmp=tmp->next;
	}
	if (!tmp)
		return;		// fixed line not found
	// set new text
	va_list vl;
	va_start(vl, pattern);

	char text[LINEBUFFER];
	vsprintf(text, pattern, vl);

	va_end(vl);
	strcpy(tmp->line, text);
#endif
}

// add fixed line and return generated id
unsigned short PFrogConsole::addfixedline(bool drawalways,int x, int y, char *pattern, ...)
{
#ifdef CONSOLE_DEBUG
	va_list vl;
	va_start(vl, pattern);

	char text[LINEBUFFER];
	vsprintf(text, pattern, vl);

	va_end(vl);

	struct fixedlines *fline=new fixedlines;
	fline->x=x;
	fline->y=y;
	fline->ID=this->fixedlinescount;
	fline->drawalways=drawalways;
	strcpy(fline->line, text);
	fline->next=this->fixedlineshead;
	this->fixedlineshead=fline;
	++this->fixedlinescount;
	return fline->ID;
#else
	return 0;
#endif
}

// add console line
void PFrogConsole::addconsole(unsigned char level, char *pattern, ...)
{
#ifdef CONSOLE_DEBUG
	
	if (!consoleAddLineEnabled || LOGLEVEL < level)
		return;	// do nothing if log level specified is too low or console adding is disabled

	va_list vl;
	va_start(vl, pattern);

	char text[LINEBUFFER];
	vsprintf(text, pattern, vl);

	va_end(vl);

	if (this->_addLineNumber) {	// add line number if required
		char text_tmp[LINEBUFFER+100];
		sprintf(text_tmp, "%0.5ld> %s", consoleAutoIncNumber++, text);
		strcpy(text, text_tmp);
	}

	// flag if we're watching last line
	bool weAreWatchingLastLine=false;
	if (linescount < NUMVISIBLELINES || (linescount-NUMVISIBLELINES==drawStartLine)) 
		weAreWatchingLastLine=true;

	// add line to console
	// if we reach max console number, remove first item
	if (linescount >= MAXBUFFERLINES) {
		// save head, go to next, and remove this one
		struct lines *oldhead=lineshead;
		lineshead=lineshead->next;
		delete oldhead;
		--linescount;
	}
	// now add logline
	if (!lineshead) {
		// no head yet, create head 
		lineshead=new lines();
		lineshead->next=NULL;
		lastline=lineshead;
		strcpy(lastline->line, text);
		lastline->level=level;
	} else {
		// we have head, add from last line
		struct lines *newline=new lines();
		lastline->next=newline;
		newline->next=NULL;
		lastline=newline;
		strcpy(lastline->line, text);
		lastline->level=level;
	}

	// update lines count
	++linescount;

	// if we were watching last line, update line number we're watching
	if (weAreWatchingLastLine) {
		if (linescount < NUMVISIBLELINES)
			drawStartLine=0;
		else
			drawStartLine=linescount-NUMVISIBLELINES;
	}

	// log to file if required
	this->logfile(level, text);
#endif
}


void PFrogConsole::StylusUp( Point stylus )
{
#ifdef CONSOLE_DEBUG
	stylusDownOnUpArrow=false;
	stylusDownOnDownArrow=false;
	stylusDownOnBackBar=false;
#endif
}


void PFrogConsole::StylusDown( Point stylus )
{
#ifdef CONSOLE_DEBUG
	tickMouseCounter=0;

	// open/close console if clicked on titlebar
	if (stylus.x >= 0 && stylus.x <= consoleWidth &&
		stylus.y >= 0 && stylus.y <= 8) {
			consoleopened=!consoleopened;
	} // clicked on console title
	// allow click on arrows only if we have more than max visible lines in buffer
	else if (this->consoleopened && this->linescount > NUMVISIBLELINES) {
		lastMouseY=stylus.y;
		if (stylus.x >= (signed)(_displayWidth-8) && stylus.x <= (signed)(_displayWidth-1) &&
				stylus.y >= 9 && stylus.y <= 16)
					stylusDownOnUpArrow=true;
		else if (stylus.x >= (signed)(_displayWidth-8) && stylus.x <= (signed)(_displayWidth-1) &&
				stylus.y >= 140 && stylus.y <= 147)
					stylusDownOnDownArrow=true;
		else if (stylus.x >= (signed)(_displayWidth-8) && stylus.x <= (signed)(_displayWidth-1) &&
				stylus.y >= 17 && stylus.y <= 139)
					stylusDownOnBackBar=true;
	}
#endif
}

void PFrogConsole::StylusMove( Point stylus )
{
#ifdef CONSOLE_DEBUG
	lastMouseY=stylus.y;
#endif
}


void PFrogConsole::draw(Display *display)
{
#ifdef CONSOLE_DEBUG

	// set alpha blending
	display->SetBlending(200);

	// draw console title or version info if needed
	if (_VersionTimeout < 0) {
		++_VersionTimeout;
		display->FillRect(0,0,display->GetWidth(),8,Color(150,150,150));
		this->drawtext(display, 3, 0, "PFROGCONSOLE - V%s", PFROGCONSOLE_VERSION);
	} else if (consoleopened) {
		display->FillRect(0,0,display->GetWidth(),8,Color(240,10,10));
		this->drawtext(display, 3, 0, "CONSOLE - CLICK TO CLOSE");
	} else {
		display->FillRect(0,0,display->GetWidth(),8,Color(150,150,150));
		this->drawtext(display, 3, 0, "CONSOLE - CLICK TO OPEN");
	}

	display->DrawLine(0,8,display->GetWidth(),8,Color(255,255,255));
	if (consoleopened) {
		display->FillRect(0,9,display->GetWidth(),148,Color(0,0,0));
		display->DrawLine(0,148,display->GetWidth(),148,Color(255,255,255));
		// draw console lines
		struct lines *tmp=lineshead;
		int y=10;
		
		unsigned long curDrawLine=0;
		int numdrawed=1;
		bool canDraw=false;

		while (tmp) {
			if (drawStartLine==curDrawLine++)
				canDraw=true;
			if (canDraw) {
				this->drawtext(display, 3, y, tmp->line);
				y+=8;
				if (numdrawed++==NUMVISIBLELINES)
					break;
			}
			tmp=tmp->next;
		}
		// scrollbars and arrows
		DrawScrollBars(display);
	}
	// draw fixed lines
	struct fixedlines *tmpf=fixedlineshead;
	while (tmpf) {
		if (consoleopened || tmpf->drawalways) {
			this->drawtext(display, tmpf->x, tmpf->y, tmpf->line);
		}
		tmpf=tmpf->next;
	}
	// unset alpha blending
	display->SetBlending(255);
	consoleWidth=display->GetWidth();

#endif
}


void PFrogConsole::DrawScrollBars(Display *display)
{
#ifdef CONSOLE_DEBUG
	// draw arrows
	DrawArrow(display, display->GetWidth()-8, 9, true, stylusDownOnUpArrow);
	DrawArrow(display, display->GetWidth()-8, 140, false, stylusDownOnDownArrow);
	// draw backbar
	int BACKBARY1=17, BACKBARY2=140;
	display->FillRect(display->GetWidth()-8, BACKBARY1, display->GetWidth(), BACKBARY2, Color(0,0,70));
	// calc dragbar height
	int backBarHeight=BACKBARY2-BACKBARY1;
	int dragBarHeight=backBarHeight;	// by default dragbar is full height of backbar
	int DRAGBARY=BACKBARY1;
	if (this->linescount > NUMVISIBLELINES) {
		// x:totmaxlen=numvis:maxline
		dragBarHeight=backBarHeight*NUMVISIBLELINES/this->linescount;
		if (dragBarHeight < 10)	// minimum height
			dragBarHeight=10;
		// calculate dragbar start Y according to current startvisible line
		// altezza max percorribile dalla dragbar
		int altezzaPercorribile=backBarHeight-dragBarHeight;
		// x:altezzaperc:index:max
		int YSHIFT=altezzaPercorribile*(drawStartLine)/(this->linescount-NUMVISIBLELINES);
		DRAGBARY=BACKBARY1+YSHIFT;
	}
	// draw dragbar
	Pixel drawBarColor=Color(255,255,255);
	if (this->stylusDownOnBackBar) drawBarColor=Color(180,180,255);
	display->FillRect(display->GetWidth()-8, DRAGBARY, display->GetWidth(), DRAGBARY+dragBarHeight, drawBarColor);
#endif
}


// draw arrows
void PFrogConsole::DrawArrow(Display *display, int x, int y, bool upArrow, bool clicked)
{
#ifdef CONSOLE_DEBUG
	int c, dhx, ay, rectsize=7;
	int BACKBARY1=17, BACKBARY2=140;
	int backBarHeight=BACKBARY2-BACKBARY1;

	// handle clicks and clicks ticks
	if (this->stylusDownOnDownArrow || this->stylusDownOnUpArrow) {
		// first tick or next ticks...
		if (tickMouseCounter==0 || (tickMouseCounter % 3) == 0) {
			if (stylusDownOnUpArrow && this->drawStartLine > 0) {
				--this->drawStartLine;
			} else if (stylusDownOnDownArrow && this->drawStartLine < (this->linescount-NUMVISIBLELINES)) {
				++this->drawStartLine;
			}
			tickMouseCounter=0;
		}
		++tickMouseCounter;
	} else if (this->stylusDownOnBackBar) {
		// click on backbar
		// calculate new start visible line according to current click on backbar
		// backbar go from 0 to numlines-MAXVISIBLELINES
		// (mousey-startbackY):backbarheight=x:totlines
		if (this->lastMouseY < (unsigned)BACKBARY1)
			this->drawStartLine=0;
		else
			this->drawStartLine=this->linescount*(this->lastMouseY-BACKBARY1)/backBarHeight;

		if (this->drawStartLine < 0)
			this->drawStartLine=0;
		else if (this->drawStartLine > this->linescount-NUMVISIBLELINES)
			this->drawStartLine=this->linescount-NUMVISIBLELINES;
	}

	dhx=x+2+(rectsize-2)/2;
	Pixel foreColor=Color(255,255,255);
	Pixel backColor=Color(0,0,255);
	if (clicked) {
		Pixel swapCol=foreColor;
		foreColor=backColor;
		backColor=swapCol;
	}
	display->DrawRect(x,y,x+rectsize,y+rectsize,foreColor);
	display->FillRect(x+1,y+1,x+rectsize,y+rectsize,backColor);
	if (upArrow) {
		c=0;
		for (ay=y+1; ay < y+rectsize; ay++) {
			if (dhx+c > x+rectsize)
				break;
			display->DrawLine(dhx-c, ay, dhx+c, ay, foreColor);
			c++;
		}
	} else {
		c=rectsize/2;
		for (ay=y+2; ay < y+rectsize; ay++) {
			display->DrawLine(dhx-c, ay, dhx+c, ay, foreColor);
			c--;
			if (c==0)
				break;
		}
	}
#endif
}

void PFrogConsole::EnableConsoleAddline(bool e)
{
#ifdef CONSOLE_DEBUG
	this->consoleAddLineEnabled=e;
#endif
}
