/**
  *  MIX IN: Event.Broker
  *  (c) 2006 Seth Dillingham <seth.dillingham@gmail.com>
  *
  *  This software is hereby released into the public domain. Do with it as
  *  you please, but with the understanding that it is provided "AS IS" and 
  *  without any warranty of any kind.
  *  
  *  (But I'd love to be told about where and how this code is being used.)
  **/
  
/**
  *  Description:
  *    Allows listeners to subscribe to event types, and remain
  *    blissfully unaware of the available publishers of those events
  *
  *  Requires prototype.js
  *  
  *  Usage:
  *    You'll usually want to create a copy of the Broker object by mixing 
  *      it in with one of your own classes as follows:
  *      Object.extend( your_obj, Event.Broker )
  *    
  *    Publishers register the event types they send with a call to 
  *      your_broker_object.registerEventsPublisher
  *      and "unregister" via your_broker_object.unregisterEventsPublisher
  *   
  *    Nothing changes for the event listeners, except they call their
  *      listen() method once for each event type (passing a reference
  *      to the broker) instead of once per event type per publisher
  **/

Event.Broker = Class.create();
Object.extend( Event.Broker, {
	_listeners: null,
	
	_publishers: null,
	
	_initListenerType: function( event_type ) {
		if ( this._listeners == null )
			this._listeners = {};
		
		if ( typeof( this._listeners[ event_type ] ) == "undefined" )
			this._listeners[ event_type ] = new Array();
	},
	
	_initPublisherType: function( event_type ) {
		if ( this._publishers == null )
			this._publishers = {};
		
		if ( this._publishers[ event_type ] == undefined )
			this._publishers[ event_type ] = new Array();
	},
	
	/**
	 *  Register a publisher with the broker. Listeners that want the types
	 *  of events produced by this publisher will be subscribed automatically.
	 *  
	 *  Params:
	 *    event_types: an event type, or an arry of event types,
	 *      which are published by the publsher
	 *    publisher: the publisher object being registered with the broker
	 *      (must mix in Event.Publisher, or implement the same public interface)
	**/
	registerEventsPublisher: function( event_types, publisher ) {
		if ( typeof( event_types ) != typeof( [] ) )
			event_types = [event_types];
		
		event_types.each( function( event_type ) {
			this._initPublisherType( event_type );
			
			this._publishers[ event_type ].push( publisher );
			
			this._initListenerType( event_type );
			
			this._listeners[ event_type ].each( function( listener_rec ) {
				publisher.addEventListener( event_type, listener_rec.listener, listener_rec.useCapture );
				}
			);
		}.bind( this ) );
	},
	
	/**
	 *  Unregister a publisher with the broker. Listeners that had been
	 *  automatically subscribed to the publisher will be un-subscribed.
	 *  
	 *  Params:
	 *    event_types: an event type, or an arry of event types,
	 *      which are published by the publsher
	 *    publisher: the publisher object being un-registered with the broker
	**/
	unregisterEventsPublisher: function( event_types, publisher ) {
		if ( typeof( event_types ) != typeof( [] ) )
			event_types = [event_types];
		
		event_types.each( function( event_type ) {
			this._listeners[ event_type ].each( function( listener_rec ) {
				publisher.removeEventListener( event_type, listener_rec.listener, listener_rec.useCapture );
			} );
			
			var ix = this._publishers[ event_type ].indexOf( publisher );
			
			if ( ix > -1 )
				this._publishers[ event_type ].splice( ix, 1 );
		}.bind( this ) );
		
		return;
	},
	
	/**
	 *  Register a listener with the broker. Causes the listener
	 *  to be automatically registered with all publishers that produce 
	 *  the specified event_type.
	 *  
	 *  You shouldn't have to  call this from your own code: it's
	 *  called automatically when your listener listens for events
	 *  from the broker.
	 *  
	 *  See Event.Listener.listenForEvent
	**/
	addEventListener: function( event_type, event_listener, useCapture ) {
		this._initListenerType( event_type );
		
		this._listeners[ event_type ].push( {
			listener: event_listener,
			useCapture: useCapture
		} );
		
		this._initPublisherType( event_type );
		
		this._publishers[ event_type ].each( function( publisher ) {
			publisher.addEventListener( event_type, event_listener, useCapture );
		} );
	},
	
	/**
	 *  Un-register a listener with the broker. The listener is
	 *  "unsubscribed" from all publishers of the given event_type
	 *  
	 *  You shouldn't have to  call this from your own code: it's
	 *  called automatically when your listener stops listening
	 *  for events from the broker.
	 *  
	 *  See Event.Listener.stopListeningForEvent
	**/
	removeEventListener: function( event_type, event_listener, useCapture ) {
		this._publishers[ event_type ].each( function( publisher ) {
			publisher.removeEventListener( event_type, event_listener, useCapture );
		} );
		
		var ix_listener = -1;
		
		this._listeners[ event_type ].each( function( listener_rec, ix ) {
			ix_listener = ix;
			throw $break;
		} );
		
		if ( ix_listener > -1 )
			this._listeners[ event_type ].splice( ix_listener, 1 );
		
		return;
	},
	
	/**
	 *  tracing/debugging feature only
	 *  
	 *  toggles event tracing on all of the publishers of a given type of event
	 *
	 *  note: this is a first pass at this feature. Since it toggles
	 *  the trace feature, toggling event tracing on two different event_types 
	 *  could activate trace for an object via the first event type, and then
	 *  deactivate it with the second event type
	**/
	toggleEventPublishersTrace: function( event_type ) {
		this._initPublisherType( event_type );
		
		this._publishers[ event_type ].each( function( publisher ) {
			publisher.toggleEventsTrace();
		} );
	}
} );
