/*
* $Id: console.cpp 4657 2017-08-22 18:41:12Z antonin.misek-skoda-auto.cz $
*
* Description  : One line description of file.
* Author       : Vaclav Kyba <mail/Jabber: vaseo1@gmail.com>  <ICQ: 98576293>
*
* Purpose      :
*     Long description of what the file is for.
*/


//#include <wx/button.h>
//#include <wx/sizer.h>
#include <wx/clipbrd.h>
#include <wx/dataobj.h>
//#include "flexilog.h"
#include "console.h"
//#include "common.h"
#include "kernel.h"
#include <wx/stdpaths.h>

using namespace VRUT;


Console::Console(wxWindow * parentWin, const wxString &appPath) : wxTextCtrl(parentWin, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, 20), wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxTE_NOHIDESEL | wxTE_LEFT | wxTE_BESTWRAP)
{
	helperWin = new wxStaticText(parentWin, wxID_ANY, wxEmptyString);
#ifdef __WXMSW__
	historyFilePath = wxStandardPaths::Get().GetUserLocalDataDir() + wxFileName::GetPathSeparator() + wxT("VRUT") + wxFileName::GetPathSeparator();
#else
	historyFilePath = wxStandardPaths::Get().GetUserLocalDataDir() + wxFileName::GetPathSeparator() + wxT(".VRUT") + wxFileName::GetPathSeparator();
#endif

	//create folder if not exists
	if (!wxDir::Exists(historyFilePath))
		wxDir::Make(historyFilePath);

	historyFilePath += wxT("console.log");
	// first try to parse cmd history from a local history file first
	wxFileName wxFname(historyFilePath);
	if (wxFname.FileExists())
	{
		wxInputStream * consoleLogStream = new wxFileInputStream(wxFname.GetFullPath());
		if (consoleLogStream)
		{
			TextParser textParser(consoleLogStream, 10000, 100);
			while (!textParser.IsEmpty())
				cmdHistory.push_back(textParser.GetWxLine());
			delete consoleLogStream;
		}
	}

	cmdHistoryIndex = cmdHistory.size();
	cmdHistory.push_back(wxEmptyString);
	
	Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(Console::onKeyDown), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(Console::onCommand), (wxObject *)NULL, this);
}


Console::~Console()
{
	//delete helperWin;
}

void Console::ShowHelperWin(std::vector<wxString> &strings)
{
	wxString s(strings[0]);
	for(size_t i=1; i<strings.size(); i++)
		s += wxT("\n") + strings[i];
	helperWin->SetLabel(s);
	helperWin->SetPosition(GetPosition() + wxPoint(std::max(GetSize().x - helperWin->GetSize().x, 100), 0));
	helperWin->Show();
}

void Console::onKeyDown(wxKeyEvent & evt)
{
#ifdef DEBUG_EVENTS
	LOG(wxT("<Console>key event"));
#endif

	wxString cmd = GetValue();
	if (evt.GetKeyCode() == WXK_TAB)
	{
		std::vector<wxString> candidates;
		if (!cmd.IsEmpty()) {
			bool fillParams = true;
			// first check whether it might be some command
			if (KERNEL->interpreter.GetCommandsStarting(cmd, &candidates))
			{
				cmd = candidates.front();
				for(size_t i=1; i<candidates.size(); i++)
				{
					size_t j=0;
					size_t minLength=__min(cmd.size(), candidates.at(i).size());
					while ((j<minLength) && (cmd.at(j)==candidates.at(i).at(j)))
						j++;
					cmd = cmd.substr(0, j);
				}
				cmd = cmd.BeforeFirst(' ');
				if (candidates.size()==1) {

					cmd+=wxT(" "); // finish the commands
					fillParams = false;
				}
				SetValue(cmd);
				SetInsertionPointEnd();
				cmdHistory.back() = cmd;
			}

			if (fillParams && ((cmd.Find(wxT("importscene")) != -1) || (cmd.Find(wxT("bin2c")) != -1))) {
				int index = cmd.Find(' ');

				wxString filename = cmd.substr(index+1);
				// it is already some parameter (there is a space in the cmd) so check the filenames
				// should also check the syntax of the commands, which is not done yet!!!
				std::vector<wxString> candidates;

				//			LOG(wxT("<Console>command paramer autofill on ") + filename);

				if (KERNEL->interpreter.GetFilesStarting(filename, &candidates)) {

					//			  LOG(wxString::Format(wxT("candidates %i"), candidates.size()));
					//			  for(size_t i=0; i<candidates.size(); i++)
					//				LOG(candidates[i]);

					filename = candidates.front();
					//			  LOG(filename);
					for(size_t i=1; i<candidates.size(); i++)
					{
						size_t j=0;
						size_t minLength=__min(filename.size(), candidates.at(i).size());
						while ((j<minLength) && (filename.at(j)==candidates.at(i).at(j)))
							j++;
						filename = filename.substr(0, j);
						//				  LOG(filename);
					}
					if (candidates.size()>1) {
						ShowHelperWin(candidates);
						//				for(size_t i=0; i<candidates.size(); i++)
						//				  LOG(candidates[i]);
					}

					cmd = cmd.substr(0, index+1) + filename;
					SetValue(cmd);
					SetInsertionPointEnd();
					cmdHistory.back() = cmd;
				}
			}

			if (fillParams && ((cmd.Find(wxT("runmodule")) != -1) || (cmd.Find(wxT("usemodule")) != -1) || (cmd.Find(wxT("forbidmodule")) != -1) || (cmd.Find(wxT("addrenderpass")) != -1)
				|| (cmd.Find(wxT("modhelp")) != -1) || (cmd.Find(wxT("setparam")) != -1) || (cmd.Find(wxT("getparam")) != -1)))
			{
				// find last space to detect where we perform the auto fill
				// warning - should have some more robust tokenizer...
				int index = cmd.Find(' ', true);
				// try to autofill module name
				wxString modulename = cmd.substr(index+1);
				// it is already some parameter (there is a space in the cmd) so check the filenames
				// should also check the syntax of the commands, which is not done yet!!!
				std::vector<wxFileName> mods = KERNEL->GetAvailableModules();
				std::vector<wxString> candidates;
				for (std::vector<wxFileName>::iterator it = mods.begin(); it != mods.end(); ++it)
				{
					if (it->GetName().substr(0, modulename.size())==modulename)
						candidates.push_back(it->GetName());
				}
				if (!candidates.empty()) {
					// get least common module name part
					modulename = candidates.front();
					for(size_t i=1; i<candidates.size(); i++) {
						size_t j=0;
						size_t minLength=__min(modulename.size(), candidates.at(i).size());
						while ((j<minLength) && (modulename.at(j)==candidates.at(i).at(j)))
							j++;
						modulename = modulename.substr(0, j);
					}

					cmd = cmd.substr(0, index+1) + modulename;

					if (candidates.size()>1) {
						// if more candidates are possible LOG them
						ShowHelperWin(candidates);
						//				for(size_t i=0; i<candidates.size(); i++)
						//				  LOG(candidates[i]);
					} else {
						if ((cmd.Find(wxT("setparam")) != -1) || (cmd.Find(wxT("getparam")) != -1))
						{
							if (cmd.substr(cmd.size()-2)==wxT("_d"))
								cmd=cmd.substr(0, cmd.size()-2);
						}
						else
							cmd+=wxT(" "); // add space after the module name
					}

					SetValue(cmd);
					SetInsertionPointEnd();
					cmdHistory.back() = cmd;
				}
			}
		}
	}
	else if (evt.GetKeyCode() == WXK_UP)
	{
		if (!cmdHistory.empty())
		{
			cmdHistoryIndex = __max(int(cmdHistoryIndex) - 1, 0);
			cmd = cmdHistory[cmdHistoryIndex];
			SetValue(cmd);
			SetInsertionPointEnd();
		}
	}
	else if (evt.GetKeyCode() == WXK_DOWN)
	{
		if (!cmdHistory.empty())
		{
			cmdHistoryIndex = __min(cmdHistoryIndex + 1, cmdHistory.size() - 1);
			cmd = cmdHistory[cmdHistoryIndex];
			SetValue(cmd);
			SetInsertionPointEnd();
		}
	}
	else if (evt.GetKeyCode() == WXK_ESCAPE)
	{
		Clear();
		cmdHistoryIndex = cmdHistory.size();
		cmd.Empty();
	}
	else if (evt.GetKeyCode() == WXK_BACK)
	{
		evt.Skip();
		cmd.RemoveLast();
		cmdHistory.back() = cmd;
	}
	else if (evt.GetModifiers() == wxMOD_CONTROL && (evt.GetKeyCode() == 'V'))
	{
		if (wxTheClipboard->Open())
		{
			if (wxTheClipboard->IsSupported( wxDF_TEXT ))
			{
				wxTextDataObject data;
				wxTheClipboard->GetData( data );
				wxString text(GetValue());
				long start, end;
				GetSelection(&start, &end);
				if (start == end)
					start = GetInsertionPoint();
				else
					text.Remove(start, end-start);
				text.insert(start, data.GetText());
				end = (long)text.size()-start-(long)data.GetText().size();
				int posr=text.Find('\r'), posn=text.Find('\n');
				while ((posr!=wxNOT_FOUND) || (posn!=wxNOT_FOUND))
				{
					int pos=(posr!=wxNOT_FOUND)?posr:posn;
					if (posn!=wxNOT_FOUND)
						pos=(posn<pos)?posn:pos;
					SetValue(text.substr(0, pos));
					wxCommandEvent evt;
					onCommand(evt);
					text.erase(0, pos+1);
					while ((text.size()>0) && ((text.at(0)==wxT('\r')) || (text.at(0)==wxT('\n'))))
						text.erase(0, 1);
					posr=text.Find('\r');
					posn=text.Find('\n');
				}
				SetValue(text);
				if (end>(long)text.size())
					SetInsertionPointEnd();
				else
					SetInsertionPoint((long)text.size()-end);
			}
			if (wxTheClipboard->IsSupported( wxDF_FILENAME ))
			{
				wxFileDataObject data;
				wxTheClipboard->GetData( data );
				wxArrayString fileNames=data.GetFilenames();
				wxString origValue(GetValue());
				for (size_t i=0; i<fileNames.size(); i++)
				{
					SetValue(wxT("importscene ")+fileNames[i]);
					wxCommandEvent evt;
					onCommand(evt);
				}
				SetValue(origValue);
				SetInsertionPointEnd();
			}
			wxTheClipboard->Close();
		}
	}
	else
	{
		evt.Skip();
		cmdHistory.back() = cmd;
		if (wxIsalpha(wxChar(evt.GetKeyCode())))
			cmd.Append(wxChar(evt.GetKeyCode()));
	}

	///Helper text
	std::vector<wxString> candidates;
	if (!cmd.IsEmpty() && KERNEL->interpreter.GetCommandsStarting(cmd, &candidates))
	{
		ShowHelperWin(candidates);
	}
	else
		if (evt.GetKeyCode() != WXK_TAB)
			helperWin->Hide(); // do not hide on tab - maybe some other info was displayed...
}


void Console::onCommand(wxCommandEvent & WXUNUSED(evt))
{
	const wxString cmd = GetValue();
	if (!wxString(cmd).Trim().IsEmpty())
	{
		cmdHistory.back() = cmd;
		cmdHistoryIndex = cmdHistory.size();
		KERNEL->interpreter.ProcessCommand(cmd);
		cmdHistory.push_back(wxEmptyString);
		// try to append the console command to the history file
		FILE *f = fopen(historyFilePath.c_str(), "a+");
		if (f) {
			fputs(cmd.ToAscii(), f);
			fputs("\n", f);
			fclose(f);
		}
	}
	Clear();
	helperWin->Hide();
	SetFocus();
}
