/**
 * Object jacFoldingMenu
 * 
 * @super Object
 * @constructor
 * @memberOf {jacFoldingMenu}
 * @param menu_wrapper_id id of the wrapper element that will contain the folding menu
 * @param [prefix] prefix prepended to all the menu ids (if you want more than one folding menu on a page
 * 
 */
function jacFoldingMenu(menu_wrapper_id) //, _menu_id_prefix)
{
	this._menu_wrapper_elem = menu_wrapper_id ? $(menu_wrapper_id) : null;
	this._menu_id_prefix = arguments.length >= 2 ? arguments[1] : '';
	this._menu_id = (this._menu_id_prefix == '' ? this._menu_id_prefix : this._menu_id_prefix + '-') + 'folding-menu'; 
	this._menu_items = new Array();
}
jacFoldingMenu.prototype = new Object();
jacFoldingMenu.prototype._menu_wrapper_elem;
jacFoldingMenu.prototype._menu_id_prefix;
jacFoldingMenu.prototype._menu_id;
jacFoldingMenu.prototype._menu_items;

jacFoldingMenu.prototype.add_child =
	function(child)
	{
//		add_child_to_array(this, this._menu_id_prefix, child, this._menu_items);
		child._menu_id_prefix = this._menu_id_prefix;
		child._menu_index = this._menu_items.length;
		child._menu_parent = this;
		this._menu_items.push(child);
	};

jacFoldingMenu.prototype.remove_child =
	function(index)
	{
		this._menu_items.splice(index, 1);
	};

jacFoldingMenu.prototype.get_id =
	function()
	{
		return this._menu_id;
	};

jacFoldingMenu.prototype.get_xhtml =
	function()
	{
		var children_xhtml = this._menu_items.collect(function(n) { return n.get_xhtml(); }).join('');
		return '<ul id="' + this._menu_id + '" class="folding-menu">' + children_xhtml + '</ul>';
	};

jacFoldingMenu.prototype.update =
	function()
	{
		this._menu_wrapper_elem.update(this.get_xhtml());
		this.start_observing();
	};
	
jacFoldingMenu.prototype.collapse_all =
	function()
	{
		this.stop_observing();
		invoke_if_exists(this._menu_items, 'collapse');
		this.update();
	};

jacFoldingMenu.prototype.start_observing =
	function()
	{
		invoke_if_exists(this._menu_items, 'start_observing');
	};

jacFoldingMenu.prototype.blah =
	function()
	{
	
	};

jacFoldingMenu.prototype.stop_observing =
	function()
	{
		invoke_if_exists(this._menu_items, 'stop_observing');
	};

jacFoldingMenu.prototype.deselect_children =
	function()
	{
		invoke_if_exists(this._menu_items, 'deselect');
	};

/**
 * jacFoldingMenuItem - base class for items contained in a jacFoldingMenu
 * 
 * @param title - title of the item
 * @return
 */
function jacFoldingMenuItem(title)
{
	this._title = title;
	this._click_bound = null;

	//please don't mess with these three
	this._menu_id_prefix = '';
	this._menu_index = 0;
	this._menu_parent = null;
}
jacFoldingMenuItem.prototype = new Object();
jacFoldingMenuItem.prototype._title;
jacFoldingMenuItem.prototype._click_bound;
jacFoldingMenuItem.prototype._menu_id_prefix;
jacFoldingMenuItem.prototype._menu_index;
jacFoldingMenuItem.prototype._menu_parent;

jacFoldingMenuItem.prototype.get_id =
	function()
	{
		return this._menu_parent != null ? (this._menu_parent.get_id() + '-' + this._menu_index) : '';
	};

jacFoldingMenuItem.prototype.get_title =
	function()
	{
		return this._title;
	};
jacFoldingMenuItem.prototype.set_title =
	function(new_title)
	{
		this._title = new_title;
	};
jacFoldingMenuItem.prototype.get_index =
	function()
	{
		return this._menu_index;
	};
		
jacFoldingMenuItem.prototype.get_xhtml =
	function()
	{
		return this._title;
	};
jacFoldingMenuItem.prototype.update =
	function()
	{
		$(this.get_id()).replace(this.get_xhtml());
	};


/**
 * jacFoldingSubmenu
 * 
 * jacFoldingSubmenu(_title, _unfolded_title[, _children][, _folded])
 * @param title - title of the submenu
 * @param unfolded_title - title of the submenu while unfolded, pass null to leave the regular title
 * @param chilrden (optional) - specifies the children, otherwise you can add_child() for each
 * @param folded (optional) - whether the menu is "folded" or collapsed, default true
 */
function jacFoldingSubmenu(title, unfolded_title) //, children)
{
	jacFoldingMenuItem.call(this, title);
	this._unfolded_title = unfolded_title == undefined ? null : unfolded_title;
	this._child_menu_items = new Array();
	this._folded = arguments.length >= 4 ? arguments[3] : true;

	//animation variables
	this._anim_ratio = 0;
	this._delta_anim_ratio = 0.1;
	this._total_height = 0;
	this._starting_height = 0;
	this._anim_executer = null;
	this._saved_contents = '';

	if(arguments.length >= 3)
	{
		arguments[2].each(
			function(item, index)
			{
				add_child_to_array(this, this._menu_id_prefix, item, this._child_menu_items);
//				item.menu_id_prefix = that._menu_id_prefix;
//				item.menu_index = that._child_menu_items.length;
//				item.menu_parent = that;
//				that._child_menu_items.push(item);
			}, this);
	}
}
jacFoldingSubmenu.prototype = new jacFoldingMenuItem;
jacFoldingSubmenu.prototype._unfolded_title;
jacFoldingSubmenu.prototype._child_menu_items;
jacFoldingSubmenu.prototype._folded;
jacFoldingSubmenu.prototype._anim_ratio;
jacFoldingSubmenu.prototype._delta_anim_ratio;
jacFoldingSubmenu.prototype._total_height;
jacFoldingSubmenu.prototype._starting_height;
jacFoldingSubmenu.prototype._anim_executer;
jacFoldingSubmenu.prototype._saved_contents;

jacFoldingSubmenu.prototype.add_child =
	function(child)
	{
		add_child_to_array(this, this._menu_id_prefix, child, this._child_menu_items);
	};

jacFoldingSubmenu.prototype.remove_child =
	function(index)
	{
		this._child_menu_items.splice(index, 1);
	};

jacFoldingSubmenu.prototype.get_xhtml =
	function()
	{
		var children_xhtml = '';
		if(!this._folded)
			children_xhtml = '<ul class="folding-submenu">'
				+ this._child_menu_items.collect(function(n) { return n.get_xhtml(); }).join('')
				+ '</ul>';

		return '<li id="' + this.get_id() + '" class="folding-submenu-title' + (this._folded ? '' : ' unfolded') + '">'
			+ ((this._unfolded_title != null && !this._folded) ? this._unfolded_title : this._title)
			+ children_xhtml + '</li>\n';
	};

jacFoldingSubmenu.prototype.update =
	function()
	{
		$(this.get_id()).replace(this.get_xhtml());
		this.start_observing();
	};

jacFoldingSubmenu.prototype.collapse =
	function()
	{
		this._folded = true;
	};
	
jacFoldingSubmenu.prototype.collapse_all =
	function()
	{
		this.stop_observing();
		invoke_if_exists(this._child_menu_items, 'collapse');
		this.update();
	};

jacFoldingSubmenu.prototype.start_observing =
	function()
	{
		if(this._click_bound == null)
			this._click_bound = this._click_obs.bindAsEventListener(this);
		$(this.get_id()).observe('click', this._click_bound);
		if(!this._folded)
			invoke_if_exists(this._child_menu_items, 'start_observing');
	};
jacFoldingSubmenu.prototype.stop_observing =
	function()
	{
		$(this.get_id()).stopObserving('click', this._click_bound);
		if(!this._folded)
			invoke_if_exists(this._child_menu_items, 'stop_observing');
	};
	
jacFoldingSubmenu.prototype.deselect_children =
	function()
	{
		invoke_if_exists(this._child_menu_items, 'deselect');
	};
jacFoldingSubmenu.prototype.deselect =
	function()
	{
		this.collapse();
		this.collapse_all();
	};

//get/setters
jacFoldingSubmenu.prototype.change_folded =
	function()
	{
		this._folded = !this._folded;
	};
jacFoldingSubmenu.prototype.set_folded =
	function(new_folded)
	{
		this._folded = new_folded;
	};
jacFoldingSubmenu.prototype.get_folded =
	function()
	{
		return this._folded;
	};

jacFoldingSubmenu.prototype._click_obs =
	function(e)
	{
		var elem = e.element();
		var par_elem = elem.up();
		if(elem.id == this.get_id() || (par_elem != null && par_elem.identify() == this.get_id()))
		{
			//make sure all others are collapsed, then collapse/expand current
			var f = this.get_folded();
			if(!f && !jacFoldingSubmenu.fold_on_click)
				return;
			this._menu_parent.collapse_all();
			this.set_folded(!f);
			this._menu_parent.update();
	
			//if we are folded now, do nothing, if we were folded and now unfoled, do the following
			if(f)
			{
				if(jacFoldingSubmenu.select_first_child && this._child_menu_items.length > 0)
					if(this._child_menu_items[0]['select'])
						this._child_menu_items[0].select();
					
				var this_elem = $(this.get_id()); 
				this._total_height = this_elem.getHeight();
				this._saved_contents = this_elem.innerHtml;
	
				//select the only child if there is only one
	//				if(child_menu_items.length == 1 && child_menu_items[0]['select'] != undefined)
	//					child_menu_items[0].select();
				this_elem.update(this._title);
	
				this._starting_height = this_elem.getHeight();
				this_elem.setStyle({height: this._starting_height});
	
				this._anim_ratio = 0.0;
				this._delta_anim_ratio = 0.1;
				this._anim_executer = new PeriodicalExecuter(this.animate_expand.bind(this), 0.025);
			}
		}
	};

jacFoldingSubmenu.prototype.animate_expand =
	function()
	{
		if(this._anim_executer == null)
			return;
		
		if(this._anim_ratio >= 0.66 && this._anim_ratio < 0.80)
			this._delta_anim_ratio = 0.08;
		if(this._anim_ratio >= 0.80 && this._anim_ratio < 1.0)
			this._delta_anim_ratio = 0.04;
	
		this._anim_ratio += this._delta_anim_ratio;
	
		if(this._anim_ratio >= 1.0)
		{
			this._anim_executer.stop();
			this._anim_executer = null;
			this.update();
			$(this.get_id()).setStyle({height: 'auto'});
		}
		else
		{
			$(this.get_id()).setStyle({height:
				Math.round(this._starting_height + (this._total_height - this._starting_height)*this._anim_ratio) + 'px'});
		}
	};

// set this static to "true" if you wish for the submenus to select the first child when unfolded
jacFoldingSubmenu.select_first_child = false;
// set this static to false if you don't wish for the menu to ever "fold" once opened
jacFoldingSubmenu.fold_on_click = true;

function jacFoldingMenuItemLink(title, link_dest_uri)
{
	jacFoldingMenuItem.call(this, title);
	this._link_dest_uri = link_dest_uri;
	this._selected = false;
}
jacFoldingMenuItemLink.prototype = new jacFoldingMenuItem;
jacFoldingMenuItemLink.prototype._link_dest_uri;
jacFoldingMenuItemLink.prototype._selected;

jacFoldingMenuItemLink.prototype.get_xhtml =
	function()
	{
		return '<li id="' + this.get_id() + '"' + (this._selected ? ' class="selected"' : '') + '>'
			+ '<a href="' + this._link_dest_uri + '">'
			+ this._title
			+ '</a></li>\n';
	};

jacFoldingMenuItemLink.prototype.select =
	function()
	{
		selected = true;
	};
jacFoldingMenuItemLink.prototype.deselect =
	function()
	{
		selected = false;
	};

/**
 * jacFoldingMenuItemJS - folding menu item that executes arbitrary javascript when clicked
 * @param title - the title of the menu item
 * @param sel_title - the title of the menu item when it is selected
 * @param click_func - function executed when clicked
 * @return
 */
function jacFoldingMenuItemJS(title, sel_title, click_func)
{
	jacFoldingMenuItem.call(this, title);
	this._sel_title = sel_title;
	this._click_func = click_func;
	this._selected = false;
}	
jacFoldingMenuItemJS.prototype = new jacFoldingMenuItem;
jacFoldingMenuItemJS.prototype._sel_title;
jacFoldingMenuItemJS.prototype._click_func;
jacFoldingMenuItemJS.prototype._selected;

jacFoldingMenuItemJS.prototype.get_xhtml =
	function()
	{
		return '<li id="' + this.get_id() + '" class="folding-menu-js' + (this._selected ? ' selected' : '') + '">'
			+ (this._selected ? this._sel_title : this._title)
			+ '</li>';
	};

jacFoldingMenuItemJS.prototype.select =
	function()
	{
		if(this._menu_parent)
		{
			if(this._menu_parent['set_folded'] != undefined)
				this._menu_parent.set_folded(false);
			this._menu_parent.deselect_children();
			this._selected = true;
			this._menu_parent.update();
			this._click_func.call(this, $(this.get_id()));
		}
	};

jacFoldingMenuItemJS.prototype.deselect =
	function()
	{
		this._selected = false;
	};

jacFoldingMenuItemJS.prototype.start_observing =
	function()
	{
		if(this._click_bound == null)
			this._click_bound = this._click_item.bindAsEventListener(this);
		$(this.get_id()).observe('click', this._click_bound);
	};

jacFoldingMenuItemJS.prototype.stop_observing =
	function()
	{
		$(this.get_id()).stopObserving('click', this._click_bound);
		this.deselect();
	};

jacFoldingMenuItemJS.prototype._click_item =
	function(e)
	{
		var my_id = this.get_id();
		var elem = e.element();
		if(my_id == elem.id || elem.descendantOf(my_id))
			this.select();
	};

function add_child_to_array(that, menu_id_prefix, child, array)
{
	child._menu_id_prefix = menu_id_prefix;
	child._menu_index = array.length;
	child._menu_parent = that;
	array.push(child);
}

function invoke_if_exists(array, func)
{
	array.each(
		function(elem)
		{
			if(elem[func] != undefined)
			{
				elem[func]();
			}
		});
}
