/* 2.0.3.2045 */

/* This is a publisher subscriber messaging system.
 *
 * We do not have a p2p messaging system. There are only a few
 * cases where we do p2p and in all those cases, the consumers
 * instantiate the producers and we pass the consumer to the
 * producer as an argument. Therefore, the producer always knows
 * the consumer and can simply call the consumer directly. Adding
 * a p2p messaging system would simply add overhead without extra
 * benefit.
 *
 * Each message may have multiple publishers, however, the message
 * system will not tell subscribers which publisher generated the
 * message.
 * 
 * There are occassions where multiple publishers may publish the
 * same topic. Subscribers need a way to pick out the messages
 * meant for them. This is accomplished through lots. A publisher
 * may publish a topic to a certain lot. In this case, only subscribers
 * who have subscribe to that topic in that lot will receive
 * the message. Messages published without lots will go into
 * a default lot.
 *
 * In most cases, js objects that share a lot will probably be
 * in the same object subtree.
 *
 * There is no queueing. We notify subscribers the moment we get
 * a message. This shouldn't be a problem for us; users are unlikely
 * to move so fast that we'd have two messages in rapid succession that
 * might get out of sync.
 */

eds.webapp.message.PubSub = function() {

	var DEFAULT_LOT = 'no lot';

	var topics = {};

	function getLot(lot) {

		if ( ( !lot ) || ( lot == 'null' ) ) {
			return DEFAULT_LOT;
		}
		return lot;
	}

	this.publish = function(topic, lot) {

		lot = getLot(lot);

		if ( topics[lot] && topics[lot][topic] ) {

			/* if another publisher has already published this
			 * particular event, simply return so we don't
			 * clobber the subscription list.
			 */
			return;
		}

		if ( typeof(topics[lot]) == 'undefined' ) {

			topics[lot] = {};
		}

		topics[lot][topic] = [];
	}

	this.isPublished = function(topic, lot) {

		lot = getLot(lot);

		if ( typeof(topics[lot]) == 'undefined' ) {
			return false;
		}

		return typeof(topics[lot][topic]) != 'undefined';
	}

	this.subscribe = function(topic, subscriber, lot) {
		var len;

		if ( !subscriber ) {
			throw "must provide a non-null subscriber";
		}
		
		if ( !topic ) {
			throw subscriber + " tried to subscribe to a null topic"
		}

		lot = getLot(lot);

		if ( !this.isPublished(topic, lot) ) {
			throw subscriber + " tried to subscribe to an unpublished topic " +
				topic;
		}

		len  = topics[lot][topic].length;
		topics[lot][topic][len] = subscriber;
	}


	this.dumpSubscriptions = function() {

		var str = '';

		for ( var lotName in topics ) {

			var lot = topics[lotName];

			str += "Lot: " + lotName + "\n";

			for ( var topicName in lot ) {

				var subscribers = lot[topicName];

				str += "\tTopic: " + topicName + "\n";
				str += "\tListeners:\n";

				for ( var jndex in subscribers ) {
					str += "\t\t" + subscribers[jndex] + "\n";
				}
				str += "\n";
			}
		}
		return str;
	}

	this.sendMessage = function(message, lot) {

		var topic = message.getTopic();

		lot = getLot(lot);

		var subscribers = topics[lot][topic];

		if ( !subscribers ) {
			return;
		}

		for ( var index in subscribers ) {
			subscribers[index].receiveMessage(message);
		}
	}


	this.teardown = function() {

		for ( var lotName in topics ) {

			var lot = topics[lotName];

			for ( var topicName in lot ) {

				var subscribers = lot[topicName];

				for ( var jndex in subscribers ) {
					delete subscribers[jndex];
				}

				delete topics[lotName][topicName];
			}

			delete topics[lotName];
		}

		delete topics;
	}
}

