/*
	ZThread.hpp
	Author : James Russell <jcrussell@762studios.com>
	Created: 07/04/09

	Purpose :	
	
	Wrapper class to allow easy thread creation.  By making a class that is 
	a subclass of ZThread and defining the run() method, a thread 'object' can
	be created that allows for greater consistency and when dealing with thread 
	resources.

	License:

	TODO
*/

#ifndef _ZTHREAD_H
#define _ZTHREAD_H

#include <ZSTL/ZList.hpp>
#include <ZSTL/ZString.hpp>

#include <ZUtil/ZConcurrency.hpp>
#include <ZUtil/ZSmartPointer.hpp>

class ZThread;
typedef void (*ZThreadCallbackFunc)(ZThread* executor, void* arg);

/*
Macro that creates an anonymous request object for marshalling code into a thread request.

Example:

TODO

@param zthread - the zthread object that should execute the request
@param argument - the void* argument to pass in (accessible as Arg)
@param wait - a boolean indicating if we should wait or not
@param code - the actual code that should be executed as part of the request (wrap in parenthesis)
*/
#define ZTHREAD_EXECUTE(zthread, argument, wait, code) \
	{ \
		class Z##__FUNCTION__##__LINE__##ThreadRequest : public ZThreadRequest \
		{ \
		protected: \
			void *Arg; \
		public: \
			Z##__FUNCTION__##__LINE__##ThreadRequest() : ZThreadRequest(), Arg(argument) { } \
			\
			virtual void Execute(ZThread *_threadObj) \
			{ \
				code \
			} \
		}; \
		\
		zthread->AddThreadRequest(ZPtr<ZThreadRequest>(znew Z##__FUNCTION__##__LINE__##ThreadRequest), wait); \
	}

//Forward declaration of ZThread
class ZThread;

//Possible return values from ZThread run
typedef enum ZThreadReturn
{
	ZTR_TERMINATE,	//Indicates the thread should terminate after returning from run
	ZTR_LOOP,		//Indicates the thread should loop after returning from run
	ZTR_PAUSE,		//Indicates the thread should pause after returning from run
	ZTR_SIZE
} ZThreadReturn;

/*
A ZThread 'Request' object.  Used for thread marshaling.
*/
class ZThreadRequest
{
friend class ZThread;
private:
	//Completion Event
	ZEvent CompletedEvent;

	//Indicates this 'ThreadRequest' should persist until removed
	bool Persist;

	//Indicates to the ZThread that this request should stop running immediately
	//This value is checked before execution
	bool Stop;

public:
	/*
	Default Constructor.

	@param _persist - boolean indicating this thread request should execute continuously until
		the ZThread object is deleted
	*/
	ZThreadRequest(bool _persist = false) 
		: CompletedEvent(), Persist(_persist), Stop(false) { }

	/*
	Destructor.

	Destroys the concurrency event.
	*/
	virtual ~ZThreadRequest() { }

	/*
	virtual public ZThreadRequest::Execute

	Virtual method, which defines the thread request to be executed.  The function will be executed
	from within the ZThread object's thread context.  If this method is not overridden and Func is not 
	NULL, Func is called with the provided argument.

	@param _threadObj - the ZThread object executing this request

	@return (void)
	*/
	virtual void Execute(ZThread *_threadObj) = 0;

	/*
	public ZThreadRequest::StopExecution

	Stops execution of the thread request, provided it has not executed yet.

	@return (void)
	*/
	void StopExecution() { this->Stop = true; }

	/*
	public ZThreadRequest::Wait
	
	Blocks until after the thread request has been executed.
	
	@return (void)
	*/
	void Wait() { CompletedEvent.Wait(); }
};

/*
ZThread object class.  Should be subclassed to make a threadable 'object'.
*/
class ZThread
{
private:
	DISABLE_COPY_AND_ASSIGN(ZThread);

public:
	/*
	Default Constructor.

	@param _threadName - the name of this thread
	*/
	ZThread(ZString _threadName = "");

	/*
	Destructor for the ZThread Object.  When the ZThread object is deleted, the destructor will wait on
	the thread to terminate and return resources to the system.
	*/
	virtual ~ZThread();

	/*
	public ZThread::AddThreadRequest()

	Adds a callback that will be executed when the thread loops.

	@param _request - the thread request to add to the thread's context
	
	@return (void)
	*/
	void AddThreadRequest(ZPtr<ZThreadRequest> _request, bool _wait = false);

	/*
	public ZThread::AddCallbackRequest()

	Adds a callback to be executed by the thread. It can be executed synchronously or asynchronously.

	@param function - The function to execute
	@param arg - The argument
	@param async - If true, this returns immediately, otherwise it waits for the function to execute.
	
	@return (void)
	*/
	void AddCallbackRequest(ZThreadCallbackFunc function, void* arg, bool async);
	
	/*
	public ZThread::GetThreadID

	Gets the thread ID of the thread object.

	@return (uint32_t) -  int that is the thread id of the thread object
	*/
	uint32_t GetThreadId();
	
	/*
	public ZThread::GetThreadName

	Gets the thread name of the ZThread object.  The name of a thread is either a 
	string value of the thread id or a name that was set by the ZThread object.

	@return (ZString) - string representing the 'name' of this thread
	*/
	ZString GetThreadName();

	/*
	public ZThread::PauseThread
	 
	Tells the user thread to pause.
	 
	@return (void)
	*/
	void PauseThread();
	
	/*
	public ZThread::RestartThread
	 
	Restarts a paused thread.
	 
	@return (void)
	*/
	void RestartThread();

	/*
	public ZThread::ThreadRunning

	Indicates if a ZThread object is executing it's run method or is paused.

	@return (bool) - boolean indicating thread is running or is paused
	*/
	bool ThreadRunning();

	/*
	public ZThread::ThreadInitialized

	Returns true if the ZThread object has called it's initThread() method and returned.

	@return (bool) - boolean indicating the thread is initialized
	*/
	bool ThreadInitialized();

	/*
	public ZThread::ShutdownThread

	Tells the user thread to shutdown.  Blocks until it happens.

	@return (void)
	*/
	void ShutdownThread();
	
	/*
	public ZThread::StartThread

	When StartThread is called on a class that extends ZThread, a thread will be created
	that will call the run() function implemented by the subclass.

	@return (bool) - boolean indicating the thread has started successfully
	*/
	bool StartThread();

	/*
	public ZThread::WaitInitialized
	
	Caller waits until the ZThread has initialized.
	
	@return (void)
	@context (all)
	*/
	void WaitInitialized();

	/*
	public ZThread::WaitShutdown
	
	Caller waits until the ZThread has shutdown.
	
	@return (void)
	@context (all)
	*/
	void WaitShutdown();
	
protected:
	//Thread 'name' of this thread
	ZString ThreadName;

	//Boolean field which, when set to true, indicates the thread should terminate.
	bool bShouldShutdown;

	//Boolean field which, when set to true, indicates the thread should pause.
	bool bShouldPause;

	/*
	This is a wrapper around the user run() method, which calls that method in a loop, executing
	thread requests and setting the condition variables appropriately.
	*/
	void Run();

	/*
	Executes the thread requests that may be pending at the time it is called by the subclass.
	*/
	void ExecuteThreadRequests();
	
	/*
	Initialization method which is called when the thread starts.  Does not have to be overridden, but any
	thread specific initialization that must be done in the thread context of the ZThread object needs 
	to be defined here.
	*/
	virtual void initThread() { }

	/*
	Shutdown method which is called when the thread terminates.  Does not have to be overridden, but any
	thread specific shutdown requirements that must be done in the thread context of the ZThread object 
	needs to be defined here.
	*/
	virtual void shutdownThread() { }

	/*
	This method must be implemented by the ZThread subclass.  This is the 'main' function of execution for
	the newly created thread.

	@param _dt - the time (in ms) since last time run was called
	@return - a ZTHREAD_RETURN value indicating whether the thread should run once (ZTR_TERMINATE),
		loop (ZTR_RETURN), or pause (ZTR_PAUSE).
	*/
	virtual ZThreadReturn run(uint64_t _dt) = 0;
	
	bool IsCallerTheThread();
private:
	struct ZThreadCallbackRequest {
		ZThreadCallbackFunc callback;
		void* arg;
		SST_Event doneEvent;
	};

	

	//Thread context of this thread
	ZThreadContext ThreadContext;

	//The mutex for use by this thread object for handling thread marshaling.
	ZMutex ThreadLock;

	//The event signaled when the thread has finished initializing
	ZEvent InitEvent;

	//Previous tick count for this thread object
	uint64_t PreviousTickCount;

	//Current tick count for this thread object
	uint64_t CurrentTickCount;

	//Boolean field which, when set to true, indicates the thread is initialized and running.
	bool bIsRunning;

	//Boolean field which, when set to true, indicates the thread has called and returned from initThread()
	bool bIsInitialized;

	//List of callbacks that will occur when the ZThread object runs.
	ZList< ZPtr<ZThreadRequest> > ZTRCallbacks;		// ZTR callbacks
	ZList< ZThreadCallbackRequest > Callbacks;		// Simple (non-object) callbacks

	/*
	Proxy method called by StartThread() which will call initThread and Run on the ZThread Object.

	@param _runnable - a pointer to the ZThread object to run
	@return - integer condition (not used)
	*/
	static int InitThread(void *_runnable);
};

#endif

