/*
 * $Id: eventhandler.h 4922 2018-04-18 14:27:26Z xkacer00@stud.fit.vutbr.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.
 */


#ifndef __EVENT_HANDLER_H__
#define __EVENT_HANDLER_H__

#include "events.h"
#include "VrutApi.h"
#include "scenemanager.h"
#ifdef VRUT_HPC
#include "kernel.h"
#endif

#include <queue>
#include <atomic>

//#define DEBUG_EVENTS


namespace VRUT
{
	class ThreadPoolJob  : public MPMC_QueueNode
	{
	public:
		ThreadPoolJob() : MPMC_QueueNode() {}
		virtual ~ThreadPoolJob() {}
		virtual void Run() = 0;
	};

	class ThreadPoolThread;
	class ThreadListThread;
	class EventHandlerThread;

	///Central event handler for processing and distributing all events
	class VRUT_CORE_API EventHandler
#ifndef VRUT_HPC
		: public wxEvtHandler
#endif
	{
	public:
		typedef std::vector<MODULE_ID> ModuleIDList;
		typedef std::vector<ModuleIDList> EventModuleList;
		///List of modules registered to listen to various events
		EventModuleList moduleListeners;
		//ID of event handler thread to be able detect modules logs
		wxThreadIdType eventHandlerThreadID;

	protected:

		//thread pool
		std::vector<ThreadPoolThread *> threadPool;
		MPMC_Queue<ThreadPoolJob> threadPoolQueue;
		std::atomic<int> threadPoolCount;

		//thread list - the threads will be created on request and deleted automatically when they end the job
		wxCriticalSection threadListMutex;
		std::vector<ThreadListThread *> threadList;

		//event processing thread
		EventHandlerThread *eventHandlerThread;
		bool eventHandlerThreadIsRunning;

		///Message queue for events that must be processed in main thread
		EventQueue mainThreadQueue;
		///Message queue for events posted by modules
		EventQueue evtQueue;
		///Clone imported scene data?
		bool cloneSceneData;
		///\c cloneSceneData parameter identifier
		Parameter::ParameterIdentifier cloneSceneDataParamID;
		///Maximum number of events to be processed per one iteration
		int evtsPerIteration;
		///\c evtsPerIteration parameter identifier
		Parameter::ParameterIdentifier evtsPerIterationParamID;
		///Event check interval in milliseconds
		///Save log messages to disc immediately
		bool logSyncEveryMsg;
		///\c logSyncEveryMsg parameter identifier
		Parameter::ParameterIdentifier logSyncEveryMsgParamID;
		///use exit(0) for application end
		bool fastExit;
		///\c fastExit parameter identifier
		Parameter::ParameterIdentifier fastExitParamID;
		///Presentation mode enabled/disabled
		bool presentationMode;
		///\c presentationMode parameter identifier
		Parameter::ParameterIdentifier presentationModeParamID;

#ifdef VRUT_HPC
	public:
#endif
		///Process events in queue - check \c msgQueue for module events
		///\return \c true if all events processed, \c false if there are still events in queue
		bool processEvents();
#ifdef VRUT_HPC
	protected:
#else
		///Presentation mode enabled/disabled
		int iconsSize;
		///\c presentationMode parameter identifier
		Parameter::ParameterIdentifier iconsSizeParamID;
		///Process idle event
		void onIdle(wxIdleEvent & evt);
		///Process key event - propagate event to appropriate manipulator module
		void onKeyEvent(wxKeyEvent & evt);
		///Process mouse event - propagate event to appropriate manipulator module
		void onMouseEvent(wxMouseEvent & evt);
#endif
		//Process VRUT event
		void processEvent(Event *evt);
		//get Scene and report error if necessary
		bool getScene(const SCENE_ID sceneID, smart_pointer_v2<Scene> &scene, const Event::EVENT_TYPE evtType)
		{
			KERNEL->sceneManager->GetScene(scene, sceneID);
			if (!scene)
			{
				LOGERROR(wxString::Format(wxT("<EventHandler>Invalid scene ID=%i in scene event (type=%i) sent by "), sceneID, evtType));
				return false;
			}
			return true;
		}

		/// Debug events by logging them to a stream
#ifdef DEBUG_EVENTS
		void DebugEvent(Event *evt, const char *type = NULL);
#endif

	public:
		///Class constructor
		EventHandler();
		///Class destructor
		virtual ~EventHandler();

		///Register module to listen to events
		///\param[in] id Unique ID of module to be registered
		///\param[in] eventType Module will be informed when events of \c eventType type occur
		void RegisterListener(const MODULE_ID & id, const Event::EVENT_TYPE eventType);
		///Unregister module from event(s) listening
		///\param[in] id Module ID
		///\param[in] eventType Type of events to unregister, all if out of custom events range (default)
		void UnregisterListener(const MODULE_ID & id, const Event::EVENT_TYPE eventType = Event::EVT_HIGHEST);
		///Post event to message queue - thread safe and module safe
		///\param[in] evt Event to post
		///\param[in] highPriority Event will be processed before any others in queue if \c true
		void PostEvent(EventMain *evt, bool highPriority = false)
		{
			mainThreadQueue.Post(evt, highPriority);
#ifndef VRUT_HPC
#ifdef __WXMSW__
			wxWakeUpMainThread();
#else
			wxWakeUpIdle();
#endif
#endif
		}
		void PostEvent(EventNormal *evt, bool highPriority = false)
		{
			evtQueue.Post(evt, highPriority);
		}
		///Distribute event to listeners
		void DistributeToListeners(Event *evt);
		///send job to thread pool
		void PostJob(ThreadPoolJob *job)
		{
			threadPoolQueue.Post(job);
		}
		//create new thread and run the job, it is intended for longer tasks with relative small CPU utilization - for example IO modules import
		void ProcessJob(ThreadPoolJob* job);
		//remove thread from thread list, it is used automatically by thread created with ProcessJob
		void RemoveThreadListThread(wxThreadIdType threadID);
		///Process module events posted by given module (any instance of library)
		void ProcessEventsFrom(MODULE_HANDLE_ID hid);
		///Release events from mainThreadQueue pushed by specific module (because of memory release on releaseModule)
		void ReleaseEventsFromModule(const Parameter::ParameterIdentifier &paramIdentifier)
		{
			//mainThreadQueue.RemoveMessagesFromModule(paramIdentifier);
		}

		/// if both of queues are empty return true
		bool IsQueueEmpty() const
		{
			return (mainThreadQueue.IsEmpty() && evtQueue.IsEmpty());
		}

		bool FastExit()
		{
			return fastExit;
		}

		void ProcessEventSceneImageAdd(const EventSceneImageAdd *eventSceneImageAdd);

		friend class EventManager;
		friend class ThreadPoolThread;
		friend class ThreadListThread;
		friend class EventHandlerThread;
	};

	class ThreadPoolThread : public wxThread
	{
		EventHandler &eventHandler;

	public:
		bool exit;
		ThreadPoolThread(EventHandler &_handler) : wxThread(wxTHREAD_DETACHED), eventHandler(_handler), exit(false) {}

		virtual ExitCode Entry();
	};

	class ThreadListThread: public wxThread
	{
		EventHandler &eventHandler;
		ThreadPoolJob *job;
	public:
		ThreadListThread(EventHandler &_handler, ThreadPoolJob *job) : wxThread(wxTHREAD_DETACHED), eventHandler(_handler), job(job) {}

		virtual ExitCode Entry();
	};

	class EventHandlerThread : public wxThread
	{
		EventHandler &eventHandler;

	public:
		EventHandlerThread(EventHandler &_handler) : wxThread(wxTHREAD_DETACHED), eventHandler(_handler) {}

		virtual ExitCode Entry();
	};

	class SceneImageAddJob : public ThreadPoolJob
	{
		EventSceneImageAdd *eventSceneImageAdd;
	public:
		SceneImageAddJob(EventSceneImageAdd *eventSceneImageAdd) : ThreadPoolJob(), eventSceneImageAdd(eventSceneImageAdd) {}
		virtual ~SceneImageAddJob()
		{
			ReleaseEvent(eventSceneImageAdd);
		}
		virtual void Run()
		{
			KERNEL->GetMessageSink()->ProcessEventSceneImageAdd(eventSceneImageAdd);
		}
	};

	class SceneImageSaveJob : public ThreadPoolJob
	{
		wxImage image;
		wxString filename;
	public:
		SceneImageSaveJob(const wxImage &image, const wxString &filename) : ThreadPoolJob(), image(image), filename(filename) {}
		virtual ~SceneImageSaveJob() {}
		virtual void Run()
		{
			//image.Mirror(false).SaveFile(filename);
			image.SaveFile(filename);
			//LOG(wxT("Image rendered: ") + filename);
			KERNEL->GetMessageSink()->PostEvent(GET_EVT_IMAGE_SAVE_DONE(filename));
		}
	};
}

#endif
