// Listenable.cpp 

#include "Listenable.h"

#include <Debug.h>
#include <stdexcept>
#include <stdio.h>

__USE_CORTEX_NAMESPACE

Listenable::~Listenable() {
// argh.
// now that Listenable relies on ILockable for locking, operations
// in the dtor aren't possible.
// it's up to the derived class to notify listeners of its deletion

//	PRINT((
//		"~Listenable\n"));
//	// notify all listeners of my demise, and make sure they know
//	// before giving up
//	BMessage m(M_DELETED);
//	tellListeners(&m, true);
//	PRINT((
//		"- ~Listenable done\n"));
}

status_t Listenable::addListener(BHandler* handler) {
	ASSERT(handler);

	// try to create messenger
	status_t error;
	BMessenger m(handler, NULL, &error);
	if(error != B_OK) {
		PRINT((
			"* Listenable::addListener(): BMessenger creation failed:\n"
			"  %s\n",
			strerror(error)));
		return error;
	}

	// add to the list
	Autolock _l(this);
	m_listeners.push_back(listener_pair(handler,m));
	return B_OK;
}

status_t Listenable::removeListener(BHandler* handler) {
	ASSERT(handler);

	lock();	

	// look for listener
	list<listener_pair>::iterator it;
	for(it = m_listeners.begin();
		it != m_listeners.end();
		it++) {
		if((*it).first == handler)
			break;
	}
	
	// if found, remove; otherwise scream
	ASSERT(it != m_listeners.end());
	m_listeners.erase(it);
		
	unlock();
	return B_OK;
}

// +++++ adjustable timeout would be nice for synchronous calls!

void Listenable::tellListeners(
	BMessage* message,
	bool synchronous) const {
	
	ASSERT(isLocked());

	for(list<listener_pair>::const_iterator it = m_listeners.begin();
		it != m_listeners.end();
		it++) {
	
		if(synchronous) {
			BMessage reply(B_NO_REPLY);
			status_t ret = 
				(*it).second.SendMessage(
					message, &reply,
					B_INFINITE_TIMEOUT, B_INFINITE_TIMEOUT);

			if(ret != B_OK ||
				reply.what == B_NO_REPLY) {
				
				char buffer[120];
				sprintf(buffer, "(%s)::tellListeners(): "
					"SendMessage() failed (synchronous): %ld.\n",
					class_name(this), ret);
					
				throw runtime_error(buffer);
			}
			ASSERT(reply.IsReply());
		} else {
			status_t ret = (*it).second.SendMessage(message);
			if(ret !=	B_OK) {
			
				char buffer[120];
				sprintf(buffer, "(%s)::tellListeners(): "
					"SendMessage() failed [%s]\n",
					class_name(this),
					(ret == B_BAD_PORT_ID) ? "bad port id" : "unknown error");
					
				throw runtime_error(buffer);
			}
		}
	}
}

// directly access the list of all listeners
// (must be locked)
const list<Listenable::listener_pair>& Listenable::listeners() const {
	ASSERT(isLocked());
	return m_listeners;
}

// END -- Listenable.cpp --
