/* (c) 1999-2000 Tino Schwarze, see COPYING for details */
/**
 * implementation of cEventDispatcher
 */

#include "cEventDispatcher.hh"

//#define DEBUG_SENDEVENT 1
//#define DEBUG_UNREGISTERCONSUMER 1

#ifndef DEBUG_SENDEVENT
#define DEBUG_SENDEVENT	0
#endif

#ifndef DEBUG_UNREGISTERCONSUMER
#define DEBUG_UNREGISTERCONSUMER 0
#endif

class ostream;

/**
 * constructor
 	*/
cEventDispatcher::cEventDispatcher ()
{
}


/**
 * destructor
 	*/
cEventDispatcher::~cEventDispatcher ()
{
}

/**
 * subscribe event consumer to given event
 	*/
void cEventDispatcher::SubscribeToEvent (
	const cEvent &event,
	cEventConsumer *consumer)
{
	// copy the reference cEvent plus it's consumer to the map
	mRegisteredEvents.insert (
			tRegisteredEvents::value_type(cStorableEvent (event), consumer));
}

/**
 * unsubscribe event consumer from given event
 	*/
void cEventDispatcher::UnsubscribeFromEvent (
	const cEvent &event,
	cEventConsumer *consumer)
{
	// find the range of all candidates for this event
	pair<tRegisteredEvents::iterator, tRegisteredEvents::iterator>
		check_range = mRegisteredEvents.equal_range (cStorableEvent (event));

	// process all entries which match the event
	// that is, from check_range.first up to but excluding
	// check_range.second
	
	while (check_range.first != check_range.second)
	{
		// save the matching iterator (it's invalidated by erase)
		tRegisteredEvents::iterator last_it = check_range.first;

		// advance
		check_range.first++;

		// now compare whether the consumer matches
		// (we only want to unregister this particular consumer!)
		if ((*last_it).second == consumer)
		{
			// unsubscribe the consumer (it does not get destroyed!)
			mRegisteredEvents.erase (last_it);
		}

	}

}

/**
 * unsubscribe all events of an consumer
 	*/
void cEventDispatcher::UnregisterConsumer (
	const cEventConsumer *consumer)
{
#if DEBUG_UNREGISTERCONSUMER
	cerr << "cEventDispatcher::UnregisterConsumer (cEventConsumer *): unregistering consumer " << hex << (void *)consumer << dec << endl;

	int unreg_count = 0;
#endif

	// We need to implement this ourself since multimap does not have
	// a function to erase elements by value (only erasing by key
	// is supported)
	// Another approach would be to use the STL algorithm remove_if,
	// but that would require an function object which I don't want
	// to implement. (The approach taken is more straight-forward, I
	// think.
	tRegisteredEvents::iterator it = mRegisteredEvents.begin(),
		end_it = mRegisteredEvents.end ();
	
	while (it != end_it)
	{

		// check whether the consumer matches
		if ((*it).second == consumer)
		{
			// we need to increment first and then erase the element,
			// so we need to keep a copy of the iterator
			tRegisteredEvents::iterator tmp_it = it;

			// advance iterator (erasing would invalidate it - no wonder!)
			it++;

			// erase element
			mRegisteredEvents.erase (tmp_it);
#if DEBUG_UNREGISTERCONSUMER
			unreg_count++;
#endif
		}
		else
		{
			it++;
		}

	}

#if DEBUG_UNREGISTERCONSUMER
	cerr << unreg_count << " subscriptions affected." << endl;
#endif
}

/**
 * send an event (at first, figure out whom it belongs to)
 	*/
void cEventDispatcher::SendEvent (
	const cEvent &event,
	bool global)
{
#if	DEBUG_SENDEVENT
	cerr << "cEventDispatcher::SendEvent (): Looking for consumer for " << event << endl;
#endif

	// find the range of all candidates for this event
	pair<tRegisteredEvents::iterator, tRegisteredEvents::iterator>
		check_range = mRegisteredEvents.equal_range (cStorableEvent (event));

	// process all entries which match the event
	// that is, from check_range.first up to but excluding
	// check_range.second
	while (check_range.first != check_range.second)
	{

#if	DEBUG_SENDEVENT
		cerr << "cEventDispatcher::SendEvent (): sending to " << (void *)(*check_range.first).second << endl;
#endif

		// send the event to the consumer
		// The consumer returns 0 if the event was accepted.
		if (((*check_range.first).second->ReceiveEvent (event) == 0)
			&& (global == false))
		{
#if DEBUG_SENDEVENT
			cerr << event << " accepted." << endl;
#endif
			break;	// only send the event as long as no one accepted it
		}

		// go to next potential consumer
		check_range.first++;
	}

#if	DEBUG_SENDEVENT
	cerr << "cEventDispatcher::SendEvent (): done." << endl;
#endif
}

void cEventDispatcher::DisplayRegistered ()
{
	cout << "list of registered events: " << endl;

	for (tRegisteredEvents::const_iterator it = mRegisteredEvents.begin();
		it != mRegisteredEvents.end();
		it++)
	{
		cout << "event \"" << (*it).first << "\", consumer " << (void *)(*it).second << endl;
	}

	cout << "end of list." << endl;
}

#undef DEBUG_SENDEVENT
#undef DEBUG_UNREGISTERCONSUMER
