/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
****************************************************************************/

#pragma once

#include "glmCoreAPI.h"
#include "glmPODArray.h"

namespace glm
{
	/**
	* \brief Abstract base class of all event sources.
	*
	* An event source (i.e., publisher) has two responsibilities:
	* 1. Register listener objects.
	* 2. Notify listener objects when an event happens by sending them a message.	
	*
	* \ingroup events
	* \author Golaem <dev@golaem.com>
	*/
	template < class TListener, class TEvent >
	class EventSource
	{
		typedef EventSource<TListener, TEvent> eventsource_type;

		/// pointer to the callback implemented by a listener to process a published event.
		typedef void ( TListener ::*ProcessEventFunc )( const TEvent& ) ;

	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared);

		/**
		* \brief constructor
		* \param processEvent pointer to the TListener method processing the event
		*/
		EventSource( ProcessEventFunc processEvent ) ;

		/**
		* \brief destructor
		*/
		virtual ~EventSource() ;

		/**
		* \brief subscribe a listener to the publication of the event
		* \param listener reference to the listener instance
		*/
		void addListener( TListener& listener ) ;

		/**
		* \brief unsubscribe a listener to the publication of the event
		* \param listener reference to the listener instance
		*/
		void removeListener( TListener& listener ) ;

		/**
		* \brief get accessor to the listeners
		* \return a const reference to a vector of Listeners
		*/
		const glm::PODArray<TListener*>& getListeners() const ;

		/**
		* \brief set accessor to the listeners
		* \param listeners the vector of listeners to set
		*/
		void setListeners( const glm::PODArray<TListener*>& listeners ) ;

		/**
		* \brief get the number of listeners
		* \return the listeners count
		*/
		unsigned int getListenersCount() const ;

		/**
		* \brief publish the event to the subscribed listeners
		* \param event a const reference to the event to publish
		* while processing a published event in its ProcessEventFunc callback.
		*/
		void publishEvent( const TEvent& event ) ;

	protected:

		EventSource() {}

		/// pointer to the TListener method processing the event
		ProcessEventFunc _processEvent ; 

		/// pointers to the added listeners interface
		glm::PODArray< TListener* > _listeners ;
	} ;

	//-------------------------------------------------------------------------
	// Constructor
	//-------------------------------------------------------------------------
	template < class TListener, class TEvent >
	EventSource< TListener, TEvent >::EventSource( ProcessEventFunc processEvent )
		: _listeners(getClassMemoryStatId())
	{ 
		_processEvent = processEvent ; 
	}

	//-------------------------------------------------------------------------
	// Destructor
	//-------------------------------------------------------------------------
	template < class TListener, class TEvent >
	EventSource< TListener, TEvent >::~EventSource()
	{
	}

	//-------------------------------------------------------------------------
	// addListener
	//-------------------------------------------------------------------------
	template < class TListener, class TEvent >
	void
		EventSource< TListener, TEvent >::addListener( TListener& listener ) 
	{ 
		_listeners.push_back( &listener ); 
	}

	//-------------------------------------------------------------------------
	// removeListener
	//-------------------------------------------------------------------------
	template < class TListener, class TEvent >
	void
		EventSource< TListener, TEvent >::removeListener( TListener& listener ) 
	{ 
		for(size_t iListener = 0, count = _listeners.size(); iListener < count; ++iListener)
		{
			TListener* currentListener = _listeners[iListener];
			if(currentListener == &listener)
			{
				_listeners.erase(_listeners.begin() + iListener);
				break;
			}
		}
	}

	//-------------------------------------------------------------------------
	// getListeners
	//-------------------------------------------------------------------------
	template < class TListener, class TEvent >		
	const glm::PODArray<TListener*>& 
		EventSource< TListener, TEvent >::getListeners() const
	{
		return _listeners ;
	}

	//-------------------------------------------------------------------------
	// setListeners
	//-------------------------------------------------------------------------
	template < class TListener, class TEvent >
	void 
		EventSource< TListener, TEvent >::setListeners( const glm::PODArray<TListener*>& listeners )
	{
		_listeners = listeners ;
	}

	//-------------------------------------------------------------------------
	// getListenersCount
	//-------------------------------------------------------------------------
	template < class TListener, class TEvent >
	unsigned int 
		EventSource< TListener, TEvent >::getListenersCount() const
	{
		return static_cast<unsigned int>( _listeners.size() ) ;
	}

	//-------------------------------------------------------------------------
	// publishEvent
	//-------------------------------------------------------------------------
	template < class TListener, class TEvent >
	void
		EventSource< TListener, TEvent >::publishEvent( const TEvent& event )
	{
		typename glm::PODArray< TListener* >::iterator ite( _listeners.begin() ) ;
		for ( ; ite != _listeners.end() ; ++ite )
		{
			( ( *ite )->*_processEvent )( event );
		}
	}
}

