#include "Console.h"
#include "menumanager.h"

#include "..\dxfunctions\dllinterface.h"
#include "..\dxfunctions\d3dapp.h"

Console::Console(MenuManager*manager) : Menu(manager,0)
{
	m_Height = 384;
	m_Tag = "> ";
	m_Key = DIK_F1;
	m_OpenSpeed = 1000;
	m_OutputLines = 50; //in constructor its not needed to use the setfunction
	m_CommandIndex = 0;
	m_Opacity = 1;
	m_BackColor = (DWORD)0;
	m_bOpen = false;

	m_InputLine = new Input(GetManager(),0);
	m_InputLine->SetAlign(AL_RIGHTTOP);
	m_InputLine->SetPosition(0,m_Height);
	//size is set with font
	m_InputLine->SetFocusByClick(false); //if input has no focus, we can preprocess the input for it
	m_InputLine->SetText(m_Tag);
	m_InputLine->SetCursorPos(m_Tag.getlen());

	AddChild(m_InputLine);
	
	SetSize(1024,m_Height);
	SetPosition(0,-m_Height);

	SetFocus(true);
}

Console::~Console(void)
{
	m_Commands.Clear();
}

void Console::Print(XString txt)
{
	if(!txt.getlen())
		return;

	int lastret = -1;
	int ret;

	do 
	{
		//first check XString for returns
		ret = txt.instr('\n',lastret+1);
		
		if(ret == 0)
			ret = txt.getlen();

		PrintLine(txt.SubStr(lastret+1,ret-lastret));

		lastret = ret;
	}
	while(ret != txt.getlen());
		
	AlignLines();
}

void Console::PrintLine(const XString &line)
{
	if((int)m_Lines.GetElementCount() >= m_OutputLines)
		DeleteTopLine();
	AddLine()->SetText(line);
}

void Console::OnUpdate(void)
{
	//sy = value that ypos should be, that depends on open-state
	float sy = m_bOpen?-1:-m_Height-1;
	float cy = GetY();

	if(cy < sy)
	{
		cy += m_OpenSpeed * GetManager()->GetDevice()->GetApp()->GetClock().AnimMultiplier();
		if(cy > sy)
			cy = sy;
	}
	else if(cy > sy)
	{
		cy -= m_OpenSpeed * GetManager()->GetDevice()->GetApp()->GetClock().AnimMultiplier();
		if(cy < sy)
			cy = sy;
	}

	SetPosition(0,cy);
}

void Console::OnDraw(void)
{ 
	if(m_Opacity == 0 || (GetY()+GetYl()) <= 0 || !GetManager()->GetShader()->IsCompiled())
		return;

	MenuManager::ColorVertex *vb = (MenuManager::ColorVertex*)GetManager()->GetVB()->LockVB(sizeof(MenuManager::ColorVertex)*4);

	vb++->pos = DXVector4(GetX(),GetY(),0,1);
	vb++->pos = DXVector4(GetX()+GetXl(),GetY(),0,1);
	vb++->pos = DXVector4(GetX(),GetY()+GetYl(),0,1);
	vb++->pos = DXVector4(GetX()+GetXl(),GetY()+GetYl(),0,1);

	GetManager()->GetVB()->UnlockVB();
	GetManager()->GetVB()->SetVBStream(sizeof(MenuManager::ColorVertex), 0);

	GetManager()->SetupRenderColor(false,m_Opacity < 1 ? true:false,(((int)(m_Opacity*255))<24) | (m_BackColor & 0xffffff));

	RENDERFX(GetManager()->GetShader(),
		GetManager()->GetDevice()->DrawPrimitive(PT_TRIANGLESTRIP,0,2);
	);
}

void Console::OnKeyPress(BYTE key)
{
	if(m_Key == key)
		ToggleOpen();
	else if(m_bOpen)
	{
		if(key == DIK_RETURN || key == DIK_NUMPADENTER) 
		{
			if(m_InputLine->GetText().getlen() > m_Tag.getlen())
			{
				Print(m_InputLine->GetText());

				XString *cmd = new XString(m_InputLine->GetText().Right(m_InputLine->GetText().getlen() - m_Tag.getlen()));

				m_Commands.AddHead(cmd,0);

				ProcessInput(*cmd);

				m_InputLine->SetText(m_Tag);
				m_InputLine->SetCursorPos(m_Tag.getlen());
			}
		}
		else if(key == DIK_UP)
		{
			m_CommandIndex++;
			if(m_CommandIndex > (int)m_Commands.GetElementCount())
				m_CommandIndex = 1;
			if(m_Commands.GetElementCount())
			{
				m_InputLine->SetText(m_Tag + *m_Commands[m_CommandIndex-1]->GetInfo());
				m_InputLine->SetCursorPos(m_InputLine->GetText().getlen());
			}
		}
		else if(key == DIK_DOWN)
		{
			m_CommandIndex--;
			if(m_CommandIndex <= 0)
				m_CommandIndex = m_Commands.GetElementCount();
			if(m_Commands.GetElementCount())
			{
				m_InputLine->SetText(m_Tag + *m_Commands[m_CommandIndex-1]->GetInfo());
				m_InputLine->SetCursorPos(m_InputLine->GetText().getlen());
			}
		}
		else 
		{
			if(key != DIK_LEFT && key != DIK_BACKSPACE || m_InputLine->GetCursorPos() > m_Tag.getlen())
			{
				m_InputLine->OnKeyPress(key);
				m_CommandIndex = 0;
			}
		}
	}
}

void Console::SetFont(Font*font)
{
	m_InputLine->SetFont(font);
	if(font)
		m_InputLine->SetSize(1024,font->GetParam()->m_Height);

	FOREACH(el,m_Lines,ListElement<GUIElement>)
	{
		font->IncRefCount();//this is very important, dont forget it!
		((Label*)el->GetInfo()->GetInfo())->SetFont(font);
		((Label*)el->GetInfo()->GetInfo())->SetSize(1024,font->GetParam()->m_Height);
	}

	AlignLines();
}

void Console::SetCommandTag(XString tag)
{
	int oldtaglen = m_Tag.getlen();

	m_Tag = tag;

	if(m_InputLine->GetText().getlen() > oldtaglen)
		m_InputLine->SetText(m_Tag + m_InputLine->GetText().Right(m_InputLine->GetText().getlen() - oldtaglen));
	else
		m_InputLine->SetText(m_Tag);
}

void Console::SetOutputLines(int lc)
{
	if(lc < 0)
		lc = 0;

	int lcdif = m_Lines.GetElementCount() - lc;

	if(lcdif > 0)
	{	
		for(int i=0;i<lcdif;++i) //delete 
		{
			DeleteTopLine();
		}	
	}

	AlignLines();

	m_OutputLines = lc;
}

void Console::SetTextColor(D3DXCOLOR color)
{
	m_InputLine->SetTextColor(color);

	FOREACH(el,m_Lines,ListElement<GUIElement>)
	{
		((Label*)el->GetInfo()->GetInfo())->SetColor(color);
	}

}

void Console::AlignLines(void)
{
	if(m_InputLine->GetFont() && m_Lines.GetElementCount())
	{
		int i = -1;
		FOREACH(line,m_Lines,ListElement<GUIElement>)
		{
			line->GetInfo()->GetInfo()->SetPosition(0,m_Height - (m_Lines.GetElementCount()-i++) * m_InputLine->GetFont()->GetParam()->m_Height); 
		}
	}
}

Label* Console::AddLine(void)
{
	Label *lb = new Label(GetManager(),0);

	lb->SetAlign(AL_RIGHTBOTTOM);
	if(m_InputLine->GetFont())
	{
		m_InputLine->GetFont()->IncRefCount();//very important, dont forget it!
		lb->SetFont(m_InputLine->GetFont());
		lb->SetSize(1024,m_InputLine->GetFont()->GetParam()->m_Height);
	}
	lb->SetTextAlign(AL_LEFTCENTER);
	lb->SetText(m_Tag);
	//lb->SetStatic(true);

	AddChild(lb);

	m_Lines.AddTail(GetChilds().GetTail(),0);

	return lb;
}

void Console::DeleteTopLine(void)
{
	ListElement<ListElement<GUIElement>> *l = m_Lines.GetHead();
	l->GetInfo()->Delete();
	l->Unlink();
	delete l;
}

void Console::ProcessInput(const XString &inp)
{
	//split input by spaces
	//filter double-spaces and spaces on beginning
	//and count spaces to know how many XStrings we need

	int spaces = 0;
	char* filtered = new char[inp.getlen()+1];

	bool start = true;
	int firstspace = 0; //pos of first space (start of first argument), we need this later
	bool lastspace = false;
	int fi = 0;

	for(int i=0;i<inp.getlen();++i)
	{
		if(start)
		{
			if(inp.getbuffer()[i] == ' ')
				break;
			else 
			{
				filtered[fi++] = inp.getbuffer()[i];
				start = false;
			}
		}
		else
		{
			if(!lastspace || inp.getbuffer()[i] != ' ')
			{
				lastspace = false;
				filtered[fi++] = inp.getbuffer()[i];
				if(inp.getbuffer()[i] == ' ')
				{
					if(!firstspace)
						firstspace = fi-1;
					spaces++;
					lastspace = true;		
				}
			}
		}
	}

	//sign end of XString
	//if last char is space we have to cut the XString earlier
	if(filtered[fi-1] == ' ') 
	{
		filtered[fi-1] = '\0';
		spaces--;
	}
	else
	{
		filtered[fi] = '\0';
	}
	
	//Print(filtered);
	
	if(spaces)
	{
		//Print(spaces);

		XString* args = new XString[spaces];

		int iarg = 0;
		
		filtered[firstspace] = '\0';

		for(int i=firstspace+1;i<=fi;++i)
		{
			if(filtered[i] == ' ' || i == fi)//here ends current argument
			{
				filtered[i] = '\0';
				args[iarg++] = filtered + firstspace + 1;
				firstspace = i;

				//Print(args[iarg-1]);
			}
		}

		OnCommand(filtered,args,iarg);

		delete [] args;
	}
	else
	{
		OnCommand(filtered,NULL,0);
	}

	delete filtered;
}