
/**
 * The CBus object is like the main cross language bus.
 * It ties in with notification to provide the user with 
 * information in a formatted way, and allows for the
 * developer to push messages through this interface.
 */
CBus = (function(){
	//The actual object
	var that = {};
	
	//Error reporting
	that.error = function(msg)
	{
		if(window.console && console.warn)
			console.warn(msg);
	}
	
	//On to definitions
	//The linked list nodes
	var linkedListHolder = function(func, scope, priority)
	{
		if(typeof(func) != 'function')
		{
			throw {
				'name' 		: 'IllegalArgumentException',
				'message'	: 'Canopy.Bus.linkedListHolder passed not a function for function.'
			};
		}
		this.func = func;
		this.scope = scope;
		this.priority = (priority ? priority : 0);
		this.Child = null;
		this.Parent = null;
	};
	//setting the child
	linkedListHolder.prototype.setChild = function(newChild)
	{
		if(this.Child)
		{
			newChild.Child = this.Child;
			this.Child.Parent = newChild;
		}
		this.Child = newChild; 
		this.Child.Parent = this;
	};
	//setting the parent
	linkedListHolder.prototype.setParent = function(newParent)
	{
		if(this.Parent)
		{
			newParent.Parent = this.Parent;
			this.Parent.Child = newParent;
		}
		this.Parent = newParent;
		this.Parent.Child = this;
	};

	
	/**
	 * Add a function to the bus, with a scope variable being bound to the func, 
	 * and a priority allowing things to shift about based on a numeric assignment 
	 * of importance.
	 * 
	 * @param {function} func
	 * @param {Object} scope
	 * @param {integer} priority
	 */
	that.addBusNode = function(func, scope, priority)
	{
		var Node = new linkedListHolder(func, scope, priority);
		if(!this.nodeLinkedList)
		{
			this.nodeLinkedList = Node;
		} else {
			var Next = this.nodeLinkedList;
			var Tail = null;
			while(Next !== null && Next.priority >= priority)
			{
				Tail = Next;
				Next = Next.Child;
			};
			if(Tail == null)
			{
				Node.setChild(this.nodeLinkedList);
				this.nodeLinkedList = Node;
			}
			else
			{
				Tail.setChild( Node );
			}
		}
	}
	
	
	/**
	 * Send a message.
	 * Message must have properties
	 * 	- type
	 *  - body
	 * @param {Object} Message
	 */
	that.send = function(type, body, attachments)
	{
		if(	!type
			||
			!body
		)
		{
			CBus.error('CBus.send','Did not receive at least a type and body.');	
		}
		var Message = {
			'type' : type,
			'body' : body,
			'attachments' : (attachments ? attachments : [])
		};
		Node = this.nodeLinkedList;
		var returnArray = [];
		while(Node !== null)
		{
			var Scope = (typeof(Node.scope) == 'object' ? Node.scope : Node);
			var returnValue = Node.func.apply(Scope, [Message, returnArray]);
			if(returnValue !== null)
			{
				returnArray[returnArray.length] = returnValue;
			}
			Node = Node.Child;
		};
		return(returnArray);
	};
	
	return that;
}());	



/**
 * CBus.Notification is a template and organizer for the look and 
 * feel of messages as they come through
 */
CBus.Notification = (function(){
	var publicThis = {
		'defaultTarget' : null,
		'defaultTimeout' : -1
	};
	
	var privateThis = {
		'styles' : {},
		'timeouts' : {
			'nodes' : [],
			'handles' : []
		}
	};
	
	publicThis.removeTimeout = function(node)
	{
		for(var i = 0, max = privateThis.timeouts.nodes.length; i < max; i++)
		{
			if(node === privateThis.timeouts.nodes[i])
			{
				var specificTimeout = privateThis.timeouts.handles[i];
				clearTimeout(specificTimeout);
				privateThis.timeouts.nodes.splice(i,1);
				privateThis.timeouts.handles.splice(i,1);
				return true;
			}
		}
		return false;
	};
	
	publicThis.addStyle = function(styleName, StyleObject)
	{
		styleName = (new String(styleName)).toLowerCase();
		if(!StyleObject)
		{
			Canopy.fault(
				'CBus.Notification.addStyle',
				'No StyleObject was passed.'
			);
		}
		if(!StyleObject.clear)
		{
			Canopy.fault(
				'CBus.Notification.addStyle',
				'The StyleObject passed did not have a clear method.'
			);
		}
		if(!StyleObject.getNode)
		{
			Canopy.fault(
				'CBus.Notification.addStyle',
				'The StyleObject passed did not have a getNode method.'
			);
		}
		if(!StyleObject.removeNode)
		{
			Canopy.fault(
				'CBus.Notification.addStyle',
				'The StyleObject passed did not have a removeNode method.'
			);
		}
		if(privateThis.styles[styleName])
		{
			Canopy.fault(
				'CBus.Notification.addStyle',
				'The style "'+styleName+'" was already defined.'
			);
		}
		privateThis.styles[ styleName ] = StyleObject;	
	}
	
	
	publicThis.notify = function(styleName, message, timeout)
	{
		if(!publicThis.defaultTarget || !publicThis.defaultTarget.appendChild)
		{
			privateThis.generateDefaultTarget();
			CBus.error(
				'CBus.Notification.notify - No one set CBus.Notification.defaultTarget '
				+ 'to a node, so we\'ve added one as the first element of body.'				
			);
		}
		timeout = (timeout ? timeout : publicThis.defaultTimeout);
		if(!privateThis.styles[styleName])
		{
			CBus.error(
				'CBus.Notification.notify - ' +
				'The style "'+styleName+'" was not defined.'				
			);
		}
		var node = privateThis.styles[styleName].getNode(message);
		if(publicThis.defaultTarget.firstChild)
		{
			publicThis.defaultTarget.insertBefore(node, publicThis.defaultTarget.firstChild);
		} else {
			publicThis.defaultTarget.appendChild(node);
		}		
		//Set the functon to close the notification
		if(parseInt(timeout) > 0)
		{
			var i = privateThis.timeouts.nodes.length;
			privateThis.timeouts.nodes[ i ] = node;
			privateThis.timeouts.handles[ i ] = 
				window.setTimeout( 
					function() 
					{ 
						if(node.parentNode != null)
						{
							privateThis.styles[styleName].removeNode(node); 
						}
					}, 
					timeout 
				);
		}
	}
	
	/**
	 *	A function to clear all notifications
	 */
	publicThis.clear = function(styleName)
	{
		if(styleName)
		{//remove just the name
			if(!privateThis.styles[styleName])
			{
				CBus.error(
					'CBus.Notification.clear - '+
					'The style name "'+styleName+'" was not found.'
				);
			}
			privateThis.styles[styleName].clear();
		}
		else
		{//remove all
			for(var styleName in privateThis.styles)
			{
				privateThis.styles[styleName].clear();
			}
		}
	};
	
	
	privateThis.generateDefaultTarget = function()
	{
		publicThis.defaultTarget = document.createElement('DIV');
		if(document.body.childNodes.length > 0)
		{
			document.body.insertBefore(publicThis.defaultTarget, document.body.childNodes[0]);
		} else {
			document.body.appendChild(publicThis.defaultTarget);
		}
	}
	
	
	return(publicThis);
}());



/**
 * Define the StyleTemplate
 * Easily extended for all the main styles.
 */
CBus.Notification.StyleTemplate = function()
{
	//holds the node data, we set the innerHTML then clone it.
	this['template'] = {
		'root' 		: null,
		'image' 	: null,
		'message' 	: null,
		'closeBox'	: null
	};
	
	this.nodes = [];
};

CBus.Notification.StyleTemplate.prototype._closeHandler = function(e)
{
	var target = YAHOO.util.Event.getTarget(e);
	while(target != null && (target.nodeName.toLowerCase() == 'div' || target.nodeName.toLowerCase() == 'img'))
	{
		if(target.className.indexOf('notificationRoot') != -1)
		{
			CBus.Notification.removeTimeout(target);
			target.parentNode.removeChild(target);
		}
		target = target.parentNode;
	}
}

CBus.Notification.StyleTemplate.prototype.buildTemplate = function()
{	//make elements
	this.template.root = document.createElement('div');
	this.template.image = document.createElement('img');
	this.template.message = document.createElement('div');
	this.template.closeBox = document.createElement('div');
	//put 'em together
	this.template.root.appendChild(this.template.image);
	this.template.root.appendChild(this.template.closeBox);
	this.template.root.appendChild(this.template.message);
	//assign classes
	YAHOO.util.Dom.addClass(this.template.root, 'notificationRoot');
	YAHOO.util.Dom.addClass(this.template.image, 'notificationImage');
	YAHOO.util.Dom.addClass(this.template.message, 'notificationMessage');
	YAHOO.util.Dom.addClass(this.template.closeBox, 'notificationCloseBox');
};
	
CBus.Notification.StyleTemplate.prototype.getNode = function(message)
{
	if(this.template.root == null)
	{
		this.buildTemplate();
	}
	this.template.message.innerHTML = message;
	var node = this.template.root.cloneNode(true);
	for(var i = 0, max = node.childNodes.length; i < max; i++)
	{
		if(node.childNodes[i].className.indexOf('notificationCloseBox') != -1)
		{
			YAHOO.util.Event.addListener(node.childNodes[i],'click',this._closeHandler);
			i = max;
		}
	}
	this.nodes[ this.nodes.length ] = node;
	return node;
};

CBus.Notification.StyleTemplate.prototype.removeNode = function(node, immediately)
{
	for(i in this.nodes)
	{
		if(node == this.nodes[i])
		{
			this.nodes.splice(i,1);
		}
	}
	if(immediately && immediately == true)
	{
        if(node.parentNode)
        {
		    node.parentNode.removeChild(node);
        }
	}
	else
	{
		var anim = new YAHOO.util.Anim(
			node, 
			{
				'height' : {
					'to' : 0
				}
			}, 
			1, 
			YAHOO.util.Easing.easeNone
		);
		anim.onComplete.subscribe(function() {
			if(node.parentNode)
			{
				node.parentNode.removeChild(node);
			}
		});
		anim.animate();
	}
};

CBus.Notification.StyleTemplate.prototype.clear = function()
{
	for(var i = this.nodes.length - 1; i >= 0; i--)
	{
		this.removeNode(this.nodes[i], true);
	}
};


/**
 * The Bridge does ajax calls to the cbus controller to get updates on messages.
 * 
 * @param {Object} Bus
 * @param {Object} targetUrl
 * @param {Object} settings
 */
CBus.Bridge = (function(){
	return;
	var that = this;
	if(!settings)
	{
		settings = {};
	}
	if(!settings.interval) { settings.interval = 15000; };
	if(!settings.onfailure) { 
		settings.onfailure = function() 
		{ 
			that.cancel();
			if(window.console && console.error)
            {
                console.error('Canopy.Event.Bus.Bridge failed to connect to "'+targetUrl+'".'); 
            }
		}
	}
	this.onfailure = settings.onfailure;
	var checkFunction = function()
	{
		var callback =
		{
			"success"	: function(o) {
				//load the array into ary
				var ary = eval(o.responseText);
				if(ary && ary.length)
				{
                    //send the messages
                    for(var i = 0, max = ary.length; i < max; i++)
                    {
                        if(ary[i] && ary[i].body && ary[i].subject)
                        {
                            Canopy.Event.Bus.sendMessage(ary[i]);
                        }
                    }
                }
				//schedule the next call
				that.interval = window.setTimeout(checkFunction, settings.interval);	
			},
			
			"failure"	: function() {
				return that.onfailure();
			}
		};
		var transaction = YAHOO.util.Connect.asyncRequest('GET', targetUrl, callback, null); 
	};
	/*if(Canopy.Event.Bus.Bridge.disabled == true)
	{
		return;
	}*/
	checkFunction();
	/*Canopy.Event.Bus.Bridge.prototype.cancel = function()
	{
		clearInterval(this.interval);
	}
	
	Canopy.Event.Bus.Bridge.disabled = false;*/
}());

