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


#ifndef	__SIGNAL__
#define	__SIGNAL__


		#include "container/nlist.h"


namespace GS {

//
template <typename T> class Signal
{
		/// Base signal listener.
		struct	IListener
		{
		virtual	void	Call(T) = 0;

		virtual	bool	BelongsToInstance(void *) const = 0;
		virtual	bool	IsFunction(void (*)(T)) const = 0;

		virtual	~IListener() {}
		};

		AutoList <IListener *> listeners;

		/// Pointer to member listener.
		template <class O, typename T1> struct InstanceListener : public IListener
		{
				O		*i;
				void (O::*f)(T1);

				void	Call(T1 t) { (i->*f)(t); }

				bool	BelongsToInstance(void *p) const { return asbool(p == (void *)i); }
				bool	IsFunction(void (*)(T1)) const { return false; }

				InstanceListener(O *_i, void (O::*_f)(T1)) : i(_i), f(_f) {}
		};

		/// Global/static function listener.
		template <typename T1> struct FunctionListener : public IListener
		{
				void (*f)(T1);

				void	Call(T1 t) { (*f)(t); }

				bool	BelongsToInstance(void *) const { return false; }
				bool	IsFunction(void (*_f)(T1)) const { return f == _f; }

				FunctionListener(void (*_f)(T1)) : f(_f) {}
		};

public:

		/// Return the number of listener on this signal.
		uint		GetListenerCount() const { return listeners.GetCount(); }

		/// Subscribe an instance member function to call when the event is triggered.
		template <class O> void Connect(O *i, void (O::*f)(T))
		{	listeners.Add(new InstanceListener <O, T> (i, f));	}
		/// Remove the subscription of all handlers associated with a specific class instance.
		template <class O> void Disconnect(O *i)
		{
			ListForeachPtr(IListener *, l, this->listeners)
				if	(l->BelongsToInstance((void *)i))
					listeners.Remove(l);
		}

		/// Subscribe a function to call when the event is triggered.
		void		Connect(void (*f)(T))
		{	listeners.Add(new FunctionListener <T> (f));	}
		/// Remove a function subscription.
		void		Disconnect(void (*f)(T))
		{
			ListForeachPtr(IListener *, l, this->listeners)
				if	(l->IsFunction(f))
					listeners.Remove(l);
		}

		/// Emit the signal, notifies all listeners.
		void		Emit(T t) const
		{
			ListForeachPtr(IListener *, l, listeners)
				l->Call(t);
		}
};

// Specialization for void type. Note: I gave up on factoring code => compiler vendor/extension hell.
template <> class Signal <void>
{
		/// Base signal listener.
		struct	IListener
		{
		virtual	void	Call() = 0;

		virtual	bool	BelongsToInstance(void *) const = 0;
		virtual	bool	IsFunction(void (*)()) const = 0;

		virtual	~IListener() {}
		};

		AutoList <IListener *> listeners;

		/// Pointer to member listener.
		template <class O> struct InstanceListener : public IListener
		{
				O		*i;
				void (O::*f)();

				void	Call() { (i->*f)(); }

				bool	BelongsToInstance(void *p) const { return asbool(p == (void *)i); }
				bool	IsFunction(void (*)()) const { return false; }

				InstanceListener(O *_i, void (O::*_f)()) : i(_i), f(_f) {}
		};

		/// Global/static function listener.
		struct FunctionListener : public IListener
		{
				void (*f)();

				void	Call() { (*f)(); }

				bool	BelongsToInstance(void *) const { return false; }
				bool	IsFunction(void (*_f)()) const { return f == _f; }

				FunctionListener(void (*_f)()) : f(_f) {}
		};

public:

		/// Return the number of listener on this signal.
		uint		GetListenerCount() const { return listeners.GetCount(); }

		/// Subscribe an instance member function to call when the event is triggered.
		template <class O> void Connect(O *i, void (O::*f)())
		{	listeners.Add(new InstanceListener <O> (i, f));	}
		/// Remove the subscription of all handlers associated with a specific class instance.
		template <class O> void Disconnect(O *i)
		{
			ListForeachPtr(IListener *, l, listeners)
				if	(l->BelongsToInstance((void *)i))
					listeners.Remove(l);
		}

		/// Subscribe a function to call when the event is triggered.
		void		Connect(void (*f)())
		{	listeners.Add(new FunctionListener(f));	}
		/// Remove a function subscription.
		void		Disconnect(void (*f)())
		{
			ListForeachPtr(IListener *, l, listeners)
				if	(l->IsFunction(f))
					listeners.Remove(l);
		}

		/// Emit the signal, notifies all listeners.
		void		Emit() const
		{
			ListForeachPtr(IListener *, l, listeners)
				l->Call();
		}
};

} // GS


#endif	// __SIGNAL__
