/*
 * TrueMVC JavaScript with Objs - www.tekool.net
 * TrueMVC JavaScript may be freely distributed under the MIT license.
 * For all details and documentation: https://github.com/tekool/truemvc-javascript
 * Inspired from PureMVC - Copyright(c) 2006-2012 Futurescale, Inc., Some rights reserved.
 */
new function()
{
	var Observer = Objs("truemvc.observer.Observer");
	var Observable = Objs("truemvc.observer.Observable");
	var Mediator = Objs("truemvc.mediator.Mediator");

	/**
	 * @class truemvc.mvc.View
	 *
	 * @classDescription
	 * The <code>View</code> class in TrueMVC.
	 *
	 * <P>
	 * In TrueMVC, the <code>View</code> class assumes these responsibilities:
	 *
	 * <UL>
	 * 	<LI>Maintain a cache of <code>Mediator</code> instances.
	 * 	<LI>Provide methods for registering, retrieving, and removing <code>Mediator</code>s.
	 * 	<LI>Notifiying <code>Mediator</code>s when they are registered or removed.
	 * 	<LI>Managing the <code>Observer</code> lists for each <code>Notification</code> in the application.
	 * 	<LI>Providing a method for attaching <code>Observer</code>s to an <code>Notification</code>'s
	 * 	<code>Observer</code> list.
	 * 	<LI>Providing a method for broadcasting a <code>Notification</code>.
	 * 	<LI>Notifying the <code>Observer</code>s of a given <code>Notification</code> when it broadcasts.
	 *
	 * @see truemvc.mediator.Mediator
	 * @see truemvc.observer.Observer
	 * @see truemvc.observer.Observable
	 * @see truemvc.observer.Notification
	 *
	 * @constructor
	 */
	var View = Objs
	(
		"truemvc.mvc.View",
		{

			/**
			 * The <code>Model</code> this <code>View</code> has to know.
			 *
			 * @type {Model}
			 * @private
			 */
			model: null,

			/**
			 * @private
			 *
			 * Mapping of <code>Mediator</code> names to <code>Mediator</code> instances.
			 *
			 * @type {Object}
			 */
			mediatorMap: null,

			/**
			 * @private
			 *
			 * Observable composite object for the <code>View></code>.
			 *
			 * @type {Observable}
			 */
			observable: null,

			/**
			 * @constructs
			 * Initialize a <code>View</code> instance.
			 */
			initialize: function()
			{
				this.mediatorMap = {};
				this.observable = new Observable();
			},

			/**
			 * Get the <code>Model</code> instance for this <code>View</code>.
			 *
			 * @return {Model}
			 * 		The <code>Model</code> instance set for this <code>View</code>.
			 */
			getModel: function()
			{
				return this.model;
			},

			/**
			 * Set the <code>Model</code> instance for this <code>View</code>.
			 *
			 * @param {Model} model
			 * 		The <code>Model</code> instance to set for this <code>View</code>.
			 */
			setModel: function( model )
			{
				this.model = model;
			},

			/**
			 * Register an <code>Observer</code> to be notified of <code>Notification</code>s with a given name.
			 *
			 * @param {String} name
			 * 		The name of the notification to notify this <code>Observer</code> of.
			 *
			 * @param {Observer} observer
			 * 		The <code>Observer</code> to register.
			 */
			registerObserver: function( name, observer )
			{
				this.observable.registerObserver( name, observer );
			},

			/**
			 * Notify the <code>Observer</code>s for a particular notification (name).
			 *
			 * <P>
			 * All previously attached <code>Observer</code>s for this notification list are notified and
			 * are passed a reference to the <code>Notification</code> in the order in which they were registered.
			 *
			 * @param {Notification} notification
			 * 		The notification object to pass when notifying <code>Observer</code>s.
			 */
			notifyObservers: function( notification )
			{
				this.observable.notifyObservers( notification );
			},

			/**
			 * Remove any <code>Observer</code> registered for the given <code>Notification</code> name targeting the
			 * same <code>Observer</code> context, same <code>Observer</code> notify method.
			 *
			 * @param {String} name
			 * 		Notification name which will not be observed anymore with this <code>Observer</code>.
			 *
			 * @param {Observer} observer
			 * 		The <code>Observer</code> which will not observe this notification anymore.
			 */
			removeObserver: function( name, observer )
			{
				this.observable.removeObserver( name, observer );
			},

			/**
			 * Remove all <code>Observer</code>s registered with this <code>Observable</code> instance.
			 *
			 * <P>
			 * Mainly used to mark current instance as candidate for Garbage Collection.
			 */
			removeAllObservers: function()
			{
				this.observable.removeAllObservers();
			},

			/**
			 * Search for any previously registered <code>Observer</code> with the same notification.
			 *
			 * @param {String} name
			 * 		The notification to search for registered <code>Observer</code>.
			 *
			 * @param {Observer} observer
			 * 		The <code>Observer</code> to search for.
			 */
			hasObserver: function( name, observer )
			{
				return this.observable.hasObserver( name, observer );
			},

			/**
			 * Register an <code>Mediator</code> instance with the <code>View</code>.
			 *
			 * <P>
			 * Registers the <code>Mediator</code> so that it can be retrieved by name, and further interrogates the
			 * <code>Mediator</code> for its <code>Notification</code> interests.
			 *
			 * <P>
			 * If the <code>Mediator</code> returns any <code>Notification</code> names to be notified about, an
			 * <code>Observer</code> is created to encapsulate the <code>Mediator</code> instance's
			 * <code>handleNotification</code> method and register it as an <code>Observer</code> for all
			 * <code>Notification</code>s the <code>Mediator</code> is interested in.
			 *
			 * @param {Mediator} mediator
			 * 		A reference to the <code>Mediator</code> instance.
			 */
			registerMediator: function( mediator )
			{
				var mediatorName/*String*/ = mediator.getMediatorName();

				// Do not allow re-registration (you must removeMediator first)
				if( this.hasMediator( mediatorName ) )
					return;

				// Register the Mediator for retrieval by name
				this.mediatorMap[mediatorName] = mediator;

				// Register Mediator as an observer for each of its notification interests
				var interests/*Array*/ = mediator.listNotificationInterests();
				var len/*Number*/ = interests.length;
				for( var i/*Number*/=0; i<len; i++ )
					this.registerObserver( interests[i], mediator );

				mediator.setModel(this.model);
				mediator.setView(this);
				mediator.onRegister();
			},

			/**
			 * Retrieve a <code>Mediator</code> from the <code>View</code>.
			 *
			 * @param {String} mediatorName
			 *		The name of the <code>Mediator</code> instance to retrieve.
			 *
			 * @return {Mediator}
			 *		The <code>Mediator</code> instance previously registered with the given <i>mediatorName</i> or an
			 *		explicit <code>null</code> if it doesn't exists.
			 */
			retrieveMediator: function( mediatorName )
			{
				//Return a strict null when the mediator doesn't exist
				if( !this.hasMediator(mediatorName) )
					return null;

				return this.mediatorMap[mediatorName];
			},

			/**
			 * Check if a <code>Mediator</code> is registered or not.
			 *
			 * @param {String} mediatorName
			 *		Name of the <code>Mediator</code> instance to verify the existence of its registration.
			 *
			 * @return {Boolean}
			 *		A <code>Mediator</code> is registered with the given <i>mediatorName</i>.
			 */
			hasMediator: function( mediatorName )
			{
				return this.mediatorMap[mediatorName] instanceof Mediator;
			},

			/**
			 * Remove a <code>Mediator</code> from the <code>View</code>.
			 *
			 * @param {String} mediatorName
			 *		Name of the <code>Mediator</code> instance to be removed.
			 *
			 * @return {Mediator}
			 *		The <code>Mediator</code> that was removed from the <code>View</code> or a strict <code>null</null>
			 *		if the <code>Mediator</code> didn't exist.
			 */
			removeMediator: function( mediatorName )
			{
				//Return a strict null when the mediator doesn't exist
				if( !this.hasMediator(mediatorName) )
					return null;

				var mediator/*Mediator*/ = this.mediatorMap[mediatorName];
				var interests/*Array*/ = mediator.listNotificationInterests();
				var i/*Number*/ = interests.length;
				while( i-- )
					this.removeObserver( interests[i], mediator );

				delete this.mediatorMap[mediatorName];
				mediator.onRemove();
				//TODO Call instead kind of finalize method ?
				mediator.setModel(null);
				mediator.setView(null);
				return mediator;
			},

			/**
			 * Remove each registered <code>Mediator</code>.
			 *
			 * <P>
			 * Rem: Call this to mark the Mediator as available for garbage collection, as it will remove itself
			 * for being referenced in each <code>Mediator</code> previously registered with it.
			 */
			removeAllProxies: function()
			{
				//Make a copy of the proxyMap to be sure it will not be modified during the loop.
				var arr/*Array*/ = [];
				for( var mediatorName/*String*/ in this.mediatorMap )
					arr.push(mediatorName);

				var i/*Number*/ = arr.length;
				while( i-- )
					this.removeMediator(arr[i]);
			}
		}
	);
}