/*
*	console.cpp
*
*	Programmé par Hillaire Sébastien
*
*
*	Se référé au fichier en-tête .h pour plus de détail
*
*/

#include "Console.h"
#include "MemoryManager/DebugNew.h"

using namespace RenderSystem;

char Console::tmpString[MAX_CHAR_PER_LINE];

Console::Console(float rapport,float consoleRapport,int nbCharCharPerLine,unsigned int idTexFont)
{
	//REGLAGE DES CARACTERES
	charSize = 1.0f/(float)nbCharCharPerLine;
	glFont.setWidth(charSize);
	glFont.setHeight(charSize);
	glFont.setIdTexFont(idTexFont);

	//REGLAGES CONSOLE
	bgColor[0] = 0;		//fond noir transparent
	bgColor[1] = 0;		//fond noir transparent
	bgColor[2] = 0;		//fond noir transparent
	bgColor[3] = 0.75;	//fond noir transparent

	fgColor[0] = 0;		//caractère bleue cyan plein
	fgColor[1] = 1;		//caractère bleue cyan plein
	fgColor[2] = 1;		//caractère bleue cyan plein
	fgColor[3] = 1;		//caractère bleue cyan plein

	cmdCursorPos = -1;	//pas de commande à rechercher

	insert = true;
	cursorPos = 0;
	cursorBlinkTime = 500;
	curBlinkTime = 0;


	rapportWH = rapport;
	charPerLine = nbCharCharPerLine;
	cheight = consoleRapport;

	isActive = false;
	
	nbChar = 0;
	curLine.isUserCommand = true;
	curLine.color[0] = fgColor[0];
	curLine.color[1] = fgColor[1];
	curLine.color[2] = fgColor[2];
	curLine.color[3] = fgColor[3];
	RAZchars(curLine.cmd,MAX_CHAR_PER_LINE);
	
	//enregistrement d'une commande null et effacement
	cmdCursorPos = 0;
	execCmd(&curLine);
	sprintf(curLine.cmd,"clear");
	curLine.isUserCommand = false;
	execCmd(&curLine);
	RAZchars(&curLine.cmd[0],MAX_CHAR_PER_LINE);
	curLine.isUserCommand = true;

	
	bool linehaschange = false;
	int lastCompletionNum = true;
}

Console::~Console()
{
	cLines.clear();
	cCmds.clear();
}



void Console::keyTyped(int key)
{
	int i=0;
	int linelen=0;

	switch(key)
	{
	case CONSOLE_COMPLETION:

		if(linehaschange)
		{
			strcpy(tmpcompletion,curLine.cmd);
			linelen = strlen(curLine.cmd);
			
			linehaschange = false;

			PossibleCompletionNum.clear();

			this->DispMsg(0.75,0.75,0.75,1,"");
			this->DispMsg(0.75,0.75,0.75,1,"--- SUGGESTION ---");
			for(i=0;i<cCmds.size();i++)
			{
				if(linelen<=(int)(strlen(cCmds[i].command_name)))
				{
					//la longueur permet la comparaison

					if( memcmp(tmpcompletion, cCmds[i].command_name, linelen*sizeof(char))==0 )
					{
						//les caractère concorde
						PossibleCompletionNum.push_back(i);
						this->DispMsg(0.75,0.75,0.75,1,cCmds[i].command_name);
					}
				}
			}
		}
		
		if(PossibleCompletionNum.size()!=0)
		{
			lastCompletionNum++;
			if(lastCompletionNum>=PossibleCompletionNum.size())
			{
				lastCompletionNum = 0;
			}

			
			RAZchars(curLine.cmd, MAX_CHAR_PER_LINE);
			strcpy( curLine.cmd, cCmds[PossibleCompletionNum[lastCompletionNum]].command_name );
			cursorPos = strlen(curLine.cmd);
			nbChar = strlen(curLine.cmd);
		}
		else
		{
			this->DispMsg(0.75,0.75,0.75,1,"---  NOTHING   ---");
		}

		break;
	case CONSOLE_CLOSE:
		linehaschange = true;
		isActive = false;
		break;

	case CONSOLE_LEFT:
		if(cursorPos>0)
			cursorPos--;
		break;

	case CONSOLE_RIGHT:
		if(cursorPos<nbChar)
			cursorPos++;
		break;

	case CONSOLE_UP:
		linehaschange = true;
		if(cLines.size()!=0)
		{
			int tmp = cmdCursorPos;
			do
			{
				tmp--;
				if(tmp<0)
				{
					tmp = cLines.size()-1;
				}
			}while( (cLines[tmp].isUserCommand==false) );
			cmdCursorPos=tmp;

			//on copie la commande
			RAZchars(&curLine.cmd[0],MAX_CHAR_PER_LINE);
			strcpy(curLine.cmd,cLines[cmdCursorPos].cmd);
			curLine.color[0] = cLines[cmdCursorPos].color[0];
			curLine.color[1] = cLines[cmdCursorPos].color[1];
			curLine.color[2] = cLines[cmdCursorPos].color[2];

			nbChar = strlen(curLine.cmd);
			cursorPos = strlen(curLine.cmd);
		}
		break;

	case CONSOLE_DOWN:
		linehaschange = true;
		if(cLines.size()!=0)
		{
			int tmp = cmdCursorPos;
			do
			{
				tmp++;
				if(tmp>(int)(cLines.size())-1)
				{
					tmp = 0;
				}
			}while( (cLines[tmp].isUserCommand==false) );
			cmdCursorPos=tmp;

			//on copie la commande
			RAZchars(&curLine.cmd[0],MAX_CHAR_PER_LINE);
			strcpy(curLine.cmd,cLines[cmdCursorPos].cmd);
			curLine.color[0] = cLines[cmdCursorPos].color[0];
			curLine.color[1] = cLines[cmdCursorPos].color[1];
			curLine.color[2] = cLines[cmdCursorPos].color[2];

			nbChar = strlen(curLine.cmd);
			cursorPos = strlen(curLine.cmd);
		}
		break;

	case CONSOLE_SUPPR_BACK:
		linehaschange = true;
		if(cursorPos>0)
		{
			for(i=cursorPos-1;i<nbChar+1;i++)
			{
				curLine.cmd[i] = curLine.cmd[i+1];
			}
			nbChar--;
			cursorPos--;
		}
		break;

	case CONSOLE_SUPPR:
		linehaschange = true;
		if( cursorPos<(nbChar) )
		{
			for(i=cursorPos;i<nbChar;i++)
			{
				curLine.cmd[i] = curLine.cmd[i+1];
			}
			curLine.cmd[i]='\0';
			nbChar--;
		}
		break;

	case CONSOLE_INSERT:
		insert=!insert;
		if(insert)
		{
			if(cursorPos>MAX_CHAR_PER_LINE-2)
				cursorPos = MAX_CHAR_PER_LINE-2;
		}
		else
		{
			if(cursorPos>MAX_CHAR_PER_LINE-2)
				cursorPos = MAX_CHAR_PER_LINE-2;
		}
		break;

	case CONSOLE_HOME:
		cursorPos = 0;
		break;

	case CONSOLE_END:
		cursorPos = nbChar;
		break;

	case CONSOLE_PG_UP:
		//TODO
		break;

	case CONSOLE_PG_DOWN:
		//TODO
		break;

	case CONSOLE_VALIDATE:
		linehaschange = true;
		cmdCursorPos = 0;
		execCmd(&curLine);
		RAZchars(curLine.cmd, MAX_CHAR_PER_LINE);
		break;


	default:
		linehaschange = true;
		cmdCursorPos = 0;

		if(insert)
		{
			//on prend le caractère seulement si on peut encor en insérer
			if(nbChar<(MAX_CHAR_PER_LINE-1))
			{
				//pour chaque caractère au dela du curseur : on décale d'un (char) vers la droite
				for(int i=nbChar;i>cursorPos-1;i--)
				{
					curLine.cmd[i] = curLine.cmd[i-1];
				}
				curLine.cmd[cursorPos] = (char)key;

				if(nbChar<(MAX_CHAR_PER_LINE-2))
					nbChar++;

				if(cursorPos<MAX_CHAR_PER_LINE-2)
					cursorPos++;
			};
		}
		else
		{
			//enregistrement du caractère
			curLine.cmd[cursorPos] = (char)key;

			//on incrémente le nb de char seulement si on est en fin de ligne
			if( (cursorPos==nbChar) && (nbChar<(MAX_CHAR_PER_LINE-2)) )
				nbChar++;

			if(cursorPos<(MAX_CHAR_PER_LINE-2))
				cursorPos++;
		}


		break;

	}
}

void Console::addCommand(char *name, void (*command_func)(char*, Console*))
{
	Console_Command ccm;

	//allocation de la mémoire pour le nom et affectation
	/*ccm.command_name = new char(strlen(name)+1);			//fait planter
	strcpy(ccm.command_name,name);*/
	ccm.command_name = name;

	//affectation du pointeur de fonction à la commande
	ccm.command_func = command_func;

	//ajout de la commande dans la listes
	cCmds.push_back(ccm);
}

void Console::render()
{
	//réglage de la projection 2D
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(0,1,0,rapportWH);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	glDepthFunc(GL_ALWAYS);


	//le fond
	if(bgColor[4]<1)
	{
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
	glBegin(GL_QUADS);
	glColor4fv(bgColor);
	glVertex2f(1,rapportWH);
	glVertex2f(1,rapportWH*cheight);
	glVertex2f(0,rapportWH*cheight);
	glVertex2f(0,rapportWH);
	glEnd();

	glDisable(GL_BLEND);


	//le bord
	glLineWidth(7);
	glColor4fv(fgColor);
	glBegin(GL_LINES);
	glVertex2f(0,rapportWH*cheight);
	glVertex2f(1,rapportWH*cheight);
	glEnd();


	//rendu des lignes de commandes
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc( GL_GREATER, 0);			//on ne laise passez que les fragments qui modifieront le buffer
	glFont.setProjection2DForGLFont2D(0,1,0,rapportWH);
	glLineWidth(3);
	this->renderLines();
	glFont.unsetProjection2DForGLFont2D();
	glDisable(GL_ALPHA_TEST);
	glDisable(GL_BLEND);


	//récupération des projections précedentes
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	glDepthFunc(GL_LESS);
}

void Console::update(unsigned int time)
{
	curBlinkTime += time;

	while(curBlinkTime>2*cursorBlinkTime)
	{
		curBlinkTime-=2*cursorBlinkTime;
	}
}

bool Console::isOpen()
{
	return isActive;
}

void Console::Open()
{
	cursorPos = 0;

	nbChar = 0;
	curLine.color[0] = fgColor[0];
	curLine.color[1] = fgColor[1];
	curLine.color[2] = fgColor[2];
	curLine.color[3] = fgColor[3];
	RAZchars(&curLine.cmd[0],MAX_CHAR_PER_LINE);

	isActive = true;
}

void Console::RAZchars(char *c, int len)
{
	for(int i = 0 ; i < len ; i++)
		c[i]='\0';
}

void Console::execCmd(Console_Line *cl)
{
	bool cmdExist = false;

	//on ajoute la commande courante au vector
	cLines.push_back(*cl);

	//std::cout<<"commande \""<<cl->cmd<<"\" executee."<<std::endl;
	sscanf(cl->cmd,"%s",ctmp);

	for(int i=0;i<cCmds.size();i++)
	{
		if(strcmp(ctmp,cCmds[i].command_name)==0)
		{
			cmdExist = true;
			cCmds[i].command_func(cl->cmd, this);
		}
	}


	if(strcmp(ctmp,"help")==0)
	{
		cmdExist = true;
		DispMsg(0,1,0,1," ");
		DispMsg(0,1,0,1,"commands :");
		DispMsg(0,1,0,1," ");
		DispMsg(0,1,0,1,"- clear");
		DispMsg(0,1,0,1,"- help");
		DispMsg(0,1,0,1," ");

		for(int i=0;i<cCmds.size();i++)
		{
			sprintf(ctmp,"- %s",cCmds[i].command_name);
			DispMsg(0,1,0,1,ctmp);
		}
		DispMsg(0,1,0,1," ");
	}
	if(strcmp(ctmp,"clear")==0)
	{
		cmdExist = true;
	}

	if(!cmdExist)
	{
		sprintf(ctmp,"  unknown command : '%s'",cl->cmd);
		DispMsg(1,0,0,1,ctmp);
	}


	//on remet à zéro la commande courante
	cursorPos = 0;
	nbChar = 0;
	curLine.color[0] = fgColor[0];
	curLine.color[1] = fgColor[1];
	curLine.color[2] = fgColor[2];
	curLine.color[3] = fgColor[3];
	RAZchars(&curLine.cmd[0],MAX_CHAR_PER_LINE);
}

void Console::renderLines()
{
	int j,i;
	float tx = 0;				//la x position de début
	float ty = cheight*rapportWH+0.03f;//la y position de début
	int nbChar;					//le nombre de ligne prit par la commande courante
	int nbLine;					//le nombre de caractère prit par la commande courante
	char *cmd;					//le pointeur sue la commande. à manipuler
	float finalx;
	float finaly;

	/*** affichage de curLine ***/
	cmd = &curLine.cmd[0];
	nbChar = strlen(curLine.cmd);
	nbLine =(nbChar/charPerLine);//on calcul le nb de ligne pour l'affichage de la commande
	ty+=nbLine*charSize;		//on ce décal en hauteur pour laffichage en descendant
	glFont.renderText2D(0, ty,"%c",'>');
	glColor4fv(curLine.color);
	for(i=0;i<nbChar;i++)		//+2 car on affiche "> " comme prompt
	{
		finalx = charSize+tx-((1+i)/charPerLine)*charPerLine*charSize;
		finaly = ty-(float)((1+i)/charPerLine)*charSize;

		glFont.renderText2D(finalx, finaly,"%c",*cmd);

		if(i == cursorPos)
		{
			if( ((float)curBlinkTime/(float)(cursorBlinkTime))>1.0f )
			{
				glColor4fv(this->fgColor);
				glDisable(GL_TEXTURE_2D);
				glBegin(GL_LINES);
				glVertex2f(finalx,finaly);
				if(insert)
				{
					glVertex2f(finalx,finaly+charSize);
				}
				else
				{
					glVertex2f(finalx+charSize,finaly);
				}
				glEnd();
				glEnable(GL_TEXTURE_2D);
			}
		}

		tx+=charSize;
		cmd++;
	}
	if(i == cursorPos)
	{
		finalx = charSize+tx-((1+i)/charPerLine)*charPerLine*charSize;
		finaly = ty-(float)((1+i)/charPerLine)*charSize;
		if( ((float)curBlinkTime/(float)(cursorBlinkTime))>1.0f )
		{
			glColor4fv(this->fgColor);
			glDisable(GL_TEXTURE_2D);
			glBegin(GL_LINES);
			glVertex2f(finalx,finaly);
			if(insert)
			{
				glVertex2f(finalx,finaly+charSize);
			}
			else
			{
				glVertex2f(finalx+charSize,finaly);
			}
			glEnd();
			glEnable(GL_TEXTURE_2D);
		}
	}
	ty+= charSize;
	tx = 0;


	/*** affichage des lignes dans le vecteur ***/
	for(j=cLines.size()-1;j>=0;j--)
	{
		if( strcmp(cLines[j].cmd,"clear")==0 )				//on arrete si on atteint une commande clear
			break;

		cmd = cLines[j].cmd;
		nbChar = strlen(cLines[j].cmd);
		nbLine =(nbChar/charPerLine);	//on calcul le nb de ligne pour l'affichage de la commande
		ty+=nbLine*charSize;			//on ce décal en hauteur pour laffichage en descendant
		glColor4fv(cLines[j].color);
		for(i=0;i<nbChar;i++)
		{
			finalx = tx-(i/charPerLine)*charPerLine*charSize;
			finaly = ty-(float)(i/charPerLine)*charSize;

			glFont.renderText2D(finalx, finaly,"%c",*cmd);

			tx+=charSize;
			cmd++;
		}
		tx=0;
		ty+=charSize;

		if( finaly>rapportWH )				//on arrete si on atteint le haut de l'écran
			break;
	}
}


void Console::DispMsg(float r,float g,float b,float a,char* msg)
{
	if(strlen(msg)>MAX_CHAR_PER_LINE-1)
	{
		this->DispMsg(1.0f,0.0f,0.0f,0.5f,"AFFICHAGE TROP LONG POUR LA CONSOLE!!!");
		return;
	}

	Console_Line cl;
	cl.isUserCommand = false;
	cl.color[0]=r;
	cl.color[1]=g;
	cl.color[2]=b;
	cl.color[3]=a;
	strcpy(cl.cmd,msg);
	cLines.push_back(cl);
}


void Console::DispMsg(float r,float g,float b,float a, const char *chaine, ...)
{
	// liste récupérant les information du paramètre ...
	va_list		argsPtr;
	// on met les inforamtion de ... dans le tableau text.
	va_start(argsPtr, chaine);
	vsprintf(tmpString, chaine, argsPtr);
	va_end(argsPtr);
	//tmpcompletion contient la chaine

	if(strlen(chaine)>MAX_CHAR_PER_LINE)
	{
		this->DispMsg(1.0f,0.0f,0.0f,0.5f,"AFFICHAGE TROP LONG POUR LA CONSOLE!!!");
		return;
	}

	Console_Line cl;
	cl.isUserCommand = false;
	cl.color[0]=r;
	cl.color[1]=g;
	cl.color[2]=b;
	cl.color[3]=a;
	strcpy(cl.cmd,tmpString);
	cLines.push_back(cl);
}



