var autoIdCounter = 0;
var defaultEasing = 'swing';
var defaultAnimation = 'slide';
var defaultAnimateSpeed = 250;
var defaultHideMenuDelay = 500;
var defaultItemText = ' ';
var defaultMenuClass = 'TPddMenu';
var defaultMenuItemClass = 'TPddMenuItem';
var TPddMenu = [];
var debugTextCol = 'white';
var debugLineCounter= 0;

$(document).ready(function() {
	if (typeof TP_MenuArr != 'undefined') {
		for (var i in TP_MenuArr) {
			buildMenu(TP_MenuArr[i], false);
		}
	}
});

function showMenu(id) {
	if (TPddMenu[id]['currentlyVisible'] == false) {
		$attachElm = TPddMenu[id]['attachElm'];
		$menuDiv = TPddMenu[id]['menuDiv'];
		var attachElm_x = $attachElm.offset().left;
		var attachElm_y = $attachElm.offset().top;
		var attachElm_height = $attachElm.outerHeight();
		var attachElm_width = $attachElm.outerWidth();
		var menuDiv_width = $menuDiv.outerWidth();
		var windowHeight = $(window).height();
		
		var menuY = (typeof TPddMenu[id]['settings'].offsetY != 'undefined') ? TPddMenu[id]['settings'].offsetY : 0;
		var menuX = (typeof TPddMenu[id]['settings'].offsetX != 'undefined') ? TPddMenu[id]['settings'].offsetX : 0;
		
		var position = TPddMenu[id]['settings'].position;
		var direction = TPddMenu[id]['settings'].direction;
		switch (position) {
			case 'top':
				var menuDiv_height = $menuDiv.height();
				menuY += (direction=='up') ? windowHeight - attachElm_y : attachElm_y - menuDiv_height;
				menuX += attachElm_x;
				break;
			case 'left':
				menuY += (direction=='up') ? windowHeight - attachElm_y - attachElm_height : attachElm_y;
				menuX += attachElm_x - menuDiv_width;
				break;
			case 'right':
				menuY += (direction=='up') ? windowHeight - attachElm_y - attachElm_height : attachElm_y;
				menuX += attachElm_x + attachElm_width;
				break;
			default: //bottom
				menuY += (direction=='up') ? windowHeight - attachElm_y - attachElm_height : attachElm_y + attachElm_height;
				menuX += attachElm_x;
		}
		
		(direction == 'up') ? $menuDiv.css({bottom : menuY, left : menuX}) : $menuDiv.css({top : menuY, left : menuX});

		animationHandler(id, true);
		TPddMenu[id]['currentlyVisible'] = true;
	}
}

function hideMenu(id) {
	animationHandler(id, false);
	TPddMenu[id]['currentlyVisible'] = false;
	TPddMenu[id]['hideTimer'] = false;
}

function animationHandler(id, showing) {
	var easing = (typeof TPddMenu[id]['settings'].easing != 'undefined') ? TPddMenu[id]['settings'].easing : defaultEasing;
	var animateSpeed = (typeof TPddMenu[id]['settings'].animateSpeed != 'undefined') ? TPddMenu[id]['settings'].animateSpeed : defaultAnimateSpeed;
	var animation = (typeof TPddMenu[id]['settings'].animation != 'undefined') ? TPddMenu[id]['settings'].animation : defaultAnimation;
	
	switch(animation) {
		case 'appear':
			(showing) ? appear(id) : disappear(id);
			break;
		case 'fade':
			(showing) ? fadeIn(id, animateSpeed, easing) : fadeOut(id, animateSpeed, easing);
			break;
		case 'flyDown':
			(showing) ? flyVertIn(id, animateSpeed, easing, true) : flyVertOut(id, animateSpeed, easing, true);
			break;
		case 'flyUp':
			(showing) ? flyVertIn(id, animateSpeed, easing, false) : flyVertOut(id, animateSpeed, easing, false);
			break;
		default: //slide
			(showing) ? animateSlideDown(id, animateSpeed, easing) : animateSlideUp(id, animateSpeed, easing);
	}
}

function flyVertIn(id, animateSpeed, easing, flyDown) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	$menuDiv.css({display:'block'});
	var offscreenOffset = 2 * $menuDiv.height();
	var direction = TPddMenu[id]['settings'].direction;
	if (direction == 'up') {
		//http://dev.jquery.com/ticket/4635
		var orig_bottom = /([0-9]+)/.exec($menuDiv.css('bottom'))[1];
		var startingY = (flyDown) ? $(window).height() + offscreenOffset : 0 - offscreenOffset;
		$menuDiv.css({bottom:startingY});
		$menuDiv.animate({bottom:orig_bottom}, animateSpeed, easing);
	} else {
		var origY = $menuDiv.offset().top;
		var startingY = (flyDown) ? 0 - offscreenOffset : $(window).height() + offscreenOffset;
		$menuDiv.css({top:startingY});
		$menuDiv.animate({top:origY}, animateSpeed, easing);
	}
}

function flyVertOut(id, animateSpeed, easing, flyDown) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	var offscreenOffset = 2 * $menuDiv.height();
	var direction = TPddMenu[id]['settings'].direction;
	if (direction== 'up') {
		var orig_bottom = /([0-9]+)/.exec($menuDiv.css('bottom'))[1];
		var endingY = (flyDown) ? $(window).height() + offscreenOffset : 0 - offscreenOffset;
		$menuDiv.animate({bottom:endingY}, animateSpeed, easing, function(){
			$(this).css({display: 'none', bottom: orig_bottom});
		});
	} else {
		var origY = $menuDiv.offset().top;
		var endingY = (flyDown) ? 0 - offscreenOffset : $(window).height() + offscreenOffset;
		$menuDiv.animate({top:endingY}, animateSpeed, easing, function(){
			$(this).css({display: 'none', top: origY});
		});
	}
}

function fadeIn(id, animateSpeed, easing) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	$menuDiv.css({display:'block', opacity:0});
	$menuDiv.animate({opacity:1}, animateSpeed, easing);
}

function fadeOut(id, animateSpeed, easing) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	$menuDiv.animate({opacity:0}, animateSpeed, easing, function(){
		$(this).css('display', 'none');
	});
}

function animateSlideDown(id, animateSpeed, easing) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	$menuDiv.css('height', 'auto');//since height was set to zero in slideUp
	var origHeight = $menuDiv.height();
	$menuDiv.css('height', 0).animate({height: origHeight}, animateSpeed, easing);
}

function animateSlideUp(id, animateSpeed, easing) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	$menuDiv.animate({height: 0}, animateSpeed, easing, function(){
		$(this).css('display', 'none');
	});
}

function appear(id) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	$menuDiv.css({display : 'block'});	
}

function disappear(id) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	$menuDiv.css({display : 'none'});
}

function mouseOutHandler(event) {
	var id = event.data;
	var mouseX = event.pageX;
	var mouseY = event.pageY;
	//find root parent node
	while (TPddMenu[id]['parent'] != '') {
		id = TPddMenu[id]['parent'];
	}
	reactToMousePosition(id, mouseX, mouseY);
}

function reactToMousePosition(id, mouseX, mouseY) {
	//return true if menu is closed, false otherwise
	var closeMenu = false;
	var menu = TPddMenu[id];
	if (menu['currentlyVisible']) {//ignore invisible submenus
		if (menu['hideTimer'] != false) {
			/*	if necessary, timer will be started again later in this method
			*	for now, destroying hideTimer is the safest way to ensure a parent doesn't
			*	get closed on accident if its timer has already started (ex: mouseOut going towards submenu)
			*/
			deconstructTimer(id);
		}
		var hasVisibleChild = false;
		var subMenuHeldOpen = false;
		for (var i in menu['children']) {
			var subMenuId = menu['children'][i];
			if (TPddMenu[subMenuId]['currentlyVisible']) {
				hasVisibleChild = true;
				var subMenuClosed = reactToMousePosition(subMenuId, mouseX, mouseY);
				if (!subMenuClosed) subMenuHeldOpen = true;
			}
		}
		if (!hasVisibleChild) subMenusHeldOpen = false;
		
		if (!subMenuHeldOpen && !isMouseInMenu(id, mouseX, mouseY)) {
			closeMenu = true;
			hideMenuDelay = (typeof TPddMenu[id]['settings'].hideMenuDelay != 'undefined') ? TPddMenu[id]['settings'].hideMenuDelay : defaultHideMenuDelay;
			TPddMenu[id]['hideTimer'] = setTimeout('hideMenu("' + id + '")', hideMenuDelay);
		}
	}
	return closeMenu;
}

function isMouseInMenu(id, x, y) {
	$menuDiv = TPddMenu[id]['menuDiv'];
	var menu_left = $menuDiv.position().left;
	var menu_width = $menuDiv.outerWidth();
	var menu_right = menu_left + menu_width;
	var menu_top = $menuDiv.position().top;
	var menu_height = $menuDiv.outerHeight();
	var menu_bottom = menu_top + menu_height;
	var retVal = true;
	/* 	mouseOut is thrown when mouse is on pixel edge of element (hence the equals sign below)
	*	When a parent and child share a wall, technically when the mouse is on the
	*	pixel that represents both the right-wall of the parent and the left-wall of
	*	the child, it is considered outside of both elements.  This is acceptable, as the hideTimer
	*	will be deconstructed as soon as the mouse enters the child again  
	*/  
	if (x <= menu_left || x >= menu_right || y <= menu_top || y >= menu_bottom) {
		retVal = false;
	}
	return retVal;
}

function deconstructTimer(id) {
	if (TPddMenu[id]['hideTimer'] != false) {
		clearTimeout(TPddMenu[id]['hideTimer']);
		TPddMenu[id]['hideTimer'] = false;
	}
	if (TPddMenu[id]['parent'] != '') {
		deconstructTimer(TPddMenu[id]['parent']);
	}
}

function buildMenu(menuObj, isChild) {
	var id = (typeof menuObj.settings.id != 'undefined') ? menuObj.settings.id : 'tpdd' + autoIdCounter++;
	var menuClass = (typeof menuObj.settings.cssClass != 'undefined') ? menuObj.settings.cssClass : defaultMenuClass;
	
	TPddMenu[id] = [];
	var attachId = menuObj.settings.attachId;
	var $attachElm = $('#' + attachId);
	TPddMenu[id]['parent'] = '';
	TPddMenu[id]['children'] = [];
	if (isChild) {
		//find the menu holding the item with the submenu
		var attachItemParent = $attachElm.parent().get(0);
		var attachItemParentId = $(attachItemParent).attr('id');
		var length = TPddMenu[attachItemParentId]['children'].length;
		//notify parent of its child
		TPddMenu[attachItemParentId]['children'][length] = id;
		//notify child of its parent
		TPddMenu[id]['parent'] = attachItemParentId;
	}
	
	var $menuDiv = $('<div>').css({display: 'none', position: 'absolute'}).attr('id',id).addClass(menuClass).appendTo('body');
	
	for (var j in menuObj.items) {
		var itemId = (typeof menuObj.items[j].id != 'undefined') ? menuObj.items[j].id : 'tpddItem' + autoIdCounter++;
		var menuItemClass = (typeof menuObj.items[j].cssClass != 'undefined') ? menuObj.items[j].cssClass : defaultMenuItemClass;
		var itemText = (typeof menuObj.items[j].text != 'undefined') ? menuObj.items[j].text : defaultItemText;
		
		var $div = $('<div>').css({cursor: 'pointer'}).addClass(menuItemClass).attr('id',itemId).text(itemText);
		
		if (typeof menuObj.items[j].url != 'undefined') {
			$div.bind('click', menuObj.items[j].url, function (event) {
				window.location = event.data
			});
		}
		
		$menuDiv.append($div);
		
		/*
		*  if submenu is found, get the ID of the current menu
		*  and set it as the 'attachId' of the submenu object
		*  Then pass that object to this method
		*  (note: submenu defined after parent item is loaded into DOM)  	
		*/  	
		if (menuObj.items[j].submenu != null) {
			if (typeof menuObj.items[j].submenu.settings == 'undefined') {
				//it is possible for subMenu not to define settings
				menuObj.items[j].submenu.settings = {'attachId' : 'dummy'};
			}
			menuObj.items[j].submenu.settings.attachId = itemId;
			buildMenu(menuObj.items[j].submenu, true);
		}
	}
	
	TPddMenu[id]['menuDiv'] = $menuDiv;
	TPddMenu[id]['attachElm'] = $attachElm;
	TPddMenu[id]['settings'] = menuObj.settings;
	TPddMenu[id]['currentlyVisible'] = false;
	TPddMenu[id]['hideTimer'] = false;
	
	//$attachElm.bind('click', id, function (event) {//possibly user defined later...
	$attachElm.bind('mouseover', id, function (event) {
		deconstructTimer(event.data);
		showMenu(event.data);
	});
	$attachElm.bind('mouseout', id, function (event) {
		mouseOutHandler(event);
	});
	$menuDiv.bind('mouseout', id, function (event) {
		mouseOutHandler(event);
	});
	$menuDiv.bind('mouseover', id, function (event) {
		deconstructTimer(event.data);
	});
}