/*
 * $Id: app.cpp 4960 2018-05-15 06:05:07Z ext.lukas.ruzicka@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 "app.h"
#include "kernel.h"
#include "flexilog.h"
#ifndef VRUT_HPC
#include "mainwindow.h"
#ifndef __WXMSW__
#include <X11/Xlib.h>
#endif
#endif
#include "eventhandler.h"
#include "imagrgb.h"
#include "scenemanager.h"
#include "wx/clipbrd.h"
//#include <wx/evtloop.h>
#include <wx/stdpaths.h>

using namespace VRUT;


#ifndef VRUT_USE_LAUNCHER
IMPLEMENT_APP(VRUT::BaseApp);
#endif

IMPLEMENT_DYNAMIC_CLASS(BaseApp, wxApp);

BaseApp::BaseApp() : wxApp()
{
#ifdef __AVX2__
	if (!Kernel::IsAVX2Supported())
	{
		wxMessageBox(wxT("This version of VRUT needs AVX2 instructions but your HW don't supports them. Most probably it will crash."), wxT("VRUT"));
	}
#elif defined __AVX__
	if (!Kernel::IsAVXSupported())
	{
		wxMessageBox(wxT("This version of VRUT needs AVX instructions but your HW don't supports them. Most probably it will crash."), wxT("VRUT"));
	}
#endif
	//wxHandleFatalExceptions();//FATAL_EXCEPTIONS: Release build provide wrong reports
#if VRUT_LICENCE_VERSION & 1
	//standard licence
	std::vector<wxString> hwInfo, networksDescriptions;
	Kernel::GetHWIdentification(hwInfo, networksDescriptions);
	if (!Kernel::IsHWLicenced(hwInfo))
	{
		wxString hwInfoStr;
		for (size_t i = 0; i < hwInfo.size(); ++i)
		{
			if (i != 0)
				hwInfoStr += wxT(", ");
			hwInfoStr += hwInfo[i];
		}
		for (auto &networksDescription : networksDescriptions)
			hwInfoStr += wxT("\n") + networksDescription;
#ifdef __WXMSW__
		wxTheClipboard->SetData(new wxTextDataObject(hwInfoStr));
		wxTheClipboard->Close();
#endif
		wxString licInfoFile(wxGetHomeDir() + wxFileName::GetPathSeparator(wxPATH_NATIVE) + wxT("VRUT_") + wxGetHostName() + wxT("_licenceInfo.txt"));
		wxFileOutputStream os(licInfoFile);
		if (os.IsOk())
		{
			os.Write(hwInfoStr.utf8_str(), strlen(hwInfoStr.utf8_str()));
			os.Close();
#ifdef __WXMSW__
			wxMessageBox(wxT("This build of VRUT is not valid for this machine.\nPlease ask to the licence: antonin.misek@skoda-auto.cz\nThe necessary information are in clipboard and in file\n")
				+ licInfoFile + wxT("\n\nVRUT není licencovaný pro tento počítač.\nPro získání licence pošlete soubor\n") + licInfoFile + wxT("\nna adresu: antonin.misek@skoda-auto.cz"), wxString::Format(("VRUT Rev. %i"), KERNEL->VRUTVersion()));
#endif
		}
#ifdef __WXMSW__
		else
			wxMessageBox(wxT("This build of VRUT is not valid for this machine.\nPlease ask to the licence: antonin.misek@skoda-auto.cz\nThe necessary information are in clipboard.\n\nVRUT není licencovaný pro tento počítač.\nPro získání licence pošlete obsah schránky na adresu: antonin.misek@skoda-auto.cz"),
				wxString::Format(("VRUT Rev. %i"), KERNEL->VRUTVersion()));
#endif
		exit(0);
	}
#endif
#if VRUT_LICENCE_VERSION & 2
	//time expiration (demo licence)
	wxString buildDateRaw(__DATE__);
	//read year
	long buildYear = 0;
	buildDateRaw.substr(buildDateRaw.size() - 4).ToLong(&buildYear);
	//read month
	wxString months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" };
	int buildMonth = 0;
	for (; buildMonth < 12; ++buildMonth)
		if (buildDateRaw.substr(0, 3) == months[buildMonth])
			break;
	//read day
	long buildDay = 0;
	buildDateRaw.substr(4).ToLong(&buildDay);
	wxDateTime buildDate(wxDateTime::Now());
	buildDate.SetYear(buildYear);
	buildDate.SetMonth((wxDateTime::Month)buildMonth);
	buildDate.SetDay(buildMonth);
	wxTimeSpan diff = wxDateTime::Now().Subtract(buildDate);
	if (diff.GetDays()>62)
	{
		buildDate += wxDateSpan(0, 0, 0, 62);
		wxMessageBox(wxT("This build of VRUT was valid to: ") + buildDate.FormatISODate() + wxT("\nPlease ask for new build:\nantonin.misek@skoda-auto.cz"), wxT("VRUT"));
		exit(0);
	}
#endif //VRUT_LICENCE_VERSION
	wxStandardPaths::Get().UseAppInfo(wxStandardPaths::AppInfo_None);
#ifdef __WXMSW__
	timeBeginPeriod(1);
#endif
#ifdef VRUT_SIGNAL_HANDLER
	typedef void (*SignalHandlerPointer)(int);
    SignalHandlerPointer previousHandler = signal(SIGSEGV, SignalHandler);
#endif
#ifndef VRUT_HPC
#ifndef __WXMSW__
	XInitThreads();
#endif
#endif
}


BaseApp::~BaseApp()
{
#ifdef __WXMSW__
	timeEndPeriod(1);
#endif
}


bool BaseApp::OnInit()
{
	wxInitAllImageHandlers();
	wxImage::AddHandler(new wxRGBHandler());
	wxString appPath;
	if (argc > 0)
	{
		wxFileName appPathF(argv[0]);
		appPath = appPathF.GetPath() + wxFileName::GetPathSeparator();
	}
#ifndef VRUT_HPC
	MainWindow * mainWin = new MainWindow(wxT("VRUT"), wxDefaultPosition, wxSize(800, 800), appPath);

	Kernel::Instantiate(mainWin, appPath);//set also app path with respect to starting command
	KERNEL->Initialize();

	SetTopWindow(mainWin);
	mainWin->SetFocus();
	mainWin->Show();

	// bind on the char hook event. This event is sent before any
	// other, so we can catch it to intercept our shortcuts
	Bind(wxEVT_CHAR_HOOK, &BaseApp::OnCharHook, this);

#else
	Kernel::Instantiate(appPath);
	KERNEL->Initialize();
#endif
	LOGDEBUG(wxT("<BaseApp>DEBUG MODE"));

	///Parse command line
	bool ret = wxApp::OnInit();

#ifndef VRUT_HPC
	///Magic fix for wxWidgets event processing
	wxKeyEvent evk;
	KERNEL->GetMessageSink()->ProcessEvent(evk);
#endif
	return ret;
}


int BaseApp::OnExit()
{
#ifndef VRUT_HPC
	Unbind(wxEVT_CHAR_HOOK, &BaseApp::OnCharHook, this);
#endif

	Kernel::Release();
	wxLog *oldLog=wxLog::SetActiveTarget(NULL);
	SAFE_DELETE(oldLog);
	return wxApp::OnExit();
}

#ifndef VRUT_HPC
void BaseApp::OnCharHook(wxKeyEvent& evt)
{
	if (!KERNEL->GetMainWindow()->HandleShortCut(evt.GetModifiers(), evt.GetKeyCode()))
	{
		// let go of the event
		// handle it by the control that has the focus 
		evt.Skip();
	}
}
#endif

void BaseApp::OnInitCmdLine(wxCmdLineParser & parser)
{
	parser.AddSwitch(wxT("h"), wxT("help"), wxEmptyString, wxCMD_LINE_OPTION_HELP);
	parser.AddParam(wxT("script_filenames"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL);
	parser.AddOption(wxT("D"));
	parser.AddOption(wxT("R"));
	///Must refuse '/' as parameter starter or cannot use "/path" style path
	parser.SetSwitchChars(wxT("-"));
}


bool BaseApp::OnCmdLineHelp(wxCmdLineParser & WXUNUSED(parser))
{
	wxMessageOutput * msgOut = wxMessageOutput::Get();
	if (msgOut)
	{
		wxString usage;
		usage << wxT("Usage: vrut [-h] [-D] [-R] [-D<param>=<value>] [<script_filenames>||<scene_filenames>]\n");
		usage << wxT(" -h, --help             \tdisplay help on the command line parameters\n");
		usage << wxT(" -D<owner.param>=<value>\tdefine parameter value,\n");
		usage << wxT(" -Rmodule_name>\trun given module,\n");
		usage << wxT("                        \tif parameter belongs to module use $moduleName.$paramName=$value (eg. '-DRenderGL.drawBVH=1')\n");
		usage << wxT("                        \tif parameter belongs to kernel use kernel.$paramName=$value (eg. '-Dkernel.evtsPerIteration=256')\n");
		msgOut->Printf(usage);
	}
	return false;
}


bool BaseApp::OnCmdLineError(wxCmdLineParser & parser)
{
	return OnCmdLineHelp(parser);
}


bool BaseApp::OnCmdLineParsed(wxCmdLineParser & parser)
{
	///Run autoexec script
	if (wxFileName::Exists(KERNEL->GetCfgPath() + wxT("autoexec.cfg")))
	{
		if (!KERNEL->interpreter.ExecuteScriptFile(KERNEL->GetCfgPath() + wxT("autoexec.cfg")))
			LOGWARNING(wxT("Could not execute user's 'autoexec.cfg' script file"));
		LOG(wxT("User's 'autoexec.cfg' script file was used"));
	}
	else
	{
		if (!KERNEL->interpreter.ExecuteScriptFile(KERNEL->GetAppPath() + wxT("autoexec.cfg")))
			LOGWARNING(wxT("Could not execute 'autoexec.cfg' script file"));
		LOG(wxT("Global 'autoexec.cfg' script file was used"));
	}

	//prepare for opening geometry files
	std::vector<SCENE_ID> IDlist;
	KERNEL->sceneManager->GetSceneIDs(IDlist);
	SCENE_ID maxSceneID = (IDlist.size()>0) ? IDlist.at(IDlist.size() - 1) + 1 : 0;

	//process all command line parameters
	for (int i = 1; i < argc; ++i)
	{
		wxString parameter = wxString::From8BitData(argv[i]);
		wxString parval;
		if (parameter.StartsWith(wxT("-D"), &parval))
		{
			Parameter::ParameterIdentifier pi = Parameter::ParameterIdentifier::FromParamName(parval.BeforeFirst('='));
			if (pi.IsValid())
				KERNEL->environment.SetParam(pi, parval.AfterFirst('='));
			else
				LOGERROR(wxT("<BaseApp>Set parameter command line argument error. Invalid parameter name: '") + parval.BeforeFirst('=') + wxT("'"));
		}
		else if (parameter.StartsWith(wxT("-R"), &parval))
		{
			KERNEL->RunModule(parval);
		}
		else
		{
			if (parameter.Matches(wxT("*.cfg")))
			{
				if (!KERNEL->interpreter.ExecuteScriptFile(parameter))
					LOGWARNING(wxT("Could not execute '") + parameter + wxT("' script file"));
			}
			else
			{
				//try to open the file as geometry
				KERNEL->GetMessageSink()->PostEvent(GET_EVT_IO_SCENE_IMPORT(parameter));
				KERNEL->GetMessageSink()->PostEvent(GET_EVT_RENDER_SCENE(SCENE_ID(maxSceneID++)));
			}
		}
	}

	return true;
}

/*
//FATAL_EXCEPTIONS: Release build provide wrong reports
void BaseApp::OnFatalException()
{
	GenerateReport(wxDebugReport::Context_Exception);
}

void BaseApp::GenerateReport(wxDebugReport::Context ctx)
{
	wxDebugReportCompress *report = new wxDebugReportCompress();
	report->AddContext(ctx);
	//report->AddAll(ctx);
	if (wxDebugReportPreviewStd().Show(*report))
	{
		if (report->Process())
		{
			wxLogMessage(wxT("Report generated in \"%s\"."),
			report->GetCompressedFileName().c_str());
			report->Reset();
		}
	}
	delete report;
}
*/

#ifdef VRUT_HPC
int BaseApp::MainLoop()
{
	while (true)
	{
		if (KERNEL->GetMessageSink()->processEvents())
			break;
	}
	return 0; 
}
#endif
