/** 
 * @fileoverview Enable (pseudo-)namespaces for Javascript and define the essential functions and root objects for the Javascript Phoenix OOP (JSPOOP) Library.
 * @author James Palmer james@phoenixlondon.co.uk
 * @version 0.1 
 */

/**
 * Registers a fully-qualified namespace identifier in the form "x.y.z" as a hierarchal series of Javascript objects representing the various parts of the identifier.  If part of the hierarchy already exists, these objects are used instead of creating more.
 *  
 * @param {String} fullyqualifiednsid A string of the form "x.y.z", representing the namespace objects to be created.
 * @returns {Boolean} true 
 */
function registerNamespace(fullyqualifiednsid)
{
	// Split the passed string into its individual components
	var nsParts = fullyqualifiednsid.split(".");
	var root = window;

	// For each level of the namespace hierarchy
	for(var i=0; i<nsParts.length; i++)
	{
		// If the next level doesn't exist then create it
		if(typeof(root[nsParts[i]]) == "undefined")
			root[nsParts[i]] = new Object();
			
		// And step down to the next level of the hierarchy
		root = root[nsParts[i]];
	}
	
	return(true);
}


/**
 * Traverses the namespace hierarchy and reports on whether the supplied namespace has already been registered.  Essentially similar to {@link #registerNamespace}, but doesn't create missing identifiers - instead it returns false when it hits the first part of the fully qualified identifier that hasn't already been defined.
 *  
 * @see #registerNamespace 
 * @param {String} fullyqualifiednsid A string of the form "x.y.z", representing the namespace to check.
 * @returns {Boolean} true if identifier exists, false if not. 
 */
function namespaceDefined(fullyqualifiednsid)
{
	var nsParts = fullyqualifiednsid.split(".");
	var root = window;

	for(var i=0; i<nsParts.length; i++)
	{
		if(typeof(root[nsParts[i]]) == "undefined")
			return(false);
		root = root[nsParts[i]];
	}
	
	return(true);
}


/**
 * Traverses the namespace hierarchy and reports on whether the supplied namespace has already been registered.  Essentially similar to {@link #registerNamespace}, but doesn't create missing identifiers - instead it throws a {@link Phoenix.Exception.MissingLibraryException} when it hits the first part of the fully qualified identifier that hasn't already been defined.
 * 
 * @see #registerNamespace 
 * @param {String} fullyqualifiednsid A string of the form "x.y.z", representing the namespace to check.
 * @param {String} sourceoferror An (optional) string representing the source of the exception, if one needs to be thrown; generally the fully-qualified name of whatever component called the function.
 * @returns {Boolean} True if identifier exists
 * @throws {@link Phoenix.Exception.MissingLibraryException} if the object represented by fullyqualifiednsid does not exist
 */
function requireNamespace(fullyqualifiednsid, sourceoferror)
{
	if(!namespaceDefined(fullyqualifiednsid))
		throw new Phoenix.Exception.MissingLibraryException(fullyqualifiednsid, sourceoferror);

	return(true);
}


/**
 * Accepts a namespace identifier as a string and (if it exists) returns a reference to the object which it represents.  Essentially similar to {@link #namespaceDefined}, but on instead of returning false/true it returns null/a reference to the namespace object.
 *  
 * @param {String} fullyqualifiednsid A string of the form "x.y.z", representing the namespace object to retrieve.
 * @returns {object} Reference to the identifier if it exists, otherwise null.
 */
function namespaceIdToObjectRef(fullyqualifiednsid)
{
	var nsParts = fullyqualifiednsid.split(".");
	var root = window;

	for(var i=0; i<nsParts.length; i++)
	{
		if(typeof(root[nsParts[i]]) == "undefined")
			return(null);
		root = root[nsParts[i]];
	}
	
	return(root);
}




// Register top-level namespace and make Exceptions element available so the rest of the Phoenix.* library can complain about missing dependencies
 
 
 
//registerNamespace("Phoenix");

/**
 * Construct the Phoenix object.
 * @class The basic Phoenix class which groups all the JSPOOP objects.
 * @constructor
 */
function Phoenix()
{
}

/**
 * Construct the Phoenix.Exception object.
 * @class Base class for Phoenix-library-specific exceptions.
 * @constructor
 * @requires Phoenix
 * @param message The exception-specific message details to display
 * @param source The name of the Phoenix class throwing the exception, for reporting purposes
 */

Phoenix.Exception = function (message, source)
{
	if(typeof(source) == "object" && source.nsIdentifier)
		this.source = source.nsIdentifier;
	else
		this.source = source || "";
		
	this.name = "Phoenix.Exception";
	this.message = message || "Generic Phoenix Exception";
	
	/**
	 * Return the exception error text.
	 * @return String	 
	 */
	this.toString = function ()
	{
		return("" + this.name + " (" + this.source + "): " + this.message);
	};
};


/**
 * Construct the Phoenix.Exception.MissingLibraryException object.
 * @class Thrown when a Phoenix library has not been included before a call to a function which requires it.
 * @constructor
 * @requires Phoenix.Exception
 * @extends Phoenix.Exception
 */

Phoenix.Exception.MissingLibraryException = function(message, source)
{
	this.base = Phoenix.Exception;
	this.base(message, source);
	this.name = "Phoenix.Exception.MissingLibraryException";

	/**
	 * Return the exception error text.
	 * @return String	 
	 */
	this.toString = function ()
	{
		return("" + this.name + " (" + this.source + "): This method requires access to the library " + this.message + ".\n\nPlease include the relevant library before calling this function.");
	};
};

/**
 * Construct the Phoenix.Exception.ReallyBadIdeaException object.
 * @class Thrown when a function is called in a way which, while not technically illegal, is a Really Bad Idea for some particular reason.  In such a situation it is vitally important that the passed message parameter explains exactly <em>what</em> about the call is a bad idea.
 * @constructor
 * @requires Phoenix.Exception
 * @extends Phoenix.Exception
 */

Phoenix.Exception.ReallyBadIdeaException = function(message, source)
{
	this.base = Phoenix.Exception;
	this.base(message, source);
	this.name = "Phoenix.Exception.ReallyBadIdeaException";
	
	/**
	 * Return the exception error text.
	 * @return String	 
	 */
	this.toString = function ()
	{
		return("" + this.name + " (" + this.source + "): This is a really bad idea.  " + this.message);
	};
};
