/** 
 * @fileOverview Accordion
 * @author Oliver Bishop / Tom McCourt
 * @version 0.0.3
 * @changeLog Added auto loading of YUI modules.
 */

/* This sets the global namespaces */
var UKISA = UKISA || {};
UKISA.widget = UKISA.widget || {};

/**
 * Create an accordion navigation
 *
 * @constructor
 * @param {String}	container				Root container of the navigation using CSS selectors.
 * @param {String}	containerNodes			Selector referencing container to point to the elements that will toggle.
 * @param {String}	handlerNodes			Selector that identifies the click handlers from the container.
 * @param {String}	contentNodes			Selector that identifies the content holders for the navigation.
 * @param			options					Configuration options.
 * @param			options.exclusive		If TRUE will only show one node open at a time.
 */
UKISA.widget.Accordion = function(container, containerNodes, handlerNodes, contentNodes, options) {
	var instance, init, option, scripts;

	this.log("Accordion init.");

	instance = this;

	this.context = {
		tree: [],
		container: null,
		containerNodes: null,
		handlerNodes: null,
		contentNodes: null,
		nodes: null,
		handlers: null,
		contents: null,
		currentNode: null, // The currently opened node.
		currentParentNode: null // The currently opened parent node.
	};		

	this.context.container = container;
	this.context.containerNodes = containerNodes;
	this.context.handlerNodes = handlerNodes;
	this.context.contentNodes = contentNodes;

	scripts = [];

	if (typeof YAHOO.util.Anim === "undefined") {
		this.log("Cannot find yui/build/animation/animation-min.js");
		scripts.push("web/yui/build/animation/animation-min.js");
	}
	if (typeof YAHOO.util.Selector === "undefined") {
		this.log("Cannot find yui/build/selector/selector-min.js");
		scripts.push("web/yui/build/selector/selector-min.js");
	}

	init = function() {
		// Default configuration.
		instance.config = {
			exclusive: false, // If TRUE will only show one node open at a time.
			easing: YAHOO.util.Easing.easeOut,
			duration: 0.3
		};

		// Set up the configuration
		for (option in options) {
			if (typeof instance.config[option] !== "undefined") {
				instance.config[option] = options[option];
			}
		}

		instance.init();
	};

	if (scripts.length) {
		if (typeof YAHOO.util.Get === "undefined") {
			this.log("Cannot find references to YAHOO Anim and/or Selector or Get.");
		} else {
			YAHOO.util.Get.script(scripts, { 
				onSuccess: init
			});
		}
	} else {
		this.log("All scripts have been found on the page.");
		init();
	}
};

UKISA.widget.Accordion.prototype = {
	/**
	 * Accordion initialisation.
	 */
	init: function() {
		var $, toggle, instance, i, ix, node, handler, content, tree, anim;

		$ = YAHOO.util.Selector.query;
		instance = this;

		this.log("Node: " + this.context.container + " " + this.context.containerNodes);
		this.log("Node handler: " + this.context.container + " " + this.context.handlerNodes);
		this.log("Node content: " + this.context.container + " " + this.context.contentNodes);

		this.context.nodes = $(this.context.container + " " + this.context.containerNodes);  

		if (!this.context.nodes.length) {
			this.log("There are no containerNodes.");
			return;
		}

		this.log("Node count: " + this.context.nodes.length);

		// This is the main toggle function to show/hide the nodes.
		toggle = function(e, data) {
			var current, toggleNode, toggleParentNode;

			current = instance.context.currentNode;
	
			toggleParentNode = function() {
				var content, anim, region, height;

				if (data.content.length) {
					content = data.content[0];
					height = 0;

					// Set up the general Anim settings.
					anim = new YAHOO.util.Anim(content); 
					anim.duration = instance.config.duration; 
					anim.method = instance.config.easing;

					// If the node content is hidden, show it.
					if (content.style.display == "none") {
						// Quickly show and calculate the height.
						content.style.display = "block";
						content.style.height = "auto";
						region = YAHOO.util.Dom.getRegion(content);
						height = region.bottom - region.top;
						instance.log("Node content height: " + height);

						// Now hide it again and animate it to a the calculated height.
						content.style.overflow = "hidden";
						content.style.height = "0";
						anim.attributes.height = {to: height}; 
						anim.onComplete.subscribe(function() {
							content.style.height = "auto";
							YAHOO.util.Dom.addClass(data.node, "active"); 
							instance.context.currentParentNode = data;
						}); 
					} else {
						// Animate the content to a zero height to hide it.
						anim.onComplete.subscribe(function() {
							content.style.display = "none";
							YAHOO.util.Dom.removeClass(data.node, "active"); 
							instance.context.currentParentNode = null;
						}); 
					}
					anim.attributes.height = {to: height}; 
					anim.animate();
				}
			};

			// Show the node.
			toggleNode = function() {
				var content, anim, region, height;

				if (data.content.length) {
					content = data.content[0];
					height = 0;

					// Set up the general Anim settings.
					anim = new YAHOO.util.Anim(content); 
					anim.duration = instance.config.duration; 
					anim.method = instance.config.easing;

					// If the node content is hidden, show it.
					if (content.style.display == "none") {
						// Quickly show and calculate the height.
						content.style.display = "block";
						content.style.height = "auto";
						region = YAHOO.util.Dom.getRegion(content);
						height = region.bottom - region.top;
						instance.log("Node content height: " + height);

						// Now hide it again and animate it to a the calculated height.
						content.style.overflow = "hidden";
						content.style.height = "0";
						anim.attributes.height = {to: height}; 
						anim.onComplete.subscribe(function() {
							content.style.height = "auto";
							YAHOO.util.Dom.addClass(data.node, "active"); 
							instance.context.currentNode = data;
						}); 
					} else {
						// Animate the content to a zero height to hide it.
						anim.onComplete.subscribe(function() {
							content.style.display = "none";
							YAHOO.util.Dom.removeClass(data.node, "active"); 
							instance.context.currentNode = null;
						}); 
					}
					anim.attributes.height = {to: height}; 
					anim.animate();
				}
			};

			if (instance.config.exclusive) {
				if (!data.parent) {
					// If you want only one to be open at a time.
					 if (!instance.context.currentNode || instance.context.currentNode.index === data.index) {
						instance.log("Toggle a NEW OR SAME node.");
						toggleNode();
					 } else {
						instance.log("Toggle a DIFFERENT node.");

						anim = new YAHOO.util.Anim(instance.context.currentNode.content[0]); 
						anim.duration = instance.config.duration; 
						anim.method = instance.config.easing;
						anim.attributes.height = {to: 0}; 
						anim.onComplete.subscribe(function() {
							YAHOO.util.Dom.removeClass(instance.context.currentNode.node, "active"); 
							instance.context.currentNode.content[0].style.display = "none";	
							toggleNode();
						});
						anim.animate();
					 }
				} else {
					// This is for a parent node.
					 if (!instance.context.currentParentNode || instance.context.currentParentNode.index === data.index) {
						instance.log("Toggle a NEW OR SAME parent node.");
						
						
						var childNodes, i, ix, childContent;

						// Close the child nodes
						childNodes = $(instance.context.containerNodes, data.node);

						// Since the parentNode has children that may be active, get them and close them.
						for (i = 0, ix = childNodes.length; i < ix; i++) {
							YAHOO.util.Dom.removeClass(childNodes[i], "active"); 

							childContent = $(instance.context.contentNodes, childNodes[i]);

							childContent[0].style.display = "none";
							instance.context.currentNode = null;	
						}

						toggleParentNode();
					 } else {
						instance.log("Toggle a DIFFERENT parent node.");

						anim = new YAHOO.util.Anim(instance.context.currentParentNode.content[0]); 
						anim.duration = instance.config.duration; 
						anim.method = instance.config.easing;
						anim.attributes.height = {to: 0}; 
						anim.onComplete.subscribe(function() {
							var childNodes, i, ix, childContent;

							// Close the child nodes
							childNodes = $(instance.context.containerNodes, instance.context.currentParentNode.node);

							// Since the parentNode has children that may be active, get them and close them.
							for (i = 0, ix = childNodes.length; i < ix; i++) {
								YAHOO.util.Dom.removeClass(childNodes[i], "active"); 

								childContent = $(instance.context.contentNodes, childNodes[i]);

								childContent[0].style.display = "none";
							}

							YAHOO.util.Dom.removeClass(instance.context.currentParentNode.node, "active"); 
							instance.context.currentParentNode.content[0].style.display = "none";
							instance.context.currentParentNode = null;	
							instance.context.currentNode = null;	
							toggleParentNode();
						});
						anim.animate();
					 }
				}
			} else {
				// When you want any number of them to be open.
				toggleNode();		
			}

			YAHOO.util.Event.stopEvent(e);

			return false;
		};

		for (i = 0, ix = this.context.nodes.length; i < ix; i++) {
			node = this.context.nodes[i];
			handler = $(this.context.handlerNodes, node); 
			content = $(this.context.contentNodes, node); 

			// Check to see if node has child nodes.
			var check = $(this.context.containerNodes, node);

			if (check.length) {
				this.log("Node has children.");
			} else {
				this.log("Node has no children.");
			}

			tree = {
				"index": i,
				"node": node,
				"handler": handler,
				"content": content,
				"parent": !!check.length,
				"children": check
			};
				
			YAHOO.util.Event.addListener(handler[0], "click", toggle, tree); 

			// Apply default settings.
			if (content.length) {
				if (YAHOO.util.Dom.hasClass(node, "active")) {
					content[0].style.display = "block";
					content[0].style.overflow = "hidden";
					content[0].style.height = "auto";
					if (this.config.exclusive) {
						if (check.length) {
							this.context.currentParentNode = tree;
						} else {
							this.context.currentNode = tree;
						}
					}
				} else {
					content[0].style.display = "none";
				}
			}
		}
	},
	log: function(s) {
		if (window.console) {
			console.log(s);
		}
	}
};