/*
 * $Id: kernel.cpp 4944 2018-05-03 08:06:59Z 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 "kernel.h"
//#include "flexilog.h"
#include "iomanager.h"
#include "manipmanager.h"
#include "generalmanager.h"
//#include "guienvironment.h"
#ifndef VRUT_HPC
#include "rendermanager.h"
#else
#include "OGLconstants.h"
#endif
#ifdef __WXMSW__
#include <iphlpapi.h>
#endif
#pragma comment(lib, "IPHLPAPI.lib")


#ifdef VRUT_SIGNAL_HANDLER
void SignalHandler(int signal)
{
	switch (signal)
	{
	case SIGSEGV:
		throw "Access Violation";
	default:
		throw "Unknown signal";
		break;
	}
}
#endif

using namespace VRUT;

Kernel * Kernel::instance = (Kernel *)NULL;

#ifdef VRUT_HPC
Kernel * Kernel::Instantiate(const wxString &appPath)
{
	if (!Kernel::instance)
		Kernel::instance = new Kernel(appPath);

	return Kernel::instance;
}
#else
Kernel * Kernel::Instantiate(MainWindow * window, const wxString &appPath)
{
	if (!Kernel::instance)
		Kernel::instance = new Kernel(window, appPath);

	return Kernel::instance;
}
#endif

void Kernel::Release()
{
	SAFE_DELETE(instance);
}

#ifdef VRUT_HPC
Kernel::Kernel(const wxString &appPath) : appPath(appPath), environment(), interpreter()
#else
Kernel::Kernel(MainWindow * window, const wxString &appPath) : appPath(appPath), mainWindow(window), environment(), interpreter()
#endif
{
#ifdef __WXMSW__
	LARGE_INTEGER f;
	VRUTGetTimeHasHRTimer = QueryPerformanceFrequency(&f) ? true : false;
	VRUTGetTimeFrequency = 1000000.0/((double)f.QuadPart);
#endif

	initialized = exiting = false;
	mainThreadID = wxThread::GetCurrentId();
	ioManager = (IOManager *)NULL;
	sceneManager = (SceneManager *)NULL;
	manipManager = (ManipulatorManager *)NULL;
	generalManager = (GeneralManager *)NULL;
	eventHandler = (EventHandler *)NULL;
#ifdef VRUT_HPC
	wxLog::SetActiveTarget(new FlexiLog(appPath));
#else
	renderManager = (RenderManager *)NULL;
#endif
	flexiLog = dynamic_cast<FlexiLog *>(wxLog::GetActiveTarget());
	VRUTConfig = NULL;
#ifdef __WXMSW__
	cfgPath = wxStandardPaths::Get().GetUserLocalDataDir() + wxFileName::GetPathSeparator() + wxT("VRUT") + wxFileName::GetPathSeparator();
#else
	cfgPath = wxStandardPaths::Get().GetUserLocalDataDir() + wxFileName::GetPathSeparator() + wxT(".VRUT") + wxFileName::GetPathSeparator();
#endif
}


Kernel::~Kernel()
{
	exiting = true;

	//Because of current ReleaseManagerData() implementation, the sceneManager has to be released first. But other modules depend on scene data and these
	//modules run in parallel, unrestricted. So when sceneManager->ReleaseManagerData() is done, the other modules still run and will inevitably crash
	//in their run() methods where they ask for sceneNodes. Therefore, we need to terminate all modules' main loops before we start to release the resources
	//and this is why StopModules() calls are here.
#ifndef VRUT_HPC
	renderManager->StopModules();
#endif
	generalManager->StopModules();
	ioManager->StopModules();
	manipManager->StopModules();

	//Watch out for the order here. When the dll gets unloaded, the heap space of that dll is no longer available to the exe (that means to the Kernel).
	//So for example when the fhs importer allocates a new scene node using the "new" keyword and then you unload the dll using the
	//ioManager->ReleaseManagerData() you can not free it from the kernel anymore. So if you, for example, called ioManager->ReleaseManagerData()
	//sooner than sceneManager->ReleaseManagerData(), the scene destruction in sceneManager->ReleaseManagerData() would fail (with a first chance 
	//exception) because the smart_pointer release of sceneNodes would be trying to delete from a heap space owned by an already unloaded iofhs.dll.
	//Maybe TODO: For us to be able to call sceneManager->ReleaseManagerData() last, we would need to deffer dll unloading to another pass (that means
	//releasing resources associated to all modules first and then unload dlls of all modules), so that the heap is not tampered by dll unloads while
	//we release resources.
	sceneManager->ReleaseManagerData();
#ifndef VRUT_HPC
	renderManager->ReleaseManagerData();
#endif
	generalManager->ReleaseManagerData();
	manipManager->ReleaseManagerData();
	ioManager->ReleaseManagerData();
	//eventHandler->ReleaseManagerData();
	//at this point nothing must depend on any manager
	SAFE_DELETE(sceneManager);
#ifndef VRUT_HPC
	SAFE_DELETE(renderManager);
#endif
	SAFE_DELETE(generalManager);
	SAFE_DELETE(manipManager);
	SAFE_DELETE(ioManager);
	SAFE_DELETE(eventHandler);
	SAFE_DELETE(VRUTConfig);
	LOGVERBOSE(wxT("<Kernel>Deinitialized"));
}


Module * Kernel::GetModule(const MODULE_ID & id) const
{
	switch (id.GetServerID())
	{
	case MANIP_MANAGER:
		return manipManager->getModule(id);
	case IO_MANAGER:
		return ioManager->getModule(id);
#ifndef VRUT_HPC
	case RENDER_MANAGER:
		return renderManager->getModule(id);
#endif
	case GENERAL_MANAGER:
		return generalManager->getModule(id);
	default:
		LOGERROR(wxT("<Kernel>Unknown module server type, module ID '")+id.ToString()+wxT("'"));
		return (Module *)NULL;
	}
}

MODULE_ID Kernel::RunModule(const wxString & moduleName) const
{
	MODULE_ID ret = generalManager->RunModule(moduleName, false);
	if (ret != MODULE_ID_NONE)
		return ret;
	ret = ioManager->RunModule(moduleName, false);
	if (ret != MODULE_ID_NONE)
		return ret;
#ifndef VRUT_HPC
	ret = renderManager->RunModule(moduleName, false);
	if (ret != MODULE_ID_NONE)
		return ret;
#endif
	ret = manipManager->RunModule(moduleName, false);
	if (ret == MODULE_ID_NONE)
		LOGERROR(wxT("<Kernel::RunModule>Module '")+moduleName+wxT("' could not be started"));
	return ret;
}

void Kernel::ReleaseModule(const MODULE_ID & id)
{
	switch (id.GetServerID())
	{
	case MANIP_MANAGER:
		manipManager->releaseModule(id);
		break;
	case IO_MANAGER:
		ioManager->releaseModule(id);
		break;
#ifndef VRUT_HPC
	case RENDER_MANAGER:
		renderManager->releaseModule(id);
		break;
#endif
	case GENERAL_MANAGER:
		generalManager->releaseModule(id);
		break;
	default:
		LOGERROR(wxT("<Kernel>Unknown module server type, module ID '")+id.ToString()+wxT("'"));
		break;
	}
}


std::vector<wxFileName> Kernel::GetAvailableModules() const
{
	std::vector<wxFileName> mods;
	if (IsInitialized())
	{
		std::vector<wxFileName> mods1 = ioManager->GetAvailableModules();
		mods.insert(mods.end(), mods1.begin(), mods1.end());
#ifndef VRUT_HPC
		mods1 = renderManager->GetAvailableModules();
		mods.insert(mods.end(), mods1.begin(), mods1.end());
#endif
		mods1 = manipManager->GetAvailableModules();
		mods.insert(mods.end(), mods1.begin(), mods1.end());
		mods1 = generalManager->GetAvailableModules();
		mods.insert(mods.end(), mods1.begin(), mods1.end());
	}
	else
		LOGERROR(wxT("<Kernel>Kernel not initialized"));

	return mods;
}


bool Kernel::Initialize()
{
	//load configuration file
	if (wxFile::Exists(KERNEL->GetCfgPath() + wxT("vrut.gui")))
	{
		wxFileInputStream is(KERNEL->GetCfgPath() + wxT("vrut.gui"));
		VRUTConfig = new wxFileConfig(is);
	}
	else if (wxFile::Exists(appPath+wxT("vrut.gui")))
	{
		wxFileInputStream is(appPath+wxT("vrut.gui"));
		VRUTConfig = new wxFileConfig(is);
	}
	else
		VRUTConfig = new wxFileConfig();
	eventHandler = new EventHandler();
	ioManager = new IOManager();
	sceneManager = new SceneManager();
	manipManager = new ManipulatorManager();
	generalManager = new GeneralManager();
#ifndef VRUT_HPC
	renderManager = new RenderManager();
	GUIEnvironment * guiEnv = new GUIEnvironment(mainWindow->GetSubWindowParent());
	if (eventHandler && ioManager && renderManager && sceneManager && manipManager && generalManager)
	{
		///Notebook for parameters
		guiEnv->SetName(PARAMS_NOTEBOOK_NAME);
		mainWindow->GetAUIManager()->AddPane(guiEnv, wxAuiPaneInfo().DefaultPane().Name(PARAMS_NOTEBOOK_NAME).Caption(PARAMS_NOTEBOOK_NAME).Bottom().Layer(0).PinButton().BestSize(480, 170));
#ifndef VRUT_HPC
		guiEnv->SetDropTarget(new DropTarget(mainWindow, DropTarget::FILE));
#endif

		mainWindow->SetUserPerspective(VRUTConfig);
		initialized = true;

		mainWindow->InitModulesToolbar(guiEnv);

		LOGVERBOSE(wxT("<Kernel>Initialized"));
		return true;
	}
	delete guiEnv;
	return false;
#else
	initialized = true;
	LOGVERBOSE(wxT("<Kernel>Initialized"));
	return true;
#endif
}


bool Kernel::GetModules(std::vector<std::pair<Module *, MODULE_INSTANCE_ID> > * moduleList, const wxString & moduleName, MODULE_INSTANCE_ID instanceID)
{
	bool ret = false;
	if (IsInitialized())
	{
		ret |= ioManager->GetModules(moduleList, moduleName, instanceID);
#ifndef VRUT_HPC
		ret |= renderManager->GetModules(moduleList, moduleName, instanceID);
#endif
		ret |= manipManager->GetModules(moduleList, moduleName, instanceID);
		ret |= generalManager->GetModules(moduleList, moduleName, instanceID);
	}
	else
		LOGERROR(wxT("<Kernel>Kernel not initialized"));

	return ret;
}


void Kernel::PreferModule(const wxString & libName)
{
	preferedModules.erase(std::remove_if(preferedModules.begin(), preferedModules.end(), [&] (wxString preferedModule)
		{ return preferedModule.IsSameAs(libName, false); }), preferedModules.end());
	preferedModules.insert(preferedModules.begin(), CloneWxString(libName));
	forbiddenModules.erase(std::remove_if(forbiddenModules.begin(), forbiddenModules.end(), [&] (wxString forbiddenModule)
		{ return forbiddenModule.IsSameAs(libName, false); }), forbiddenModules.end());
}


void Kernel::ForbidModule(const wxString & libName)
{
	forbiddenModules.erase(std::remove_if(forbiddenModules.begin(), forbiddenModules.end(), [&] (wxString forbiddenModule)
		{ return forbiddenModule.IsSameAs(libName, false); }), forbiddenModules.end());
	forbiddenModules.insert(forbiddenModules.begin(), CloneWxString(libName));
	preferedModules.erase(std::remove_if(preferedModules.begin(), preferedModules.end(), [&] (wxString preferedModule)
		{ return preferedModule.IsSameAs(libName, false); }), preferedModules.end());
}


bool Kernel::IsForbidden(const wxString & libName) const
{
	for (auto &name : forbiddenModules)
		if (name.IsSameAs(libName, false) || name.IsSameAs(wxT("lib") + libName, false))
			return true;
	return false;
}

bool Kernel::InitGlew()
{
#ifndef VRUT_HPC
	return glewInit() == GLEW_OK;
#else
	return false;
#endif
}

bool Kernel::IsAVXSupported()
{
	//  Misc.
	bool HW_MMX;
	bool HW_x64;
	bool HW_LZCNT;
	bool HW_RDRAND;
	bool HW_BMI1;
	bool HW_BMI2;
	bool HW_ADX;

	//  SIMD: 128-bit
	bool HW_SSE;
	bool HW_SSE2;
	bool HW_SSE3;
	bool HW_SSSE3;
	bool HW_SSE41;
	bool HW_SSE42;
	bool HW_SSE4a;
	bool HW_AES;
	bool HW_SHA;

	//  SIMD: 256-bit
	bool HW_AVX=false;
	bool HW_XOP;
	bool HW_FMA3;
	bool HW_FMA4;
	bool HW_AVX2;

	//  SIMD: 512-bit
	bool HW_AVX512F;
	bool HW_AVX512PF;
	bool HW_AVX512ER;
	bool HW_AVX512CD;

	int info[4];
	cpuid(info, 0, 0);
	int nIds = info[0];
	// Capture vendor string
	char vendor[0x20];
	memset(vendor, 0, sizeof(vendor));
	*reinterpret_cast<int*>(vendor) = info[1];
	*reinterpret_cast<int*>(vendor + 4) = info[3];
	*reinterpret_cast<int*>(vendor + 8) = info[2];

	cpuid(info, 0x80000000, 0);
	unsigned nExIds = info[0];

	//  Detect Features
	if (nIds >= 0x00000001){
		cpuid(info,0x00000001, 0);
		HW_MMX    = (info[3] & ((int)1 << 23)) != 0;
		HW_SSE    = (info[3] & ((int)1 << 25)) != 0;
		HW_SSE2   = (info[3] & ((int)1 << 26)) != 0;
		HW_SSE3   = (info[2] & ((int)1 <<  0)) != 0;

		HW_SSSE3  = (info[2] & ((int)1 <<  9)) != 0;
		HW_SSE41  = (info[2] & ((int)1 << 19)) != 0;
		HW_SSE42  = (info[2] & ((int)1 << 20)) != 0;
		HW_AES    = (info[2] & ((int)1 << 25)) != 0;

		HW_AVX    = (info[2] & ((int)1 << 28)) != 0;
		HW_FMA3   = (info[2] & ((int)1 << 12)) != 0;

		HW_RDRAND = (info[2] & ((int)1 << 30)) != 0;
	}
	if (nIds >= 0x00000007){
		cpuid(info,0x00000007, 0);
		HW_AVX2   = (info[1] & ((int)1 <<  5)) != 0;

		HW_BMI1   = (info[1] & ((int)1 <<  3)) != 0;
		HW_BMI2   = (info[1] & ((int)1 <<  8)) != 0;
		HW_ADX    = (info[1] & ((int)1 << 19)) != 0;

		HW_AVX512F  = (info[1] & ((int)1 << 16)) != 0;
		HW_AVX512PF = (info[1] & ((int)1 << 26)) != 0;
		HW_AVX512ER = (info[1] & ((int)1 << 27)) != 0;
		HW_AVX512CD = (info[1] & ((int)1 << 28)) != 0;
		HW_SHA      = (info[1] & ((int)1 << 29)) != 0;
	}
	if (nExIds >= 0x80000001){
		cpuid(info, 0x80000001, 0);
		HW_x64   = (info[3] & ((int)1 << 29)) != 0;
		HW_LZCNT = (info[2] & ((int)1 <<  5)) != 0;
		HW_SSE4a = (info[2] & ((int)1 <<  6)) != 0;
		HW_FMA4  = (info[2] & ((int)1 << 16)) != 0;
		HW_XOP   = (info[2] & ((int)1 << 11)) != 0;
	}
	char brand[0x40];
    memset(brand, 0, sizeof(brand));
	if (nExIds >= 0x80000004)
	{
		cpuid(info, 0x80000002, 0);
		memcpy(brand, info, sizeof(int)*4);
		cpuid(info, 0x80000003, 0);
		memcpy(brand + 16, info, sizeof(int)*4);
		cpuid(info, 0x80000004, 0);
		memcpy(brand + 32, info, sizeof(int)*4);
	}
	return HW_AVX;
}

bool Kernel::IsAVX2Supported()
{
	int info[4];
	cpuid(info, 0, 0);
	bool HW_AVX2 = false;
	if (info[0] >= 0x00000007) {
		cpuid(info, 0x00000007, 0);
		HW_AVX2 = (info[1] & ((int)1 << 5)) != 0;
	}
	return HW_AVX2;
}

void Kernel::GetHWIdentification(std::vector<wxString> &hwInfo, std::vector<wxString> &networksDescriptions)
{
	hwInfo.clear();
	networksDescriptions.clear();
#ifdef __WXMSW__
	//workaround for wxWidgets bug in wxGetHostName on Windows 10
	TCHAR buffer[256] = TEXT("");
	DWORD dwSize = sizeof(buffer);
	if (GetComputerNameEx(ComputerNameDnsFullyQualified, buffer, &dwSize))
		hwInfo.push_back(wxString(buffer));
	else
		hwInfo.push_back(wxGetFullHostName());
#else
	hwInfo.push_back(wxGetFullHostName());
#endif // __WXMSW__

	int info[4];
	cpuid(info, 0x80000000, 0);
	unsigned nExIds = info[0];
	if (nExIds < 0x80000004)
		return;
	char brand[0x40];
	memset(brand, 0, sizeof(brand));
	if (nExIds >= 0x80000004)
	{
		cpuid(info, 0x80000002, 0);
		memcpy(brand, info, sizeof(int) * 4);
		cpuid(info, 0x80000003, 0);
		memcpy(brand + 16, info, sizeof(int) * 4);
		cpuid(info, 0x80000004, 0);
		memcpy(brand + 32, info, sizeof(int) * 4);
	}
	hwInfo.push_back(wxString::From8BitData(brand));

#ifdef __WXMSW__
	IP_ADAPTER_INFO AdapterInfo[16];       // Allocate information for up to 16 NICs
	DWORD dwBufLen = sizeof(AdapterInfo);  // Save memory size of buffer

	DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen);
	assert(dwStatus == ERROR_SUCCESS);  // Verify return value is valid, no buffer overflow

	PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo; // Contains pointer to current adapter info
	do
	{
		hwInfo.push_back(wxString::Format(wxT("%02X-%02X-%02X-%02X-%02X-%02X"), (int)pAdapterInfo->Address[0], (int)pAdapterInfo->Address[1], (int)pAdapterInfo->Address[2], (int)pAdapterInfo->Address[3],
			(int)pAdapterInfo->Address[4], (int)pAdapterInfo->Address[5]));
		networksDescriptions.push_back(wxString::From8BitData(pAdapterInfo->Description));
		pAdapterInfo = pAdapterInfo->Next;    // Progress through linked list, Terminate if last adapter
	} while (pAdapterInfo);
#else
	wxDir dir(wxT("/sys/class/net"));
	if (dir.IsOpened())
	{
		wxString filename;
		bool cont = dir.GetFirst(&filename);
		while (cont)
		{
			wxFileInputStream is(wxT("/sys/class/net/")+filename+wxT("/address"));
			if (is.IsOk())
			{
				char buff[100];
				is.Read(buff, sizeof(buff));
				hwInfo.push_back(wxString::From8BitData(buff).Trim());
			}
		    cont = dir.GetNext(&filename);
		}
	}
	//motherboard
	// /sys/class/dmi/id/board_serial
#endif
}

#if VRUT_LICENCE_VERSION
#define VRUT_CHECK_LICENCE_FILE
//#define VRUT_SIGN_LICENCE_FILE
//#define VRUT_GENERATE_LICENCING_KEYS
#ifdef VRUT_CHECK_LICENCE_FILE
#include "openssl/evp.h"
#include "openssl/rsa.h"
#include "publickey.txt.h"
#endif
#ifdef VRUT_SIGN_LICENCE_FILE
#include "openssl/evp.h"
#include "openssl/rsa.h"
#include "privatekey.txt.h"
#endif
//#include "openssl\err.h"
#endif

#ifdef VRUT_CHECK_LICENCE_FILE
bool Kernel::CheckLicenceFile(const wxString &licenceFilePath, const std::vector<wxString> &hwInfo)
{
	///// Check signature of licence file
	if (!wxFileExists(licenceFilePath))
		return false;
	wxFileInputStream fistream(licenceFilePath);
	if (!fistream.IsOk())
		return false;
	TextParser textParser(&fistream, 100000, 1000);
	wxString licenceString, signature;
	const char *keywords[] = { "VRUT licence file version", "--------------------------------------------------", "" };
	int blockID = 0;
	bool validLicenceFound = false;
	while (!textParser.IsEmpty())
	{
		switch (textParser.KeywordsSwitch(keywords))
		{
		case 0://VRUT licence file version
		{
			int major, minor;
			textParser.ParseInt(&major);
			textParser.SkipBytes(1);
			textParser.ParseInt(&minor);
			if ((major != 1) || (minor != 0))
				LOGERROR(wxT("KERNEL: Unsupported licence file version"));
			break;
		}
		case 1://--------------------------------------------------
			++blockID;
			break;
		default:
			if (blockID == 1)
			{
				wxString licenceLine(textParser.GetWxLine());
				licenceString += licenceLine + wxT("\n");
				//split licence and check if all parts are in real HW description
				TextParser textParserLine(licenceLine.utf8_str());
				bool licenceValid = true;
				while (!validLicenceFound)
				{
					if (std::find(hwInfo.begin(), hwInfo.end(), textParserLine.GetWxStringUtf8(",")) == hwInfo.end())
					{
						licenceValid = false;
						break;//early skip: currently processed licence is not valid
					}
					if (textParserLine.IsEmpty("", false))
					{
						validLicenceFound = true;//we checked all parts of current licence and all was OK
						break;
					}
					textParserLine.SkipBytes(2);
				}
			}
			else
				signature += textParser.GetWxLine();
			break;
		}
	}
	if (!validLicenceFound)
		return false;

	size_t mlen = strlen(licenceString.utf8_str());
	char* msg = (char*)malloc(mlen + 1);
	strncpy(msg, licenceString.utf8_str(), mlen);
	msg[mlen] = 0;

	byte *sig = (byte*)malloc(signature.size() * 6 / 8 + 1);
	size_t slen = 0;
	//base64 decoding
	const char *base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

	unsigned int packet = 0;
	unsigned int packetBits = 0;
	for (size_t i = 0; i < signature.size(); ++i)
	{
		packet += (strchr(base64Table, (char)signature.GetChar(i)) - base64Table) << packetBits;
		packetBits += 6;
		while (packetBits > 8)
		{
			sig[slen] = packet & 255;
			++slen;
			packet = packet >> 8;
			packetBits -= 8;
		}
	}
	//if (packetBits > 0)
	//{
	//	sig[slen] = packet & 255;
	//	++slen;
	//}

	const unsigned char *ccc = publickey_txt;
	EVP_PKEY *vkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ccc, sizeof(publickey_txt));

	EVP_MD_CTX *mdctx = NULL;
	if (!(mdctx = EVP_MD_CTX_create()))
		return false;


	// Initialize `key` with a public key
	if (1 != EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, vkey))
		return false;

	// Initialize `key` with a public key
	if (1 != EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg)))
		return false;

	if (1 == EVP_DigestVerifyFinal(mdctx, sig, slen))
	{
		// Success
		return true;
	}
	else
	{
		return false;
		//Failure
	}
	///// END OF Check signature of licence file
}
#endif //VRUT_CHECK_LICENCE_FILE

bool Kernel::IsHWLicenced(std::vector<wxString> &hwInfo)
{
#if VRUT_LICENCE_VERSION

#ifdef VRUT_SIGN_LICENCE_FILE
	///// Sign licence file
	wxFileInputStream fistream(wxT("VRUT_licence_input.txt"));
	TextParser textParser(&fistream, 100000, 1000);
	wxString licenceString;
	while (!textParser.IsEmpty())
	{
		wxString line = textParser.GetWxLine();
		if (line.substr(0, 2) == wxT("//"))
			continue;
		size_t commentPos = line.find(wxT("//"));
		if (commentPos != wxString::npos)
			line = line.substr(0, commentPos);
		licenceString += line + wxT("\n");
	}

	size_t mlen = strlen(licenceString.utf8_str());
	char* msg = (char*)malloc(mlen + 1);
	strncpy(msg, licenceString.utf8_str(), mlen);
	msg[mlen] = 0;

	const unsigned char *ccc = privatekey_txt;
	EVP_PKEY *skey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &ccc, sizeof(privatekey_txt));

	EVP_MD_CTX *mdctx = NULL;
	if (!(mdctx = EVP_MD_CTX_create()))
		return false;

	byte *sig = NULL;
	size_t slen = 0;

	if (1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, skey))
		return false;

	// Call update with the message
	if (1 != EVP_DigestSignUpdate(mdctx, msg, strlen(msg)))
		return false;

	// Finalize the DigestSign operation
	// First call EVP_DigestSignFinal with a NULL sig parameter to obtain the length of the * signature. Length is returned in slen
	if (1 != EVP_DigestSignFinal(mdctx, NULL, &slen))
		return false;
	// Allocate memory for the signature based on size in slen
	if (!(sig = (byte*)OPENSSL_malloc(sizeof(unsigned char) * slen)))
		return false;
	// Obtain the signature
	if (1 != EVP_DigestSignFinal(mdctx, sig, &slen))
		return false;

	wxFileOutputStream fostream(wxT("VRUT_licence.txt"));
	wxString header(wxT("VRUT licence file version 1.0\n"));
	wxString delimiter(wxT("--------------------------------------------------\n"));
	fostream.Write(header.utf8_str(), strlen(header.utf8_str()));
	fostream.Write(delimiter.utf8_str(), strlen(delimiter.utf8_str()));
	fostream.Write(licenceString.utf8_str(), strlen(licenceString.utf8_str()));
	fostream.Write(delimiter.utf8_str(), strlen(delimiter.utf8_str()));
	//base64 encoding
	unsigned int packet = 0;
	unsigned int packetBits = 0;
	wxString signature;
	char oneletter[2];
	oneletter[1] = 0;
	const char *base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	for (size_t i = 0; i < slen; ++i)
	{
		packet += sig[i] << packetBits;
		packetBits += 8;
		while (packetBits>6)
		{
			oneletter[0] = base64Table[packet & 63];
			signature += wxString::From8BitData(oneletter);
			packet = packet >> 6;
			packetBits -= 6;
		}
	}
	if (packetBits > 0)
	{
		oneletter[0] = base64Table[packet & 63];
		signature += wxString::From8BitData(oneletter);
	}
	fostream.Write(signature.utf8_str(), strlen(signature.utf8_str()));
	fostream.Close();
	///// END OF Sign licence file
	exit(0);
#endif //VRUT_SIGN_LICENCE_FILE

#ifdef VRUT_CHECK_LICENCE_FILE
	if (!CheckLicenceFile(wxT("VRUT_licence.txt"), hwInfo))
	{
		if (!CheckLicenceFile(GetCommonCfgPath() + wxT("VRUT_licence.txt"), hwInfo))
			return false;
	}
	return true;
#endif //VRUT_CHECK_LICENCE_FILE

#ifdef VRUT_GENERATE_LICENCING_KEYS
	///// Create Sign and Verify HMAC keys
	EVP_PKEY *skey = NULL, *vkey = NULL;
	RSA* rsa = NULL;
	do
	{
		skey = EVP_PKEY_new();
		if (skey == NULL) {
			break; // failed
		}
		vkey = EVP_PKEY_new();
		if (vkey == NULL) {
			break; //failed
		}
		rsa = RSA_generate_key(2048, RSA_F4, NULL, NULL);
		assert(rsa != NULL);
		if (rsa == NULL) {
			break; // failed 
		}
		// Set signing key
		int rc = EVP_PKEY_assign_RSA(skey, RSAPrivateKey_dup(rsa));
		if (rc != 1) {
			break; // failed
		}
		// Sanity check. Verify private exponent is present
		// assert(EVP_PKEY_get0_RSA(skey)->d != NULL);

		// Set verifier key
		rc = EVP_PKEY_assign_RSA(vkey, RSAPublicKey_dup(rsa));
		assert(rc == 1);
		if (rc != 1) {
			break; // failed
		}
		// Sanity check. Verify private exponent is missing 
		// assert(EVP_PKEY_get0_RSA(vkey)->d == NULL);
	} while (0);

	if (rsa) {
		RSA_free(rsa);
		rsa = NULL;
	}

	//save private key
	unsigned char *aaa = NULL;
	int outSize = i2d_PrivateKey(skey, &aaa);
	FILE *fp = fopen("C:/temp/privatekey.txt", "wb");
	fwrite(aaa, 1, outSize, fp);
	fclose(fp);

	//save public key
	aaa = NULL;
	outSize = i2d_PublicKey(vkey, &aaa);
	fp = fopen("C:/temp/publickey.txt", "wb");
	fwrite(aaa, 1, outSize, fp);
	fclose(fp);
	///// END OF Create Sign and Verify HMAC keys
#endif //VRUT_GENERATE_LICENCING_KEYS

	/*	///// Load keys from file
	//load private key
	unsigned char bbb[2000];
	FILE *fp = fopen("C:/temp/privatekey.txt", "rb");
	int outSize = fread(bbb, 1, 2000, fp);
	fclose(fp);
	const unsigned char *ccc = bbb;
	EVP_PKEY *skey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &ccc, outSize);

	//load public key
	fp = fopen("C:/temp/publickey.txt", "rb");
	outSize = fread(bbb, 1, 2000, fp);
	fclose(fp);
	ccc = bbb;
	EVP_PKEY *vkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ccc, outSize);
	///// END OF Load keys from file
	*/

	///// RSA encrypt and decrypt
	//http://stackoverflow.com/questions/15050191/openssl-how-to-store-and-recover-keys-to-encrypt-and-decrypt-messages
#endif //VRUT_LICENCE_VERSION
	return false;
}

std::shared_ptr<FontGlyphsTexture> Kernel::GetOrCreateFont(wxFont font)
{
	wxCriticalSectionLocker lock(_fontLock);

	for (auto &fnt : _fonts)
		if (fnt.first == font)
			return fnt.second;

	auto glyphs = std::make_shared<FontGlyphsTexture>();
	glyphs->Create(font, 1024, 32);			// Create 1024 glyphs
	_fonts.push_back(std::make_pair(font, glyphs));

	return glyphs;
}

int Kernel::VRUTVersion()
{
	return 4943;
}