/*
 * $Id: eventhandler.cpp 4917 2018-04-16 13:02:30Z 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 "eventhandler.h"
#include "kernel.h"
#include "generalmanager.h"
#include "iomanager.h"

#ifndef VRUT_HPC
#include "rendermanager.h"
#include "manipmanager.h"
#endif


using namespace VRUT;

EventHandler::EventHandler() :
#ifndef VRUT_HPC
	wxEvtHandler(),
#endif
	cloneSceneDataParamID(wxT("kernel"), wxT("cloneSceneData"), wxT("0"), wxT("Clone scene data in events.")),
	evtsPerIterationParamID(wxT("kernel"), wxT("evtsPerIteration"), wxT("0"), wxT("Number of events processed in one iteration of main thread.")),
	logSyncEveryMsgParamID(wxT("kernel"), wxT("logSyncEveryMsg"), wxT("0"), wxT("Save log messages to file immediatelly.")),
	fastExitParamID(wxT("kernel"), wxT("fastExit"), wxT("0"), wxT("Use exit(0) for end of application.\n(It is not \"correct\" but it is fast.)")),
	presentationModeParamID(wxT("kernel"), wxT("presentationMode"), wxT("0"), wxT("Turn presentaion mode on/off."))
#ifndef VRUT_HPC
	,iconsSizeParamID(wxT("kernel"), wxT("iconsSize"), wxT("0"), wxT("Size of icons used in toolbar."))
#endif
{
	wxString cloneSceneDataTmp(wxT("0"));
	cloneSceneDataTmp=KERNEL->environment.SetParam(cloneSceneDataParamID, cloneSceneDataTmp);
	cloneSceneData = isTrue(cloneSceneDataTmp);
#ifndef VRUT_HPC
	GUIEnvironment::GUIParamInfo pi(cloneSceneDataParamID, GUIEnvironment::GUIParamInfo::GUI_TYPE_CHECKBOX);
	PostEvent(GET_EVT_PARAM_GUIREGISTER(pi, cloneSceneDataTmp));
#endif

	wxString evtsPerIterationTmp(wxT("128"));
	evtsPerIterationTmp=KERNEL->environment.SetParam(evtsPerIterationParamID, evtsPerIterationTmp);
	TextParser(evtsPerIterationTmp.utf8_str()).ParseInt(&evtsPerIteration);
#ifndef VRUT_HPC
	GUIEnvironment::GUIParamInfo pi2(evtsPerIterationParamID, GUIEnvironment::GUIParamInfo::GUI_TYPE_SPINCONTROL);
	pi2.minval = 1;
	pi2.maxval = 10000;
	PostEvent(GET_EVT_PARAM_GUIREGISTER(pi2, evtsPerIterationTmp));
#endif

	wxString logSyncEveryMsgTmp(wxT("0"));
	logSyncEveryMsgTmp=KERNEL->environment.SetParam(logSyncEveryMsgParamID, logSyncEveryMsgTmp);
	logSyncEveryMsg=isTrue(logSyncEveryMsgTmp);
#ifndef VRUT_HPC
	GUIEnvironment::GUIParamInfo pi4(logSyncEveryMsgParamID, GUIEnvironment::GUIParamInfo::GUI_TYPE_CHECKBOX);
	PostEvent(GET_EVT_PARAM_GUIREGISTER(pi4, logSyncEveryMsgTmp));
#endif

	wxString fastExitTmp(wxT("1"));
	fastExitTmp=KERNEL->environment.SetParam(fastExitParamID, fastExitTmp);
	fastExit = isTrue(fastExitTmp);
#ifndef VRUT_HPC
	GUIEnvironment::GUIParamInfo pi5(fastExitParamID, GUIEnvironment::GUIParamInfo::GUI_TYPE_CHECKBOX);
	PostEvent(GET_EVT_PARAM_GUIREGISTER(pi5, fastExitTmp));
#endif

	wxString presentationModeTmp(wxT("0"));
	presentationModeTmp = KERNEL->environment.SetParam(presentationModeParamID, presentationModeTmp);
	presentationMode = isTrue(presentationModeTmp);
#ifndef VRUT_HPC
	GUIEnvironment::GUIParamInfo pi6(presentationModeParamID, GUIEnvironment::GUIParamInfo::GUI_TYPE_CHECKBOX);
	PostEvent(GET_EVT_PARAM_GUIREGISTER(pi6, presentationModeTmp));

	wxString iconsSizeTmp(wxString::Format(wxT("32")));
	iconsSizeTmp = KERNEL->environment.SetParam(iconsSizeParamID, iconsSizeTmp);
	int iconsSizeI;
	if (TextParser(iconsSizeTmp.utf8_str()).ParseInt(&iconsSizeI))
		iconsSize = iconsSizeI;
	iconsSize = std::max(std::min(iconsSize, 16), 256);
	GUIEnvironment::GUIParamInfo pi7(iconsSizeParamID, GUIEnvironment::GUIParamInfo::GUI_TYPE_TEXTCONTROL);
	PostEvent(GET_EVT_PARAM_GUIREGISTER(pi7, iconsSizeTmp));
#endif

	///Basic events
#ifndef VRUT_HPC
	KERNEL->GetMainWindow()->Connect(wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(EventHandler::onIdle), (wxObject *)NULL, this);

	///Key events
	Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(EventHandler::onKeyEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_KEY_UP, wxKeyEventHandler(EventHandler::onKeyEvent), (wxObject *)NULL, this);

	///Mouse events
	//TODO: can be replaced with single 'MOUSE_EVENTS' Connect call?
	Connect(wxID_ANY, wxEVT_ENTER_WINDOW, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_LEAVE_WINDOW, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_LEFT_DOWN, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_LEFT_UP, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_LEFT_DCLICK, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_RIGHT_DOWN, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_RIGHT_UP, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_RIGHT_DCLICK, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_MIDDLE_DOWN, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_MIDDLE_UP, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_MIDDLE_DCLICK, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_MOTION, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
	Connect(wxID_ANY, wxEVT_MOUSEWHEEL, wxMouseEventHandler(EventHandler::onMouseEvent), (wxObject *)NULL, this);
#endif

	moduleListeners.resize(Event::EVT_HIGHEST);
	threadPoolCount=0;
	// create threadPool threads
	int numThreads = __max(1, wxThread::GetCPUCount());
	for (int i = 0; i < numThreads; i++)
	{
		threadPool.push_back(new ThreadPoolThread(*this));
		if (threadPool.back()->Create() == wxTHREAD_NO_ERROR)
			threadPool.back()->Run();
	}
	LOGVERBOSE(wxString::Format(wxT("<EventHandler>Thread pool count: %i"), numThreads));

	// start event handler thread
	eventHandlerThread = new EventHandlerThread(*this);
	if (eventHandlerThread->Create() == wxTHREAD_NO_ERROR) 
		eventHandlerThread->Run();
	//	== wxTHREAD_NO_ERROR);
	eventHandlerThreadID=eventHandlerThread->GetId();
	eventHandlerThreadIsRunning=true;
}


EventHandler::~EventHandler()
{
	for (size_t i=0; i<threadPool.size(); ++i)
		threadPool[i]->exit=true;
	//weak up every thread to exit immediately
	class ThreadPoolEmptyJob : public ThreadPoolJob
	{
		public:
			ThreadPoolEmptyJob() : ThreadPoolJob() {}
			virtual ~ThreadPoolEmptyJob() {}
			virtual void Run() {}
	};
	for (size_t i=0; i<threadPool.size(); ++i)
		threadPoolQueue.Post(new ThreadPoolEmptyJob());

	while (threadPoolCount>0)
		wxThread::Sleep(1);
	/*//detached threads remove itself so it is not necessary now to Delete them this way
	for (size_t i=0; i<threadPool.size(); ++i)
	{
		if (threadPool.front()->IsRunning())
			threadPool.front()->Delete();
		//SAFE_DELETE(threadPool.front());
	}*/
	threadPool.clear();

	//TODO: is correct to kill threadList on VRUT exit?
	wxCriticalSectionLocker lock(threadListMutex);
	for (size_t i=0; i<threadList.size(); ++i)
	{
		if (threadList.front()->IsRunning())
			threadList.front()->Delete();
	}
	threadList.clear();

	if (eventHandlerThread)
		if (eventHandlerThread->IsRunning())
			eventHandlerThread->Delete();
	while (eventHandlerThreadIsRunning)
		wxThread::Sleep(1);

	///This prevents calling process idle from main window
	//KERNEL->GetMainWindow()->Disconnect(wxID_ANY, wxEVT_IDLE);
#ifndef VRUT_HPC
	Disconnect();
#endif
	moduleListeners.clear();

	///clean thread pool queue
	while (!threadPoolQueue.IsEmpty())
	{
		ThreadPoolJob *job = threadPoolQueue.ReceiveTimeout(0);
		if (job)
			delete job;
	}

	while (!mainThreadQueue.IsEmpty())
	{
		Event *evt = mainThreadQueue.ReceiveTimeout(0);
		if (evt)
		{
			if (evt->type == Event::EVT_LOG)
				processEvent(evt);
			ReleaseEvent(evt);
		}
	}
	while (!evtQueue.IsEmpty())
	{
		Event *evt = evtQueue.ReceiveTimeout(0);
		if (evt)
		{
			if (evt->type == Event::EVT_LOG)
				processEvent(evt);
			ReleaseEvent(evt);
		}
	}
#ifndef VRUT_HPC
	///Do not try to delete connected handlers
	SetNextHandler((wxEvtHandler *)NULL);
#endif
}


void EventHandler::RegisterListener(const MODULE_ID & id, const Event::EVENT_TYPE eventType)
{
	if (eventType >= Event::EVT_HIGHEST)
		LOGERROR(wxT("<EventHandler>Error registering listener, invalid event type"));
	else
	{
		ModuleIDList * midList = &moduleListeners[eventType];
		for (auto &it : *midList)
			if (it == id)
				return;
		midList->push_back(id);
	}
}


void EventHandler::UnregisterListener(const MODULE_ID & id, const Event::EVENT_TYPE eventType)
{
	///Unregister 1 type of event
	if (eventType < Event::EVT_HIGHEST)
	{
		ModuleIDList * midList = &moduleListeners[eventType];
		for (ModuleIDList::iterator it = midList->begin(); it != midList->end(); )
		{
			if (*it == id)
				it = midList->erase(it);
			else
				++it;
		}
	}
	else  ///Unregister all
	{
		for (auto &it : moduleListeners)
		{
			for (ModuleIDList::iterator mit = it.begin(); mit != it.end();)
			{
				if (*mit == id)
					mit = it.erase(mit);
				else
					++mit;
			}
		}
	}
}

void EventHandler::ProcessEventsFrom(MODULE_HANDLE_ID hid)
{
	/*std::deque<wxCommandEvent> temp;
	MessageQueueError err;
	while (!evtQueue.IsEmpty())
		temp.push_back(evtQueue.ReceiveTimeout(0, err));
	for (std::deque<wxCommandEvent>::iterator it = temp.begin(); it != temp.end(); it++)
	{
		if (it->GetClientData() && it->GetExtraLong() && MODULE_ID(it->GetExtraLong()).GetHandleID() == hid)
		{
			if (!KERNEL->IsExiting() || it->GetEventType() == Event::EVT_LOG)
				processEvent(*it);
			ReleaseEvent(*it);
		}
		else
			PostEvent(*it);
	}*/
}

// this method processes events in the main application thread
bool EventHandler::processEvents()
{
#ifdef VRUT_HPC
	Event *evt = mainThreadQueue.ReceiveTimeout(100);
	if (evt)
	{
		if (!KERNEL->IsExiting() || evt->type == Event::EVT_LOG)
		{
			processEvent(evt);
			DistributeToListeners(evt);
		}
		else if (evt->type == Event::EVT_EXIT)
		{
			ReleaseEvent(evt);
			return true;//true means that application should stop
		}
		else
			ReleaseEvent(evt);
		///Now we're safe - no memory allocated on module heap
		///residing in event structure
	}
	return false;
#else
	int msgProcessed = 0;
	while (!mainThreadQueue.IsEmpty() && (msgProcessed++ < evtsPerIteration))
	{
		///Check for pending events sent from modules
		Event *evt = mainThreadQueue.ReceiveTimeout(0);
		if (evt)
		{
			if (!KERNEL->IsExiting() || evt->type == Event::EVT_LOG)
			{
				processEvent(evt);
				DistributeToListeners(evt);
			}
			else
				ReleaseEvent(evt);
			///Now we're safe - no memory allocated on module heap
			///residing in event structure
		}
	}
	return (msgProcessed <= evtsPerIteration);
#endif
}

#ifndef VRUT_HPC
void EventHandler::onIdle(wxIdleEvent & evt)
{
	if (!processEvents())
		evt.RequestMore();
}


void EventHandler::onKeyEvent(wxKeyEvent & evt)
{
	Event *kevt=GET_EVT_INPUT_KEY(evt);
#ifdef DEBUG_EVENTS
	DebugEvent(kevt, "key");
#endif
	DistributeToListeners(kevt);
	//Crtl+Alt+Shift+C to activate Console
	if ((evt.GetUnicodeKey()==wxT('C') || evt.GetUnicodeKey()==wxT('c')) && evt.AltDown() && evt.ControlDown() && evt.ShiftDown() && (evt.GetEventType()==wxEVT_KEY_UP))
	{
		//LOG(wxT("EventHandler Console focus"));
		KERNEL->GetMainWindow()->SetFocusToConsole();
	}
	evt.Skip();
}


void EventHandler::onMouseEvent(wxMouseEvent & evt)
{
	Event *mevt=GET_EVT_INPUT_MOUSE(evt);
#ifdef DEBUG_EVENTS
	DebugEvent(mevt, "mouse");
#endif
	DistributeToListeners(mevt);
	evt.Skip();
}
#endif // VRUT_HPC


void EventHandler::processEvent(Event *evt)
{
#ifdef DEBUG_EVENTS
	DebugEvent(evt, "EventHandler::processEvent");
#endif
	if (KERNEL->IsExiting())
		return;

	switch (evt->type)
	{
	case Event::EVT_EXIT:
		{
#ifndef VRUT_HPC
			wxCloseEvent closeEvt(wxEVT_CLOSE_WINDOW, KERNEL->GetMainWindow()->GetId());
			closeEvt.SetEventObject(KERNEL->GetMainWindow());
			KERNEL->GetMainWindow()->GetEventHandler()->AddPendingEvent(closeEvt);
#endif
		}
		break;
	case Event::EVT_LOG:
		FlexiLog::LogMessageFlexi(((EventLog*)evt)->msg, ((EventLog*)evt)->loglevel);
		break;
	case Event::EVT_LOG_LEVEL_SET:
		FlexiLog::SetLogLevel(((EventLogLevelSet*)evt)->loglevel);
#ifndef VRUT_HPC
		KERNEL->GetMainWindow()->FixLoglevelMenu(((EventLogLevelSet*)evt)->loglevel);
#endif
		break;
	case Event::EVT_PARAM_SET_DO:
		KERNEL->environment.SetParam(((EventParamSetDo*)evt)->paramIdentifier, ((EventParamSetDo*)evt)->value);
		break;
	case Event::EVT_PARAM_SET:
		UPDATE_PARAM_FROM_EVENT_BOOL(cloneSceneDataParamID, cloneSceneData, evt);
		UPDATE_PARAM_FROM_EVENT_INT(evtsPerIterationParamID, evtsPerIteration, evt);
		if (((EventParamSet*)evt)->paramIdentifier == logSyncEveryMsgParamID)
		{
			UPDATE_PARAM_FROM_EVENT_BOOL(logSyncEveryMsgParamID, logSyncEveryMsg, evt);
			FlexiLog::GetInstance()->SyncEveryMsg(logSyncEveryMsg);
		}
		UPDATE_PARAM_FROM_EVENT_BOOL(fastExitParamID, fastExit, evt);
		UPDATE_PARAM_FROM_EVENT_BOOL(presentationModeParamID, presentationMode, evt);

#ifndef VRUT_HPC
		if (((EventParamSet*)evt)->paramIdentifier == iconsSizeParamID)
		{
			UPDATE_PARAM_FROM_EVENT_INT(iconsSizeParamID, iconsSize, evt);
			iconsSize = std::min(std::max(iconsSize, 16), 256);
			KERNEL->GetMainWindow()->ResizeIcons(iconsSize);
		}
		if (((EventParamSet*)evt)->originId != GUIEnvironment::setParamOriginId)
		{
			((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->SetParam(((EventParamSet*)evt)->paramIdentifier, ((EventParamSet*)evt)->value);
		}
#endif
		if (((EventParamSet*)evt)->paramIdentifier == presentationModeParamID)
			PostEvent(GET_EVT_PRESENTATION_MODE(presentationMode));
		break;
	case Event::EVT_PARAM_REGISTER:
		KERNEL->environment.SetParam(((EventParamRegister*)evt)->paramIdentifier, ((EventParamRegister*)evt)->value);
		break;
	case Event::EVT_PARAM_UNREGISTER:
		KERNEL->environment.UnregisterParam(((EventParamUnregister*)evt)->paramIdentifier);
		break;
#ifndef VRUT_HPC
	case Event::EVT_PARAM_GUIREGISTER:
		((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->RegisterGUIParam(((EventParamGuiRegister*)evt)->paramInfo, ((EventParamGuiRegister*)evt)->value);
		break;
	case Event::EVT_PARAM_GUIUNREGISTER:
		((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->HideParams(((EventParamGuiUnregister*)evt)->paramIdentifier, true);
		break;
	case Event::EVT_PARAM_GUIENABLE:
		((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->EnableGUIControl(((EventParamGuiEnable*)evt)->paramInfo, ((EventParamGuiEnable*)evt)->enable);
		break;
	case Event::EVT_PARAM_GUISHOW:
		((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->ShowGUIControl(((EventParamGuiShow*)evt)->paramInfo, ((EventParamGuiShow*)evt)->show);
		break;
	case Event::EVT_PARAM_GUI_BEGIN_GROUP:
		((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->BeginGUIParamGroup(((EventParamGuiBeginGroup*)evt)->paramGroupInfo);
		break;
	case Event::EVT_PARAM_GUI_SET_LAYOUT:
		((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->SetLayout(((EventParamGuiSetLayout*)evt)->paramIdentifier, ((EventParamGuiSetLayout*)evt)->layoutFileName);
		break;
#endif
	case Event::EVT_MODULE_PREFER:
		KERNEL->PreferModule(((EventModulePrefer*)evt)->moduleName);
#ifndef VRUT_HPC
		if (KERNEL->GetMainWindow())
			KERNEL->GetMainWindow()->ResizeIcons();
#endif
		break;
	case Event::EVT_MODULE_FORBID:
		KERNEL->ForbidModule(((EventModuleForbid*)evt)->moduleName);
#ifndef VRUT_HPC
		if (KERNEL->GetMainWindow())
			KERNEL->GetMainWindow()->ResizeIcons();
#endif
		break;
	case Event::EVT_MODULE_RELEASE:
		KERNEL->ReleaseModule(((EventModuleRelease*)evt)->moduleID);
		break;
#ifndef VRUT_HPC
	case Event::EVT_MODULE_RELEASED:
		if (KERNEL->GetMainWindow())
			KERNEL->GetMainWindow()->ModuleToolbarUpdate(((EventModuleReleased*)evt)->moduleID);
		break;
#endif
	case Event::EVT_MODULE_RUN:
		KERNEL->RunModule(((EventModuleRun*)evt)->moduleName);
		break;
#ifndef VRUT_HPC
	case Event::EVT_MODULE_INSTANTIATED:
		if (KERNEL->GetMainWindow())
			KERNEL->GetMainWindow()->ModuleToolbarUpdate(((EventModuleInstantiated*)evt)->moduleID);
		break;
#endif
	case Event::EVT_SCENE_IMAGE_ADD:
		ProcessEventSceneImageAdd((EventSceneImageAdd *)evt);
		break;
	case Event::EVT_IO_SCENE_IMPORT:
		KERNEL->ioManager->ImportScene(((EventIoSceneImport*)evt)->sceneFile, ((EventIoSceneImport*)evt)->sceneID, ((EventIoSceneImport*)evt)->rootID);
		break;
	case Event::EVT_IO_SCENE_EXPORT:
	{
		smart_pointer_v2<Scene> scene;
		KERNEL->sceneManager->GetScene(scene, ((EventIoSceneExport*)evt)->sceneID);
		if (!scene)
			LOGERROR(wxT("<EventHandler>Invalid scene ID in event"));
		else
			KERNEL->ioManager->ExportScene(scene, ((EventIoSceneExport*)evt)->rootID, ((EventIoSceneExport*)evt)->sceneFile);
	}
	break;
#ifndef VRUT_HPC
	case Event::EVT_RENDER_SCENE:
		{
			smart_pointer_v2<Scene> scene;
			KERNEL->sceneManager->GetScene(scene, ((EventRenderScene*)evt)->sceneID);
			if (!scene)
			{
				LOGERROR(wxT("<EventHandler>Invalid scene ID in event"));
				break;
			}
			NODE_ID cameraID = ((EventRenderScene*)evt)->cameraID;
			RENDER_WINDOW_ID windowID = ((EventRenderScene*)evt)->windowID;
			bool useActiveStereo = ((EventRenderScene*)evt)->useActiveStereo;
			if (cameraID==NODE_ID_NONE)
			{
				//create new camera
				wxString camName = wxT("default_camera");
				int tmp=0;
				std::vector<smart_pointer<Camera> > camNodeVec;
				scene->GetNodes(camNodeVec, SceneNode::CAMERA);
				bool found=true;
				while (found)
				{
					found=false;
					for (size_t i=0; i<camNodeVec.size(); i++)
					{
						if (camNodeVec[i]->GetName()==camName)
						{
							found=true;
							break;
						}
					}
					if (!found)
						break;
					camName=wxString::Format(wxT("default_camera_%i"), tmp);
					tmp++;
				}
				cameraID = scene->Insert(new Camera(camName, useActiveStereo?Camera::EYE_ACTIVE_STEREO:Camera::EYE_CENTER), scene->GetRootID());
				scene->ResetView(cameraID);
				windowID = KERNEL->renderManager->RenderScene(scene, cameraID, windowID, useActiveStereo);
				scene->Fit(cameraID);
				if (windowID != RENDER_WINDOW_ID_NONE)
					KERNEL->manipManager->ManipulateCamera(((EventRenderScene*)evt)->sceneID, cameraID, windowID);
			}
			else
			{
				//use existing camera
				windowID = KERNEL->renderManager->RenderScene(scene, cameraID, windowID, useActiveStereo);
			}
			PostEvent(GET_EVT_RENDER_SCENE_DONE(((EventRenderScene*)evt)->sceneID, cameraID, windowID));
		}
		break;
	case Event::EVT_RENDER_FULLSCREEN_SET:
	{
		GLWindow *glwin = KERNEL->renderManager->GetGLWindow(((EventRenderFullscreenSet*)evt)->windowID);
		if (glwin)
			glwin->SetFullscreen(((EventRenderFullscreenSet*)evt)->fsMode, ((EventRenderFullscreenSet*)evt)->positionX, ((EventRenderFullscreenSet*)evt)->positionY, ((EventRenderFullscreenSet*)evt)->sizeX,
				((EventRenderFullscreenSet*)evt)->sizeY, ((EventRenderFullscreenSet*)evt)->colorRGB);
		break;
	}
	case Event::EVT_RENDER_FULLSCREEN_TOGGLE:
	{
		GLWindow *glwin = KERNEL->renderManager->GetGLWindow(((EventRenderFullscreenToggle*)evt)->windowID);
		if (glwin)
			glwin->ToggleFullscreen();
		break;
	}
	case Event::EVT_RENDER_WIN_SHOW_CURSOR:
	{
		GLWindow *glwin = KERNEL->renderManager->GetGLWindow(((EventRenderWinShowCursor*)evt)->windowID);
		if (glwin)
			glwin->ShowCursor(((EventRenderWinShowCursor*)evt)->show);
	}
	break;
	case Event::EVT_RENDER_WIN_FOCUS:
	{
		GLWindow *glwin = KERNEL->renderManager->GetGLWindow(((EventRenderWinFocus*)evt)->windowID);
		if (glwin)
			glwin->Focus();
	}
	break;
	case Event::EVT_RENDER_WIN_CLOSE:
	{
		// set camera which can not be deleted (if I need destroy window without camera I can protect camera by ID)
		KERNEL->renderManager->SetProtectedCameraID(((EventRenderWinClose*)evt)->protectedCameraID);
		KERNEL->renderManager->DestroyGLWindow(((EventRenderWinClose*)evt)->windowID);
		break;
	}
	case Event::EVT_GUI_PANE_SHOW:
		KERNEL->GetMainWindow()->ShowPane(((EventGuiPaneShow*)evt)->paneID, ((EventGuiPaneShow*)evt)->show);
		break;
	case Event::EVT_GUI_PARAM_SHOW:
		if (((EventGuiParamShow*)evt)->show)
			((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->ShowParams(((EventGuiParamShow*)evt)->paramIdentifier);
		else
			((GUIEnvironment *)KERNEL->GetMainWindow()->GetParamsNotebook())->HideParams(((EventGuiParamShow*)evt)->paramIdentifier);
		break;
#endif //VRUT_HPC
	case Event::EVT_MAIN_THREAD_CALLBACK:
	{
		Module * module = KERNEL->GetModule(((EventMainThreadCallback*)evt)->moduleID);
		if (module)
		{
			int callID = ((EventMainThreadCallback*)evt)->callID;
			module->processSyncEvent(Event::EVT_MAIN_THREAD_CALLBACK, (void *)&callID);
		}
		break;
	}
	case Event::EVT_SCENE_ADD:
		KERNEL->sceneManager->AddScene(((EventSceneAdd*)evt)->sceneName, ((EventSceneAdd*)evt)->sceneID);
		break;
	case Event::EVT_SCENE_REMOVE:
		KERNEL->sceneManager->RemoveScene(((EventSceneRemove*)evt)->sceneID);
		break;
	case Event::EVT_SCENE_MATERIAL_ADD:
		{
			if (!((EventSceneMaterialAdd*)evt)->material)
				LOGERROR(wxT("<EventHandler>No material defined in scene event"));
			else
			{
				smart_pointer_v2<Scene> scene;
				if (!getScene(((EventSceneMaterialAdd*)evt)->sceneID, scene, evt->type))
					break;
				if (cloneSceneData)
				{
					scene->AddMaterial(((EventSceneMaterialAdd*)evt)->material->Clone());
					delete ((EventSceneMaterialAdd*)evt)->material;
				}
				else
					scene->AddMaterial(((EventSceneMaterialAdd*)evt)->material);
			}
			break;
		}
	case Event::EVT_SCENE_MATERIAL_REMOVE:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneMaterialRemove*)evt)->sceneID, scene, evt->type))
				scene->RemoveMaterial(scene->GetMaterialID(((EventSceneMaterialRemove*)evt)->materialName));
			break;
		}
	case Event::EVT_SCENE_MATERIAL_CONVERT_TRANSP_TO_REFL:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneMaterialConvertTranspToRefl*)evt)->sceneID, scene, evt->type))
				scene->ConvertMaterialTransparencyToReflexivity(((EventSceneMaterialConvertTranspToRefl*)evt)->materialName);
			break;
		}
	case Event::EVT_SCENE_GEOMETRY_ADD:
		{
			if (!((EventSceneGeometryAdd*)evt)->geometry)
				LOGERROR(wxT("<EventHandler>No geometry defined in scene event"));
			else
			{
				smart_pointer_v2<Scene> scene;
				if (!getScene(((EventSceneGeometryAdd*)evt)->sceneID, scene, evt->type))
					break;
				if (cloneSceneData)
				{
					scene->AddGeometry(((EventSceneGeometryAdd*)evt)->geometry->Clone());
					delete ((EventSceneGeometryAdd*)evt)->geometry;
				}
				else
					scene->AddGeometry(((EventSceneGeometryAdd*)evt)->geometry);
			}
			break;
		}
	case Event::EVT_SCENE_GEOMETRY_REMOVE:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneGeometryRemove*)evt)->sceneID, scene, evt->type))
				scene->RemoveGeometry(((EventSceneGeometryRemove*)evt)->geometryID);
			break;
		}
	case Event::EVT_SCENE_VARIANT_ADD:
		{
			if (!((EventSceneVariantAdd*)evt)->variant)
				LOGERROR(wxT("<EventHandler>No variant defined in scene event"));
			else
			{
				smart_pointer_v2<Scene> scene;
				if (!getScene(((EventSceneVariantAdd*)evt)->sceneID, scene, evt->type))
					break;
				if (cloneSceneData)
				{
					scene->AddVariant(((EventSceneVariantAdd*)evt)->variant->Clone());
					delete ((EventSceneVariantAdd*)evt)->variant;
				}
				else
					scene->AddVariant(((EventSceneVariantAdd*)evt)->variant);
			}
			break;
		}
	case Event::EVT_SCENE_VARIANT_REMOVE:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneVariantRemove*)evt)->sceneID, scene, evt->type))
				scene->RemoveVariant(((EventSceneVariantRemove*)evt)->variantID);
			break;
		}
	case Event::EVT_SCENE_VARIANT_ACTIVESTATE_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneVariantActiveStateSet*)evt)->sceneID, scene, evt->type))
				scene->SetVariantActiveState(((EventSceneVariantActiveStateSet*)evt)->variantID, ((EventSceneVariantActiveStateSet*)evt)->activeState);
			break;
		}
	case Event::EVT_SCENE_VARIANT_DETAILSTATE_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneVariantDetailStateSet*)evt)->sceneID, scene, evt->type))
				scene->SetVariantDetailState(((EventSceneVariantDetailStateSet*)evt)->variantName, ((EventSceneVariantDetailStateSet*)evt)->state, ((EventSceneVariantDetailStateSet*)evt)->detail,
					((EventSceneVariantDetailStateSet*)evt)->status);
		}
		break;
	case Event::EVT_SCENE_ENVIRONMENT_PROBE_ADD:
		{
			if (!((EventSceneEnvironmentProbeAdd*)evt)->environmentProbe)
				LOGERROR(wxT("<EventHandler>No environmentProbe defined in scene event"));
			else
			{
				smart_pointer_v2<Scene> scene;
				if (!getScene(((EventSceneEnvironmentProbeAdd*)evt)->sceneID, scene, evt->type))
					break;
				if (cloneSceneData)
				{
					scene->AddEnvironmentProbe(((EventSceneEnvironmentProbeAdd*)evt)->environmentProbe->Clone());
					delete ((EventSceneEnvironmentProbeAdd*)evt)->environmentProbe;
				}
				else
					scene->AddEnvironmentProbe(((EventSceneEnvironmentProbeAdd*)evt)->environmentProbe);
			}
			break;
		}
	case Event::EVT_SCENE_ENVIRONMENT_PROBE_ENABLE:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneEnvironmentProbeEnable*)evt)->sceneID, scene, evt->type))
				scene->EnableEnvironmentProbe(((EventSceneEnvironmentProbeEnable*)evt)->envProbeID, ((EventSceneEnvironmentProbeEnable*)evt)->enable);
		}
		break;
	case Event::EVT_SCENE_ENVIRONMENT_PROBE_SET_SINGLE_SHOT:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneEnvironmentProbeSetSingleShot*)evt)->sceneID, scene, evt->type))
				scene->SetSingleShotEnvironmentProbe(((EventSceneEnvironmentProbeSetSingleShot*)evt)->envProbeID, ((EventSceneEnvironmentProbeSetSingleShot*)evt)->singleShot);
		}
		break;
	case Event::EVT_SCENE_CLIPPING_PLANE_ADD:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneClippingPlaneAdd*)evt)->sceneID, scene, evt->type))
				scene->AddClippingPlane(new ClippingPlane(((EventSceneClippingPlaneAdd*)evt)->clippingPlane));
		}
		break;
	case Event::EVT_SCENE_NODE_COPY:
		{
			smart_pointer_v2<Scene> scene;
			if (!getScene(((EventSceneNodeCopy*)evt)->sceneID, scene, evt->type))
				break;
			if (((EventSceneNodeCopy*)evt)->sceneID==((EventSceneNodeCopy*)evt)->srcSceneID)
				scene->Copy(scene.getPtr(), ((EventSceneNodeCopy*)evt)->srcID, ((EventSceneNodeCopy*)evt)->targetID);
			else
			{
				smart_pointer_v2<Scene> srcScene;
				if (getScene(((EventSceneNodeCopy*)evt)->srcSceneID, srcScene, evt->type))
					scene->Copy(srcScene.getPtr(), ((EventSceneNodeCopy*)evt)->srcID, ((EventSceneNodeCopy*)evt)->targetID);
			}
			break;
		}
	case Event::EVT_SCENE_NODE_MOVE:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeMove*)evt)->sceneID, scene, evt->type))
				scene->Move(((EventSceneNodeMove*)evt)->srcID, ((EventSceneNodeMove*)evt)->targetID);
			break;
		}
	case Event::EVT_SCENE_NODE_INDEX_CHANGE:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeIndexChange*)evt)->sceneID, scene, evt->type))
				scene->ChangeChildIndex(((EventSceneNodeIndexChange*)evt)->nodeID, ((EventSceneNodeIndexChange*)evt)->newIndex);
			break;
		}
	case Event::EVT_SCENE_NODE_INSERT:
		{
			if (!((EventSceneNodeInsert*)evt)->node)
				LOGERROR(wxT("<EventHandler>No node defined in scene event"));
			else
			{
				smart_pointer_v2<Scene> scene;
				if (!getScene(((EventSceneNodeInsert*)evt)->sceneID, scene, evt->type))
					break;
				if (cloneSceneData)
				{
					scene->Insert(smart_pointer<SceneNode>(((EventSceneNodeInsert*)evt)->node->Clone()), ((EventSceneNodeInsert*)evt)->parentID);
					delete ((EventSceneNodeInsert*)evt)->node;
				}
				else
					scene->Insert(smart_pointer<SceneNode>(((EventSceneNodeInsert*)evt)->node), ((EventSceneNodeInsert*)evt)->parentID);
			}
			break;
		}
	case Event::EVT_SCENE_NODE_REMOVE:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeRemove*)evt)->sceneID, scene, evt->type))
				scene->Remove(((EventSceneNodeRemove*)evt)->nodeID);
			break;
		}
	case Event::EVT_SCENE_NODE_NAME_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeNameSet*)evt)->sceneID, scene, evt->type))
				scene->SetName(((EventSceneNodeNameSet*)evt)->nodeID, ((EventSceneNodeNameSet*)evt)->newName);
			break;
		}
	case Event::EVT_SCENE_NODE_TRANSFORM_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeTransformSet*)evt)->sceneID, scene, evt->type))
				scene->SetTransformation(((EventSceneNodeTransformSet*)evt)->nodeID, ((EventSceneNodeTransformSet*)evt)->matrix);
			break;
		}
	case Event::EVT_SCENE_NODE_TRANSFORM:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeTransform*)evt)->sceneID, scene, evt->type))
				scene->Transform(((EventSceneNodeTransform*)evt)->nodeID, ((EventSceneNodeTransform*)evt)->matrix);
			break;
		}
	case Event::EVT_SCENE_NODE_TRANSLATE_ABS:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeTranslateAbs*)evt)->sceneID, scene, evt->type))
				scene->TranslateAbs(((EventSceneNodeTranslateAbs*)evt)->nodeID, ((EventSceneNodeTranslateAbs*)evt)->vector);
			break;
		}
	case Event::EVT_SCENE_NODE_GEOMETRY_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeGeometrySet*)evt)->sceneID, scene, evt->type))
				scene->SetGeometry(((EventSceneNodeGeometrySet*)evt)->nodeID, ((EventSceneNodeGeometrySet*)evt)->geometryID);
			break;
		}
	case Event::EVT_SCENE_NODE_MATERIAL_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeMaterialSet*)evt)->sceneID, scene, evt->type))
				scene->SetMaterial(((EventSceneNodeMaterialSet*)evt)->nodeID, scene->GetMaterialID(((EventSceneNodeMaterialSet*)evt)->materialName));
			break;
		}
	case Event::EVT_SCENE_NODE_LIGHT_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (!getScene(((EventSceneNodeLightSet*)evt)->sceneID, scene, evt->type))
				break;
			scene->SetLight(((EventSceneNodeLightSet*)evt)->nodeID, ((EventSceneNodeLightSet*)evt)->light);
		}
		break;
	case Event::EVT_SCENE_NODE_LOD_DISTANCE_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeLodDistanceSet*)evt)->sceneID, scene, evt->type))
				scene->SetLODDistance(((EventSceneNodeLodDistanceSet*)evt)->nodeID, ((EventSceneNodeLodDistanceSet*)evt)->LODID, ((EventSceneNodeLodDistanceSet*)evt)->distance);
		}
		break;
	case Event::EVT_SCENE_NODE_SWITCH_ACTIVECHILD_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeSwitchActiveChildSet*)evt)->sceneID, scene, evt->type))
				scene->SetSwitchActiveChild(((EventSceneNodeSwitchActiveChildSet*)evt)->nodeID, ((EventSceneNodeSwitchActiveChildSet*)evt)->activeChild);
			break;
		}
	case Event::EVT_SCENE_NODE_SOUND_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (!getScene(((EventSceneNodeSoundSet*)evt)->sceneID, scene, evt->type))
				break;
			EventSceneNodeSoundSet *ev((EventSceneNodeSoundSet*)evt);
			scene->SetSoundNode(ev->nodeID, ev->gain, ev->looping, ev->state, ev->minGain, ev->maxGain, ev->referenceDistance, ev->rollOffFactor, ev->maxDistance, ev->pitch, ev->direction,
				ev->coneInnerAngle, ev->coneOuterAngle, ev->coneOuterGain, ev->secOffset, ev->dataSource);
			break;
		}
		case Event::EVT_SCENE_NODE_SOUND_STATE_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeSoundStateSet*)evt)->sceneID, scene, evt->type))
				scene->SetSoundState(((EventSceneNodeSoundStateSet*)evt)->nodeID, ((EventSceneNodeSoundStateSet*)evt)->state);
			break;
		}
	case Event::EVT_SCENE_NODE_ACTIVE_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneNodeActiveSet*)evt)->sceneID, scene, evt->type))
				scene->SetActive(((EventSceneNodeActiveSet*)evt)->nodeID, ((EventSceneNodeActiveSet*)evt)->active);
			break;
		}
	case Event::EVT_SCENE_CAM_FIT:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneCameraFit*)evt)->sceneID, scene, evt->type))
				scene->Fit(((EventSceneCameraFit*)evt)->cameraID, false, ((EventSceneCameraFit*)evt)->nodeID);
			break;
		}
	case Event::EVT_SCENE_CAM_FIT_NEAR_FAR:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneCameraFitNearFar*)evt)->sceneID, scene, evt->type))
				scene->Fit(((EventSceneCameraFitNearFar*)evt)->cameraID, true, ((EventSceneCameraFitNearFar*)evt)->nodeID);
			break;
		}
	case Event::EVT_SCENE_CAM_RESETVIEW:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneCamResetView*)evt)->sceneID, scene, evt->type))
				scene->ResetView(((EventSceneCamResetView*)evt)->cameraID);
			break;
		}
	case Event::EVT_SCENE_CAM_PROJECTION_SET:
		{
			smart_pointer_v2<Scene> scene;
			if (!getScene(((EventSceneCameraProjectionSet*)evt)->sceneID, scene, evt->type))
				break;
			EventSceneCameraProjectionSet *ev((EventSceneCameraProjectionSet*)evt);
			//LOG(wxString::Format(wxT("EventHandler::EVT_SCENE_CAM_PROJECTION_SET before SetProjectionParams camID=%lu, w=%i, h=%i"), (unsigned long)camID,  (unsigned int)projWidth, (unsigned int)projHeight));
			scene->SetProjectionParams(ev->cameraID, ev->projWidth, ev->projHeight, ev->projNear, ev->projFar, ev->projFov, ev->upFix);
			break;
		}
	case Event::EVT_SCENE_CAM_SET_PERSPECTIVE_PARAMS:
		{
			smart_pointer_v2<Scene> scene;
			if (!getScene(((EventSceneCamSetPerspectiveParams*)evt)->sceneID, scene, evt->type))
				break;
			EventSceneCamSetPerspectiveParams *ev((EventSceneCamSetPerspectiveParams*)evt);
			scene->SetPerspectiveParams(ev->cameraID, ev->nearPlane, ev->farPlane, ev->offset, ev->eye, ev->eyeSeparation, ev->pa, ev->pb, ev->pc, ev->eyeAxis, ev->lookAt);
			break;
		}
	case Event::EVT_SCENE_CARPET_REGISTER:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneCarpetRegister*)evt)->sceneID, scene, evt->type))
				scene->CreateCarpet(((EventSceneCarpetRegister*)evt)->nodeID, ((EventSceneCarpetRegister*)evt)->isTracked, ((EventSceneCarpetRegister*)evt)->eyeSeparation, ((EventSceneCarpetRegister*)evt)->bodyID);
			break;
		}
	case Event::EVT_SCENE_CARPET_UNREGISTER:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneCarpetUnregister*)evt)->sceneID, scene, evt->type))
				scene->RemoveCarpet(((EventSceneCarpetUnregister*)evt)->nodeID);
			break;
		}
	case Event::EVT_SCENE_CARPET_SET_CAM_PARAMS:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneCarpetSetCamParams*)evt)->sceneID, scene, evt->type))
				scene->SetCarpetCamParams(((EventSceneCarpetSetCamParams*)evt)->nodeID, ((EventSceneCarpetSetCamParams*)evt)->eyeSeparation, ((EventSceneCarpetSetCamParams*)evt)->isTracked,
					((EventSceneCarpetSetCamParams*)evt)->bodyID);
			break;
		}
	case Event::EVT_SCENE_CARPET_SET_OBSERVER_POSITION:
		{
			smart_pointer_v2<Scene> scene;
			if (getScene(((EventSceneCarpetSetObserverPosition*)evt)->sceneID, scene, evt->type))
				scene->UpdateCarpetOffset(((EventSceneCarpetSetObserverPosition*)evt)->nodeID, ((EventSceneCarpetSetObserverPosition*)evt)->position);
			break;
		}
	case Event::EVT_INPUT_TRACKING:
	{
		///Get the position and orientation matrix and try to update flying carpets in all current scenes
		///only accept 6DOF bodies
		if ((((EventInputTracking*)evt)->deviceData.type == DeviceData::DEVICE_6DoF) || (((EventInputTracking*)evt)->deviceData.type == DeviceData::DEVICE_FlyStick2))
		{
			///Extract the transformation matrix
			///We now have to separate the rotation and translation components - this is because
			///we need to take the rotation into account when shifting the eye position.
			//Let's just hope there wasn't a scaling transformation somewhere on the way
			Matrix rotMat = ((EventInputTracking*)evt)->deviceData.matrix.ExtractRotation();
			Vector3 translate = ((EventInputTracking*)evt)->deviceData.matrix.ExtractTranslation();
			///Now we iterate through all the scenes and try to update all the tracked carpets that are bound to this body
			std::vector<smart_pointer_v2<Scene> > scenes;
			KERNEL->sceneManager->GetScenes(scenes);
			for (auto &scene : scenes)
				scene->UpdateCarpets(((EventInputTracking*)evt)->deviceData.id, translate, rotMat);
		}
		break;
	}
	case Event::EVT_INTERPRETER_PROCESS_COMMANDS:
		KERNEL->interpreter.ProcessCommand(((EventInterpreterProcessCommands*)evt)->commands);
		break;
	default:
		break;
	}
}


void EventHandler::DistributeToListeners(Event *evt)
{
	bool highPrior = (evt->type == Event::EVT_EXIT);
#ifdef SYNC_DEBUG
	if (evt->type==Event::EVT_RENDER_SYNCHRONIZATION_REGISTER)
		LOG(wxT("<EventHandler::DistributeToListeners>EVT_RENDER_SYNCHRONIZATION_REGISTER"));
#endif	
	///Distribute to listeners registered with event type
	size_t index = evt->type;
	if (!moduleListeners[index].empty())
	{
		for (size_t i=1; i<moduleListeners[index].size(); ++i)
		{
			Module * module = KERNEL->GetModule(moduleListeners[index][i]);
			if (module)
			{
#ifdef SYNC_DEBUG
				if (evt->type==Event::EVT_RENDER_SYNCHRONIZATION_REGISTER)
					LOG(wxT("<EventHandler::DistributeToListeners>EVT_RENDER_SYNCHRONIZATION_REGISTER module=")+module->GetName());
				else if (evt->type==Event::EVT_RENDER_SYNCHRONIZATION_BARRIER_RELEASE)
					LOG(wxString::Format(wxT("<EventHandler::DistributeToListeners> EVT_RENDER_SYNCHRONIZATION_BARRIER_RELEASE (lastBarrier=%i)"), ((EventRenderSynchronizationBarrierRelease*)evt)->renderState)+module->GetName());
				else if (evt->type==Event::EVT_RENDER_UPDATE)
					LOG(wxString::Format(wxT("<EventHandler::DistributeToListeners> EVT_RENDER_UPDATE (renderState=%i)"), ((EventRenderUpdate*)evt)->renderState)+module->GetName());
#endif	
				Event *clonedEvt = CloneEvent(evt);
				module->PostEvent(clonedEvt, highPrior);
			}
		}
		Module * module = KERNEL->GetModule(moduleListeners[index][0]);
		if (module)
		{
#ifdef SYNC_DEBUG
			if (evt->type==Event::EVT_RENDER_SYNCHRONIZATION_REGISTER)
				LOG(wxT("<EventHandler::DistributeToListeners>EVT_RENDER_SYNCHRONIZATION_REGISTER module=")+module->GetName());
			else if (evt->type==Event::EVT_RENDER_SYNCHRONIZATION_BARRIER_RELEASE)
				LOG(wxString::Format(wxT("<EventHandler::DistributeToListeners> EVT_RENDER_SYNCHRONIZATION_BARRIER_RELEASE (lastBarrier=%i)"), ((EventRenderSynchronizationBarrierRelease*)evt)->renderState)+module->GetName());
			else if (evt->type==Event::EVT_RENDER_UPDATE)
				LOG(wxString::Format(wxT("<EventHandler::DistributeToListeners> EVT_RENDER_UPDATE (renderState=%i)"), ((EventRenderUpdate*)evt)->renderState)+module->GetName());
#endif	
			evt->next=nullptr;
			module->PostEvent(evt, highPrior);
			return;
		}
	}
	ReleaseEvent(evt);
}

void EventHandler::ProcessJob(ThreadPoolJob* job)
{
	if (!job)
		return;

	wxCriticalSectionLocker lock(threadListMutex);
	threadList.push_back(new ThreadListThread(*this, job));
	if (threadList.back()->Create() == wxTHREAD_NO_ERROR)
		threadList.back()->Run();
}

void EventHandler::RemoveThreadListThread(wxThreadIdType threadID)
{
	wxCriticalSectionLocker lock(threadListMutex);
	for (size_t i = 0; i < threadList.size(); ++i)
		if (threadList[i]->GetId() == threadID)
			threadList.erase(threadList.begin() + i);
}

void EventHandler::ProcessEventSceneImageAdd(const EventSceneImageAdd *eventSceneImageAdd)
{
	if (eventSceneImageAdd->image)
		KERNEL->sceneManager->AddImage(eventSceneImageAdd->texFile, eventSceneImageAdd->image, eventSceneImageAdd->cubeSide, eventSceneImageAdd->imageRefName);

	const_smart_pointer<Image> image = KERNEL->sceneManager->GetImage(eventSceneImageAdd->texFile, true, eventSceneImageAdd->cubeSide, eventSceneImageAdd->imageRefName, eventSceneImageAdd->reload, eventSceneImageAdd->flip);

	std::vector<smart_pointer_v2<Scene> > scenes;
	KERNEL->sceneManager->GetScenes(scenes);
	for (auto &scene : scenes)
	{
		std::vector<smart_pointer<Material> > mats;
		scene->GetMaterials(mats);
		for (auto &mat : mats) //TODO: The whole fixMaterialBlending functionality should probably be here.
		{
			if (((eventSceneImageAdd->imageRefName.empty() && (mat->imageName == eventSceneImageAdd->texFile)) || (!eventSceneImageAdd->imageRefName.empty() && (mat->imageName == eventSceneImageAdd->imageRefName))))
			{
				Material *nmat = mat->Clone();
				if (nmat->FixBlendingAndCubeMap(image.getPtr()))
					scene->AddMaterial(nmat);
				else
					delete nmat;
			}
		}
	}
}

#ifdef DEBUG_EVENTS
void EventHandler::DebugEvent(Event *evt, const char *type)
{
	wxString events(VRUTGetTime().ToString());

	if (type)
		events += wxT(" ") + wxString::From8BitData(type);

	events += wxT("\n") + EventToString(evt);
	events += wxT("\npending=")+evtQueue.EventsToString();//for now always show all pending messages (TODO: implement evtQueue.GetSize)
	/*static int counter = 0;
	if (counter++ % 50 == 0) {
		// time to time show all pending events
		events += evtQueue.EventsToString();
	}*/
	LOG(events);
}
#endif

wxThread::ExitCode ThreadPoolThread::Entry()
{
	++eventHandler.threadPoolCount;
	// just keep processing jobs from the queue
	while (!TestDestroy() && !exit)
	{
		/// Check for pending jobs
		ThreadPoolJob * job = eventHandler.threadPoolQueue.ReceiveTimeout(1000);
		if (job)
		{
			if (!exit)
				job->Run();
			delete job;
		}
	}
	--eventHandler.threadPoolCount;
	return 0;
}

wxThread::ExitCode ThreadListThread::Entry()
{
	// process job and detach
	if (job)
	{
		job->Run();
		delete job;
	}
	eventHandler.RemoveThreadListThread(this->GetId());
	return 0;
}

wxThread::ExitCode EventHandlerThread::Entry()
{
	// just keep processing messages from the queue
	while (!TestDestroy())
	{
		/// Check for pending events sent from modules
		Event *evt = eventHandler.evtQueue.ReceiveTimeout(1000);
		if (evt)
		{
			if (!KERNEL->IsExiting() || evt->type == Event::EVT_LOG)
			{
#ifdef DEBUG_EVENTS
				eventHandler.DebugEvent(evt, "thread");
#endif
#ifdef SYNC_DEBUG
				if (evt->type==Event::EVT_RENDER_SYNCHRONIZATION_BARRIER_RELEASE)
					LOG(wxString::Format(wxT("<EventHandlerThread::Entry> EVT_RENDER_SYNCHRONIZATION_BARRIER_RELEASE (lastBarrier=%i)"), ((EventRenderSynchronizationBarrierRelease*)evt)->renderState));
#endif
				eventHandler.processEvent(evt);
				if (evt->type != Event::EVT_LOG)
					eventHandler.DistributeToListeners(evt);
				else
					ReleaseEvent(evt);
			}
			else
				ReleaseEvent(evt);
			///Now we're safe - no memory allocated on module heap
			///residing in event structure
		}
	}
	eventHandler.eventHandlerThreadIsRunning=false;
	return 0;
}
