/*
 * 
 * ADOBE CONFIDENTIAL
 * ___________________
 * 
 * Copyright 2011 Adobe Systems Incorporated
 * All Rights Reserved.
 * 
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 */

(function($) {
	$.fn.museMenu = function(options) {
		return this.each(function() {
			// State
			var $currentMenuItemContainer = $();
			
			// Detect mouse leaving the entire menu...
			// When mouse has left any menu item and doesn't reenter any menu item for more than 300ms,
			// close all the submenus and set the current menu item container to empty.
			// If the mouse reenters any menu item during the waiting period, cancel the delayed action.
			var isOverMenuItem = false;
			var $menuItemContainers = $(this).find('.MenuItemContainer');
			var $menuItems = $(this).find('.MenuItem');
			$menuItems.bind('mouseenter', function() { isOverMenuItem = true; });
			$menuItems.bind('mouseleave', function() {
				isOverMenuItem = false;
				setTimeout(function() {
					if (isOverMenuItem === false) {
						$menuItemContainers.each(function() { $(this).data('hideSubmenu')(); });
						$currentMenuItemContainer = $();
					}
				}, 300); 
			});
			
			// Prepare menuItems before DOM restructuring
			$menuItems.each(function() {
				// Store references to elements
				var $menuItem = $(this);
				var $submenu = $menuItem.siblings('.SubMenu');
				var $menuItemContainer = $menuItem.closest('.MenuItemContainer');
				var isTopLevel = $menuItemContainer.parentsUntil('.MenuBar').filter('.MenuItemContainer').length === 0;
				
				// DOM restructuring to separate top level submenus
				if (isTopLevel && $submenu.length > 0) {
					// Prepare offsetContainer and relative layout distance. MenuBar class is required for SubMenu css to take effect.
					var $offsetContainer = $("<div style='position:absolute' class='MenuBar popup_element'></div>").hide().appendTo('body');
					$submenu.show();
					var originalSubmenuOffset = $submenu.offset();
					var originalMenuItemOffset = $menuItem.offset();
					var dx = originalSubmenuOffset.left - originalMenuItemOffset.left;
					var dy = originalSubmenuOffset.top - originalMenuItemOffset.top;
					$submenu.hide();
				}
				
				// Interface implementation for every menuItemContainer
				$menuItemContainer
					.data('$parentMenuItemContainer', $menuItemContainer.parent().closest('.MenuItemContainer'))
					.data('showSubmenuOnly', function() {
						// To avoid mistaken root menu items, reparent at mouse event rather than at initialization
						if (isTopLevel && $submenu.length > 0) {
							var offset = $menuItemContainer.offset();
							$offsetContainer
								.appendTo('body')
								.css({ left:offset.left, top:offset.top })
								.append($submenu)
								.show();
						}
						$submenu.show();
						$submenu.find('.SubMenu').hide(); // hide nested-submenus
					}) 
					.data('hideSubmenu', function() {
						$submenu.hide();
					})
					.data('isDescendentOf', function($ancestors) { // Ancestor test
						var $parentMenuItemContainer = $menuItemContainer.data('$parentMenuItemContainer');
						while ($parentMenuItemContainer.length > 0) {
							if ($ancestors.index($parentMenuItemContainer) >= 0) {
								return true; 
							}
							$parentMenuItemContainer = $parentMenuItemContainer.data('$parentMenuItemContainer');
						}
						return false;
					});
				
				// Evaluate cursor movement from one menuItemContainer (a) to another menuItemContainer (b)
				var fromNone = function($a, $b) { return $a.length == 0; };
				var fromParent = function($a, $b) { return $b.data('$parentMenuItemContainer').index($a) >= 0; };
				var fromSibling = function($a, $b) { return $b.siblings().index($a) >= 0; };
				var fromDescendent = function($a, $b) { return $a.data('isDescendentOf')($b); };
				var fromSiblingDescendent = function($a, $b) { return $a.data('isDescendentOf')($b.siblings('.MenuItemContainer')); };
				var sameItems = function($a, $b) { return $a.get(0) == $b.get(0); };
				
				// Select a menu item. Hide or show submenus depending what cursor movement occurred
				var delayedSelectMenuItem = function() {
					var a = $currentMenuItemContainer;
					var b = $menuItemContainer;
					
					if (fromNone(a, b)) {
						b.data('showSubmenuOnly')();
						
					} else if (fromParent(a, b)) {
						b.data('showSubmenuOnly')();
						
					} else if (fromSibling(a, b)) {
						a.data('hideSubmenu')();
						b.data('showSubmenuOnly')();
						
					} else if (fromDescendent(a, b)) {
						b.data('showSubmenuOnly')();
						
					} else if (fromSiblingDescendent(a, b)) {
						b.siblings('.MenuItemContainer').each(function() {
							$(this).data('hideSubmenu')();
						});
						b.data('showSubmenuOnly')();
						
					} else if (sameItems(a, b)) {
						// same nodes, do nothing
					} else {
						// error case, could assert here
					}
					$currentMenuItemContainer = $menuItemContainer;
				};
				
				// If mouse enters the menu item and stays there for more than 200ms, select the menu item.
				// If the mouse leaves the menu item, cancel the delayed action.
				var delayedSelectMenuItemId = null;
				$menuItem.bind('mouseenter', function() {
					delayedSelectMenuItemId = setTimeout(function() { delayedSelectMenuItem(); }, 200);
					$menuItem.one('mouseleave', function() { clearTimeout(delayedSelectMenuItemId); });
				});
			});
		});
	};
}(jQuery));
