/**
* Use jQuery to convert a CMS menu with multiple levels into a drop down menu
*
* Example structure
* <ul class="menu" id="menu">
*	<li id="menu_1_1" class="level0"><a href="link1">1 Menu</a></li>
*	<li id="menu_1_2" class="level1"><a href="link2">1A Sub Menu</a></li>
*	<li id="menu_1_3" class="level1"><a href="link3">1B Sub Menu</a></li>
*	<li id="menu_1_4" class="level1"><a href="link4">1C Sub Menu</a></li>
*	<li id="menu_1_5" class="level2"><a href="link5">1C-2 Sub Sub Menu</a></li>
*	<li id="menu_1_6" class="level0"><a href="link6">2 Menu</a></li>
*	<li id="menu_1_7" class="level1"><a href="link7">2A Sub Menu</a></li>
* </ul>
*
* Usage: $("#menu").createDropdown({});
*
* [CSS]
* Adds special classes to structure:
* first = First element in children
* last = Last element in children
* expands = Element has children
* focus = Menu item is expanded and selected
*
* @author One Big Broadcast, Bryan Wiebe
* @copyright December 13, 2011
* @version 1.0.2
*
* Minify instructions:
* @minify-level SIMPLE_OPTIMIZATIONS
* @minify-console YES
* @minify-filename /cms/js/plugins/menu
*/
/*
* Change log:
* December 13, 2011 - 1.0.2 - 	Added focus class to all parent elements so that we can still have a focus image selected when mouse moves down
*								children.
*
*/
(function($) {

	$.fn.createDropdown = function(options) {
	
		var defaults = {
			childWidth: null, // width and drop down children (do not include px) (null = parent width)
			effects: "none", // fade, none
			animateTime: 250 // ms
		};
		var options = $.extend(defaults, options);
		
		return this.each(function() {
		
			var menu = $(this);
			var level = 0;
			var objParent = null, pastObj = null;
			
			var tree = [];
			var parents = [];
			
			/**
			* Get the level set in the li
			* @return Level
			*/
			var getLevel = function(li) {
			
				var className = li.attr("class");
				var p = className.indexOf("level");
				var level = -1;
				
				if (p > -1) {
					p += 5;
					var s = "", levelString = "";
					while (className.length > p && ((s = className.substring(p, p + 1)) != " ")) {
						levelString += s;
						p++;
					}
					level = parseInt(levelString);
				} else {
					console.error("[menu] Menu <li> tag doesn't have a level class set.");
				}
				
				return level;
			
			}
			
			/**
			* Get the tree object by <li>
			* @return Tree Object
			*/
			var getTreeObject = function(li) {
				// set current focus to tree object for <li>
				for (var i in tree) {
					if ($(tree[i].li)[0] == $(li)[0]) {
						return tree[i];
						break;
					}
				}
				
			};
			
			/**
			* Check if a tree object is a child of another tree object
			* @param treeObject Child
			* @param parentObject Parent
			* @return Boolean True if parent
			*/
			var checkIfChildOf = function(treeObject, parentObject) {
				console.log("[menu] Checking if 0 is child of 1.", treeObject.li, parentObject.li);
				if ($(treeObject.li)[0] == $(parentObject.li)[0]) {
					console.log("[menu]	Match found");
					return true;
				} else {
					if (treeObject.parent != null) {
						console.log("[menu]	Match not found. Recursively going up tree");
						return checkIfChildOf(treeObject.parent, parentObject);
					} else {
						console.log("[menu]	Match not found and no more parents to search.");
						return false;
					}
				}
			};
			
			/**
			* Get the head parent of any child.
			* @param child Tree Object Child
			* @return Tree Object The head (root) parent of child
			*/
			var getHeadParent = function(child) {
				if (child.parent != null) {
					return getHeadParent(child.parent);
				} else {
					return child;
				}
			};
			
			
			
			// iterate through each li in menu to create tree
			var id = 0;
			$("li", menu).each(function(index) {
			
				var li = $(this);
				level = getLevel(li);
				
				if (level == 0) {
				
					// root item // parent is 
					console.info("[menu] new root ", id);
					tree.push({li: li, parent: null, level: 0, visible: true, id: id++});
					
				} else {
				
					// hide item
					li.css({
						"display": "none",
						"position": "absolute"
					});
					
					// get parent (next up with level of (level-1))
					for (i=index-1;i>=0;i--) {
						var pLi = $($("li", menu).get(i));
						var pLevel = getLevel(pLi);
						
						if (pLevel == level - 1) {
							console.info("[menu] li(" + index + ") found parent at " + i);
							// found parent
							
							li.addClass("child");
							tree.push({li: li, parent: getTreeObject(pLi), level: level, visible: false, id: id++});
							
							break;
						} else {
							//console.info("parent not found i=" + i, ", pLevel=" + pLevel + ", looking for=" + (level - 1));
						}
					}
					
					
				}
			
			});
			
			// set special classes (first and last child)
			level = -1;
			var found = false;
			
			/**
			* Check if a Tree Object has any children
			* @param parentObject Tree Object to check
			* @return True if child or children found
			*/
			var hasChildren = function(parentObject) {
			
				for (var i in tree) {
					var child = tree[i];
					if (child.parent != null && child.parent.id == parentObject.id) {
						return true;
					}
				}
				
				return false;
			
			};
			
			/**
			* Recurse through all tree objects and add "first" and "last" and "expands" classes
			* first = First child
			* last = Last child
			* expands = Has Children and is level > 0
			* @param parentObject Tree Object to check
			*/
			var setSpecialClasses = function(parentObject) {
			
				var lastChild = null;
				var childCount = 0;
				
				for (var i in tree) {
					var child = tree[i];
					if (child.parent != null && child.parent.id == parentObject.id) {
						if (childCount == 0) {
							//console.info("[menu] setting first child", child);
							child.li.addClass("first");
						}
						
						childCount++;
						lastChild = child;
						
						// recurse into selected child's children
						setSpecialClasses(child);
					}
					
				}
				if (lastChild != null) {
					lastChild.li.addClass("last");
				}
				
				if (parentObject.level > 0 && hasChildren(parentObject)) {
					parentObject.li.addClass("expands");
				}
			
			};
			
			for (var i in tree) {
				// get all level 0 children
				if (tree[i].level == 0) {
					setSpecialClasses(tree[i]);
				}
			}
			

			// add mouse events to each <li> element
			var currentFocus = null; // tree object for the currently selected <li>
			
			for (var i in tree) {
			
				var li = tree[i].li;
				
				// drop down - Show and position all children
				li.mouseenter(function(event) {
				
					var li = $(this);
					var topOffset = 0, leftOffset = 0;
					
					console.info("[menu] Menu <li> Item Mouse Enter", li);
					
					// set current focus to tree object for <li>
					currentFocus = getTreeObject(li);
					
					// set focus class to this and all parents
					li.addClass("focus");
					
					// find all children from this <li>
					var i = 0;
					for (var childIndex in tree) {
						var child = tree[childIndex];
						//console.info("[menu] ", child);
						if (child.parent != null && $(child.parent.li)[0] == $(li)[0]) {
							console.info("[menu] 	Child found at " + childIndex, child.li);
							
							if (i == 0) {
								// first child. define offsets
								if (child.level > 1) {
									// go to the right
									leftOffset += child.parent.li.width();
								} else {
									// below top menu
									topOffset += child.parent.li.height();
								}
							}
							
							// move child to parent (assumes position relative)
							var cssObj = {
								"display": "block",
								"position": "absolute",
								"opacity": 1,
								"width": (options.childWidth != null?options.childWidth:child.parent.li.width()) + "px",
								"left": (child.parent.li.position().left + leftOffset) + "px",
								"top": (child.parent.li.position().top + topOffset) + "px",
								"z-index": 1000 + (child.level * tree.length) + childIndex
							};
							
							child.li.css(cssObj);
							
							// effect
							if (options.effects == "fade" && !child.visible) {
								child.li.hide().stop().fadeIn(options.animateTime);
							}
							
							topOffset += child.li.height();
							child.visible = true;
							i++;
							
						}
					}
				
				});
				
				// drop up - Hide all children
				li.mouseleave(function(event) {
				
					var li = $(this);
					console.info("[menu] Menu <li> Item Mouse Leave", li, event);
					
					// clear current focus
					if (currentFocus != null && $(currentFocus.li)[0] == $(li)[0]) {
						currentFocus = null;
					}
					
					/**
					* Hide all open children
					* @param li <li> Selector representing the active element
					*/
					var hideAll = function(li) {
					
						console.warn("[menu] 	hideAll() Removing children of " + (li!=null?li.html():"ALL"));
						for (var childIndex in tree) {
							var child = tree[childIndex];
							
							if (li == null) {
								child.li.removeClass("focus");
							}
							
							if (child.parent != null && (li == null || $(child.parent.li)[0] == $(li)[0])) {
							//if (child.parent != null && checkIfChildOf(child, getTreeObject(li))) {
								
								// move child to parent (assumes position relative)
								
								// remove focus class
								child.li.removeClass("focus");
								
								// effect
								if (options.effects == "fade" && child.visible) {
									var fadeLi = child.li;
									fadeLi.stop().fadeOut(options.animateTime, function() {
										console.info("[menu]	hideAll() Fade complete. Hiding child " + fadeLi.html());
										fadeLi.hide();
									});
								} else {
								
									child.li.hide();
								
								}
								
								child.visible = false;
								
							}
						}
					};
					
					// wait 100ms and check if we are still hovering over a child of currentFocus
					var timeout = 50;
					setTimeout(function(){
					
						console.info("[menu] Waited " + timeout + "ms. Now checking if currentFocus is a child.", currentFocus);
					
						if (currentFocus != null && $(currentFocus.li)[0] != $(li)[0]) {
						
							var isChild = checkIfChildOf(currentFocus, getTreeObject(li));
							
							// hide all other root <li>
							if (currentFocus.level == 0) {
								for (var i in tree) {
									if (tree[i].level == 0 && tree[i].id != currentFocus.id) {
										tree[i].li.removeClass("focus");
										hideAll(tree[i].li);
									}
								}
							}
							
							var focusLi = li;
							if (!isChild) {
								// not a child
								focusLi.removeClass("focus");
								hideAll(focusLi);
							}
						
						} else {
							// current focus isn't set
							var headParent = getHeadParent(getTreeObject(li));
							console.warn("[menu] No currentFocus set. hiding all", headParent);
							hideAll();
						}					
					
					}, timeout);
					
				});
			
			}
		
		});
			
	}

})(jQuery);
