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


#ifndef	__NMIXER_THREAD_CONTROLLER__
#define	__NMIXER_THREAD_CONTROLLER__


		#include "core/mixer.h"
		#include "audio/sample_interface.h"
		#include "nstring/nstring.h"
		#include "thread/mutex.h"


namespace GS {
	namespace Audio {

//
struct	MixerVariant : public SharedObject
{
		union
		{
			int		i_value;
			float	f_value;
		};

		AutoPtr <FutureData> future_data;

		MixerVariant() : i_value(0) {}
		MixerVariant(int v) : i_value(v) {}
		MixerVariant(float v) : f_value(v) {}
		MixerVariant(FutureData *f) : future_data(f) {}
};

typedef	SharedPtr <MixerVariant> sMixerVariant;

//
struct	MixerCommand
{
		enum	Code
		{	Nop = 0, LoadSound, UnloadSound, Start, Stream, GetState, Pause, Stop, Resume, Lock, Unlock, Close,
			SetMaster, SetVolume, SetPitch, SetPanning, SetLoopMode, SetLoopPosition,
			GetMaster, GetVolume, GetPitch, GetPanning, GetLoopMode, GetLoopPosition	};

		Code		code;
		int			channel;

		String		uri;

		union
		{
			bool	b_value;
			float	f_value;
			int		i_value;

		};

		sData		data;
		ASync::Future <MixerVariant *> *result;

		MixerCommand() {}
		MixerCommand(Code c) : code(c) {}
		MixerCommand(Code c, float v) : code(c), f_value(v) {}
		MixerCommand(Code c, const char *name) : code(c), uri(name) {}
		MixerCommand(Code c, Data *_data) : code(c), data(_data) {}
		MixerCommand(Code c, int n) : code(c), channel(n) {}
		MixerCommand(Code c, int n, Data *d) : code(c), channel(n), data(d) {}
		MixerCommand(Code c, int n, bool v) : code(c), channel(n), b_value(v) {}
		MixerCommand(Code c, int n, float v) : code(c), channel(n), f_value(v) {}
		MixerCommand(Code c, int n, int v) : code(c), channel(n), i_value(v) {}
		MixerCommand(Code c, int n, const char *name) : code(c), channel(n), uri(name) {}
};

/*!
	@short	Threaded mixer controller.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
template <class _Thread> class ThreadedMixer : public IMixer
{
		AutoPtr <_Thread> thread;

		bool		PutCommand(MixerCommand *cmd, MixerVariant **result = 0)
		{
			if (thread.IsNull() || !thread->device)
				return false;

			ASync::Future <MixerVariant *> future_result;

			forever
			{
				Threading::MutexLock lock(&thread->cmdbuffer_mutex);

				if	(thread->cmdbuffer.GetFree() > 0)
					if	(MixerCommand **put = &thread->cmdbuffer.CurrentPut())
					{
						*put = cmd;
						cmd->result = result ? &future_result : NULL;		// Future is to be Set() by the worker thread.

						thread->cmdbuffer.Produce();
						thread->cmdbuffer_event.Trigger();
						break;
					}
			}

			if	(result)
				*result = future_result.Get();

			return true;
		}
		MixerVariant *PutCommandAndWaitResult(MixerCommand *cmd)
		{
			MixerVariant *result;
			return PutCommand(cmd, &result) ? result : 0;
		}

public:

virtual	bool		Open()
		{
			if	(thread.IsValid())
				return true;

			thread = new _Thread;
			return thread->Start();
		}
virtual	void		Close()
		{
			PutCommand(new MixerCommand(MixerCommand::Close));
			if	(thread.IsValid())
				thread->Join();
			thread = 0;
		}

virtual	void		SuspendWorkerThread()
		{	thread->SuspendMixer();	}
virtual	void		ResumeWorkerThread()
		{	thread->ResumeMixer();	}

virtual	void		SetMasterVolume(float volume = 1.0)
		{	PutCommand(new MixerCommand(MixerCommand::SetMaster, volume));	}
virtual	float		GetMasterVolume()
		{
			AutoPtr <MixerVariant> v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetMaster)));
			return v.IsValid() ? v->f_value : 1;
		}
virtual	bool		StartFast(int channel, FutureData *data)
		{	return data ? PutCommand(new MixerCommand(MixerCommand::Start, channel, data->Get())) : false;	}
virtual	int			Start(int channel, FutureData *data)
		{
			AutoPtr <MixerVariant> v(data ? PutCommandAndWaitResult(new MixerCommand(MixerCommand::Start, channel, data->Get())) : 0);
			return v.IsValid() ? v->i_value : -1;
		}
virtual	bool		StreamFast(int channel, const char *uri)
		{	return PutCommand(new MixerCommand(MixerCommand::Stream, channel, uri));	}
virtual	int			Stream(int channel, const char *uri)
		{
			AutoPtr <MixerVariant> v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::Stream, channel, uri)));
			return v.IsValid() ? v->i_value : -1;
		}

virtual	void		Stop(int channel)
		{	PutCommand(new MixerCommand(MixerCommand::Stop, channel));	}
virtual	void		Pause(int channel)
		{	PutCommand(new MixerCommand(MixerCommand::Pause, channel));	}
virtual	void		Resume(int channel)
		{	PutCommand(new MixerCommand(MixerCommand::Resume, channel));	}

virtual	int			LockChannel()
		{
			AutoPtr <MixerVariant> v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::Lock)));
			return v.IsValid() ? v->i_value : -1;
		}
virtual	void		UnlockChannel(int channel)
		{	PutCommand(new MixerCommand(MixerCommand::Unlock, channel));	}
virtual	void		UnlockAllChannels()
		{
			for	(int n = 0; n < 64; ++n)
				PutCommand(new MixerCommand(MixerCommand::Unlock, n));
		}

virtual	float		GetChannelPitch(int channel)
		{
			AutoPtr <MixerVariant> v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetPitch, channel)));
			return v.IsValid() ? v->f_value : 1;
		}
virtual	float		GetChannelVolume(int channel)
		{
			AutoPtr <MixerVariant> v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetVolume, channel)));
			return v.IsValid() ? v->f_value : 1;
		}
virtual	float		GetChannelPanning(int channel)
		{	return -1;	}
virtual	Loop		GetChannelLoopMode(int channel)
		{
			AutoPtr <MixerVariant> v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetLoopMode, channel)));
			return v.IsValid() ? (v->i_value == AL_TRUE ? Repeat : None) : None;
		}
virtual	int			GetChannelLoopPosition(int channel)
		{	return 0;	}

virtual	void		SetChannelPitch(int channel, float pitch)
		{	PutCommand(new MixerCommand(MixerCommand::SetPitch, channel, pitch));	}
virtual	void		SetChannelVolume(int channel, float volume)
		{	PutCommand(new MixerCommand(MixerCommand::SetVolume, channel, volume));	}
virtual	void		SetChannelPanning(int channel, float panning)
		{	PutCommand(new MixerCommand(MixerCommand::SetPanning, channel, panning));	}
virtual	void		SetChannelLoopMode(int channel, Loop loop = None)
		{	PutCommand(new MixerCommand(MixerCommand::SetLoopMode, channel, loop == IMixer::Repeat ? AL_TRUE : AL_FALSE));	}
virtual	void		SetChannelLoopPosition(int, int ms)
		{}

virtual	State		GetState(int channel)
		{
			AutoPtr <MixerVariant> v(PutCommandAndWaitResult(new MixerCommand(MixerCommand::GetState, channel)));

			if	(v.IsValid())
				switch (v->i_value)
				{
					case AL_INITIAL:
					case AL_STOPPED:	return Stopped;
					case AL_PAUSED:		return Paused;
					case AL_PLAYING:	return Playing;
				}

			return Invalid;
		}

virtual	FutureData	*LoadSound(const char *uri)
		{
			AutoPtr <MixerVariant> v(uri ? PutCommandAndWaitResult(new MixerCommand(MixerCommand::LoadSound, uri)) : 0);
			return v.IsValid() ? v->future_data.Detach() : 0;
		}
virtual	void		UnloadSound(FutureData *data)
		{
			AutoPtr <MixerVariant> v(data ? PutCommandAndWaitResult(new MixerCommand(MixerCommand::UnloadSound, data->Get())) : 0);
		}

		~ThreadedMixer()
		{	Close();	}
};

	} // Audio
} // GS


#endif	// __NMIXER_THREAD_CONTROLLER__
