/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__THREAD_CONTROLLER__
#define	__THREAD_CONTROLLER__


		#include "thread/atomic_value.h"
		#include "thread/thread_event.h"
		#include "thread/future.h"
		#include "thread/thread.h"
		#include "thread/mutex.h"
		#include "container/nlist.h"


namespace GS {
	namespace Threading {

//------------------------------------------------------------------------------
template <typename Target> struct DefaultControllerPolicy
{
static	const bool	use_event = true;	// use thread event to sleep the worker thread
static	const bool	own_target = false;	// the controller owns the target object and is responsible for deleting it

static	void		Update(Target *) {}
};

template <typename Target, class Policy = DefaultControllerPolicy <Target> > class Controller
{
public:

struct	Command
{
		Atomic32	dispose;
virtual	void	Execute(Target *target) = 0;

		Command() : dispose(1) {}
virtual	~Command() {}
};

template <class Result> struct CommandWithResult : public Command
{
		Future <Result> future_result;
		CommandWithResult() { this->dispose.Set(0); }
};

private:

struct	WorkerThread : public Thread
{
		Target		*target;

		Atomic32	running;

		Mutex		command_mutex;
		Event		command_event;

		AutoList <Command *> command_queue;

		void		Execute()
		{
			forever
			{
				Policy::Update(target);
				{
					MutexLock lock(&command_mutex);
					while (command_queue.GetCount() > 0)
					{
						command_queue[0]->Execute(target);
						while (command_queue[0]->dispose.Get() != 1)
							; // spin lock on command dispose flag
						command_queue.RemoveAt(0);
					}
					if	(running.Get() != 1)
						break;
				}
				if	(Policy::use_event)
					command_event.Wait();
			}
			running.Set(0);
		}
		void		Stop()
		{
			running.Set(2);
			if	(Policy::use_event)
				command_event.Trigger();
			while (running.Get() != 0); // spinlock
		}

		WorkerThread(Target *t) : target(t), running(1) {}
		~WorkerThread()
		{
			if	(Policy::own_target)
				delete target;
		}
};

		WorkerThread worker;

public:

		void		QueueCommand(Command *c)
		{
			{
				MutexLock lock(&worker.command_mutex);
				worker.command_queue.Append(c);
			}
			worker.command_event.Trigger();
		}
		template <class Result> Result QueueCommand(CommandWithResult <Result> *c)
		{
			QueueCommand((Command *)c);

			Result result = c->future_result.Get();		// wait for command result
			c->dispose.Set(1);							// flag command disposal
			return result;								// return result
		}

		bool		Start()
		{	return worker.Start();	}
		void		Stop()
		{	worker.Stop();	}

		Controller(Target *t) : worker(t) {}
};
//------------------------------------------------------------------------------

	} // Threading
} // GS


#endif	// __THREAD_CONTROLLER__
