/**
 * View()
 *
 * @author   Sean McCann <sean@mudbuginfo.com>
 * @version  0.2.5
 * @date     2006-02-09
 *
 * Changelog:
 * - 2005-05-19:  Created from older view code. (Sean McCann)
 * - 2005-07-13:  Added a check to the display to make sure that our view hadn't been unattached from the document.  (Clovis Mello)
 * - 2005-07-15:  Updated documentation (Sean McCann)
 * - 2005-09-19:  Added the "update_display" option and support for any addtional options in the future  (Sean McCann)
 * - 2005-09-20:  Added the "enable_caching" option (Sean McCann)
 * - 2005-09-20:  Added a reference to the model last used by display(). (Sean McCann)
 * - 2005-09-23:  Moved assignment of the model reference in display() so that other methods have access to the model before view creation is finished (Sean McCann)
 * - 2005-10-14:  Bugfix: property 'view_id' should have been 'view_element_id' to match all subclasses.  New check prevents unecessary call to replaceChild() in display().  Changed name of constructor parameter. (Sean McCann)
 * - 2006-02-09:  Added error output to View constructor when exceptions are thrown (Sean McCann)
 *
 * @todo  implement interface for adding/removing event listeners to the view
 */

/**
 * Parent object for every piece of dynamically updating content that we wish to create
 *
 * @param string id    Optional.  Specifies the id of the element that will serve as the view.  Not specified by default.
 * @uses Cache  saves generated content to minimize work on the client end and preserve the state of that view.
 */
function View( id )
{
	try
	{
		this.view_element_id = ''; // default id
		this.view_element = null;
		this.cache = new Cache();

		this.model = null;

		if (typeof(id) != 'undefined')
		{
			this.view_element_id = id;
		}

		/**
		 * Any optional settings for the view itself
		 * @access private
		 */
		this.options = new Object();

		// Set default values for our object
		this.options['enable_caching'] = true;
		this.options['update_display'] = true;
	}
	catch(e)
	{
		error_message(e, "View.js", 'View::view()');
	}
}
View.prototype.display = ViewDisplay;
View.prototype.createView = ViewCreateView;
View.prototype.getElementId = ViewGetElementId;
View.prototype.getElement = ViewGetElement;
View.prototype.setElementId = ViewSetElementId;
View.prototype.getOption = ViewGetOption;
View.prototype.setOption = ViewSetOption;


function ViewGetElement() {
	return this.view_element;
}

/**
 * Displays the visual representation of some model
 * @param mixed model   an array containing objects representing id/organzation pairs
 * @param string key    key used to cache the view
 */
function ViewDisplay( model, key )
{
	try
	{
		var cached_view;
		var new_view_element;

		// Save the reference to the model being shown by the View
		this.model = model;

		// Check the cache to see if we've created this view before.
		// If we have, use our cached copy.  Otherwise, create a new
		// view based on the specified model.
		if (this.options['enable_caching'])
		{
			cached_view = this.cache.retrieveData(key);
		}
		if (cached_view)
		{
			new_view_element = cached_view;
		}
		else
		{
			new_view_element = this.createView(model);

			// Cache the view associated with the current model.
			// Note that we cache the view because if we only cached
			// the data, then we would still need to re-generate
			// the view each time the cached data is
			// loaded, and that can tax a lesser CPU.
			if (this.options['enable_caching'])
			{
				this.cache.saveData(key, new_view_element);
			}
		}

		// If the "update_display" option is turned on, then update the
		// visual display of the view on screen
		if (this.options['update_display'])
		{
			// if our view reference gets unattached from the
			// document, look for a new instance of our existing
			// view before attempting to swap it out for a new one.
			this.view_element = document.getElementById(this.view_element_id);

			// Replace our old view with our new one
			if (this.view_element != new_view_element)
			{
				this.view_element.parentNode.replaceChild(new_view_element, this.view_element);
			}
		}

		// Save a local reference to our brand new view element
		this.view_element = new_view_element;

		return true;
	}
	catch(e)
	{
		error_message(e, "View.js", 'View::display()');
		return false;
	}
}

/**
 * Creates the actual element representation used by the View object
 * This virtual function must be written for each subclass
 * @param mixed model
 */
function ViewCreateView( model )
{
	try
	{
		throw new Error('The createView() method must be defined by the class extending the abstract View class.');
	}
	catch(e)
	{
		error_message(e, "View.js", 'View::createView()');
		return false;
	}
}


/**
 * Registers the id of the element where the view will be created.  If using an id different from the default, call this method immediately after instantiating the View.
 *
 * @param string id   the id of the X/HTML element where the view will be displayed.
 * @return boolean
 */
function ViewSetElementId( id )
{
	try
	{
		this.view_element_id = id;

		// Re-initialize view based on new id
		this.view_element = document.getElementById(this.view_element_id);
		return true;
	}
	catch(e)
	{
		error_message(e, "View.js", 'View::setElementId()');
		return false;
	}
}


/**
 * Returns the id of the DOM element where the view will be created.
 *
 * @return string
 */
function ViewGetElementId( )
{
	try
	{
		return this.view_element_id;
	}
	catch(e)
	{
		error_message(e, "View.js", 'View::getElementId()');
		return false;
	}
}

/**
 * Gets the value of a View configuration option
 * Throws an exception if a non-existant option is given
 * @param string option_name   the option that we want to know about
 * @return mixed
 */
function ViewGetOption( option_name )
{
	try
	{
		if (typeof this.options[option_name] == 'undefined')
		{
			throw new Error("This View does not have the '" + option_name + "' option ");
		}

		return this.options[option_name];
	}
	catch(e)
	{
		error_message(e, "View.js", 'View::getOption');
		return false;
	}
}

/**
 * Sets the value of a View configuration option
 *
 * @param string option_name   the option that we want to change
 * @param mixed  value         the new value for our option
 */
function ViewSetOption( option_name, value )
{
	try
	{
		if (typeof this.options[option_name] == 'undefined')
		{
			throw new Error("This View does not have the '" + option_name + "' option ");
		}

		this.options[option_name] = value;
		return true;
	}
	catch(e)
	{
		error_message(e, "View.js", 'View::setOption');
		return false;
	}
}