#ifndef EVENTS_H_INCLUDED
#define EVENTS_H_INCLUDED

#include <assert.h>
#include "../utils/mixins.hh"

#ifndef my_assert
#define my_assert(x) assert(x)
#endif

/// Double linked cyclic list implementation.
/// Beware! When a node is copied (duplicated), the copy is inserted after original into the list!
/// This is done to make it work when you're storing nodes in some container which invokes copy constructor on components.
class ListNodeBare {
	public:

		ListNodeBare * prev;
		ListNodeBare * next;// first and last for holder.
		inline void ConnectNextTo( ListNodeBare *a ) {/// connecting next to itself is equivalent to disconnect.
			Disconnect();
			next = a->next;
			prev = a;
			next->prev = this;
			prev->next = this;
		}
		inline void ConnectPrevTo( ListNodeBare *a ) {/// connecting prev to itself is equivalent to disconnect.
			Disconnect();
			next = a;
			prev = a->prev;
			next->prev = this;
			prev->next = this;
		}
		inline void StepNext() {/// moves itself forward in the ring
			ListNodeBare *a = next;

			next->prev = prev;/// disconnect itself
			prev->next = next;

			next = a->next;/// reconnect itself after next
			prev = a;
			next->prev = this;
			prev->next = this;
		}
		inline void StepPrev() {/// moves itself backward in the ring
			ListNodeBare *a = prev;

			next->prev = prev;/// disconnect itself
			prev->next = next;

			next = a;/// reconnect itself before prev
			prev = a->prev;
			next->prev = this;
			prev->next = this;
		}

		inline void SanityCheck() {
			my_assert( prev != 0 );
			my_assert( next != 0 );
			my_assert( prev->next == this );
			my_assert( next->prev == this );
		}

		inline void Disconnect() {
			if ( prev != this ) {//if we're connected
				my_assert( prev != 0 );
				my_assert( next != 0 );
				my_assert( next != this );
				next->prev = prev;
				prev->next = next;
				// short itself.
				prev = this;
				next = this;
			} else {
				// we're not connected,must be so on both ends.
				my_assert( next == this );
			};
		}
		ListNodeBare()
			: prev( this ),
			next( this )
		{}
		virtual ~ListNodeBare() {
			Disconnect();
		}

		ListNodeBare( const ListNodeBare &a ) {
			next = a.next;
			prev = const_cast<ListNodeBare *>( &a );
			next->prev = this;
			prev->next = this;
		}

		ListNodeBare& operator= ( const ListNodeBare &a ) {
			if( this != &a ) {
				next = a.next;
				prev = const_cast<ListNodeBare *>( &a );
				next->prev = this;
				prev->next = this;
			}
			return *this;
		}
};

/// A base class for event receivers
template<class TParamType>
class EventReceiverFuncBase: public ListNodeBare {
	public:
		virtual void OnEvent( TParamType /*param*/ ) {}
};

/// use EventSender::SendEvent to send events.
template<class TParamType>
class EventSender: public EventReceiverFuncBase<TParamType> {
	public:
		EventSender()
			:m_enabled(true)
		{}

		void OnEvent( TParamType /*param */) {
			/// you may want to put assert(0) here, or log a message.
			/// This method is only called when you have multiple senders sharing same event list.
			/// Which happens when you unnecessarily make a copy of sender.
		}
		void SendEvent( TParamType param ) {
			//std::cout<<"SendEvent called "<<std::endl;
			//ListNodeBare *pt=next;
			ListNodeBare *end = static_cast<ListNodeBare *>( this );
			//my_assert(pt->prev==end);
			EventReceiverFuncBase<TParamType> iterator_node;
			iterator_node.ConnectNextTo( end );
			while ( m_enabled && ( iterator_node.next != end ) ) {

				iterator_node.SanityCheck();

				( static_cast<EventReceiverFuncBase<TParamType> *>( iterator_node.next ) )->OnEvent( param );
				iterator_node.StepNext();
			}
		}

		void Enable( bool enable = true )
		{
			m_enabled = enable;
		}

	protected:
		bool m_enabled;
};

/// Use EventReceiverFunc<base_class, parameter_type, &base_class::method> to make event receiver adaptor
template < class TReceiverType, class TParamType, void ( TReceiverType::*TMethod )( TParamType ) >
class EventReceiverFunc: public EventReceiverFuncBase<TParamType>, public SL::NonCopyable
{
		TReceiverType *receiver_obj;
	public:
		EventReceiverFunc( TReceiverType *receiver_obj_, EventSender<TParamType> *sender = 0 ):
				receiver_obj( receiver_obj_ )
		{
			if ( sender ) {
				ListNodeBare *a = static_cast<ListNodeBare *>( sender );
				ListNodeBare::ConnectPrevTo( a );
			}
		}

		void Connect( EventSender<TParamType> &sender ) {
			Connect( &sender );
		}

		void Connect( EventSender<TParamType> *sender ) {
			if ( sender ) {
				ListNodeBare *a = static_cast<ListNodeBare *>( sender );
				ListNodeBare::ConnectPrevTo( a );
			} else {
				ListNodeBare::Disconnect();
			}
		}

		virtual void OnEvent( TParamType param ) {
			if ( receiver_obj ) {
				( receiver_obj->*TMethod )( param );/// TMethod is constant here at compile time, so chances are the call is inlined. It is faster than standard pointer-to-member function call.
			}
		}
};

/// Use EventReceiverFunc<base_class, parameter_type, &base_class::method> to make event receiver adaptor
template < class TReceiverType, class TParamType, class TAuxParamType, void ( TReceiverType::*TMethod )( TParamType, TAuxParamType ) >
class EventReceiverFuncAux: public EventReceiverFuncBase<TParamType>
{
		TReceiverType *receiver_obj;
	public:
		TAuxParamType aux_param;
		EventReceiverFuncAux( TReceiverType *receiver_obj_, EventSender<TParamType> *sender = 0, TAuxParamType aux_param_ = TAuxParamType() ):
				receiver_obj( receiver_obj_ ),
				aux_param( aux_param_ )
		{
			if ( sender ) {
				ListNodeBare *a = static_cast<ListNodeBare *>( sender );
				ListNodeBare::ConnectPrevTo( a );
			}
		}

		void Connect( EventSender<TParamType> &sender ) {
			Connect( &sender );
		}

		void Connect( EventSender<TParamType> *sender ) {
			if ( sender ) {
				ListNodeBare *a = static_cast<ListNodeBare *>( sender );
				ListNodeBare::ConnectPrevTo( a );
			} else {
				ListNodeBare::Disconnect();
			}
		}

		virtual void OnEvent( TParamType param ) {
			if ( receiver_obj ) {
				( receiver_obj->*TMethod )( param, aux_param );/// TMethod is constant here at compile time, so chances are the call is inlined. It is faster than standard pointer-to-member function call.
			}
		}
};



#endif // EVENTS_H_INCLUDED

/**
    This file is part of SpringLobby,
    Copyright (C) 2007-2011

    SpringLobby is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 as published by
    the Free Software Foundation.

    SpringLobby is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with SpringLobby.  If not, see <http://www.gnu.org/licenses/>.
**/

