/*
 * middleNavigation.js
 * Functionality for the middle menu
 *
 * Part of dan pearlman theme
 *
 * @author Jack Weinert (lieblinx/CodeMonkz)
 */

// Middle navigation html tag structure
//
// +- menu --------------------------------------------------------------------+
// | +- menu_c --------------------------------------------------------------+ |
// | | caption text                                                          | |
// | +-----------------------------------------------------------------------+ |
// | +- menu_e --------------------------------------------------------------+ |
// | | +- menu_e0 (not containing class dp_menu_entry submenu ) -----------+ | |
// | | | +- menu_e0_c ---------------------------------------------------+ | | |
// | | | | menu entry caption                                            | | | |
// | | | +---------------------------------------------------------------+ | | |
// | | +-------------------------------------------------------------------+ | |
// | | +- menu_e1 (contains class dp_menu_entry_submeu) -------------------+ | |
// | | | +- menu_e1_c ---------------------------------------------------+ | | |
// | | | | submenu entry caption                                         | | | |
// | | | +---------------------------------------------------------------+ | | |
// | | | +- submenu -----------------------------------------------------+ | | |
// | | | | ...                                                           | | | |
// | | | +---------------------------------------------------------------+ | | |
// | | +-------------------------------------------------------------------+ | |
// | +-----------------------------------------------------------------------+ |
// +---------------------------------------------------------------------------+
//
// -- menu --
// contains a menu including its caption (not used for danpearlman).
//
// class:
// dp_menu - indicates the top level container of a menu
// dp_menu_level_x - indicates the menu level depth
// dp_section - specifies the choosen section to choose section-dependent
//	layout css
// dp_menu_hidden - the whole menu, including caption, is hidden
// dp_menu_collapsed - all entries of this menu, precisely the entry container
//	is hidden
//
// -- menu_c --
// contains the caption of a menu, in danpearlman its hidden per default.
//
// class:
// dp_menu_caption - indicates the menu caption
//
// -- menu_e --
// entry container of a menu
//
// class:
// dp_menu_entries - indicates the menu entry container
//
// -- menu_e0, menu_e1 ... --
// a single entry of a menu
//
// class:
// dp_menu_entry - indicates a menu entry
// dp_menu_entry_submenu - indicates that this menu entry is a submenu and
//	therefor contains a "menu" structure
// dp_menu_selected - indicates that this menu entry was selected before, so its
//	marked via css
//
// -- menu_e0_c, menu_e1_c ... --
// entry caption
//
// class:
// dp_menu_entry_caption - indicates a menu entry caption

function DP_EntryClicked(entry)
{
	if (DNAniServer.IsAnimationPlaying() === false)
	{
		DPi_ToggleEntry(entry);
	}
}

function DPi_ToggleEntry(entry)
{
	// mark entry
	var menuStruct = DPi_ScanMenu('mn',false);
	var destPath = DPi_GetDestinationNavigationPath(entry);
	
	var ret = [];
	var animationTime = menuStruct.ApplyPath(ret,destPath,0,0);
	for (var i in ret)
	{
		var animation = ret[i];
		var frameCount = animation.GetFrameCount();
		var frameTypes = '';
		for (var j=0;j < frameCount;j++)
		{
			var frame = animation.GetFrame(j);
			frameTypes += 'Frame #'+j+' of class '+frame.GetType()+'\n';
		}
		//alert('Animation with ' + frameCount + ' frames found!\n' + frameTypes + 'Overall playing time ' + animation.GetDuration());
		animation.StartAnimation();
		DNAniServer.AppendAnimation(animation);
	}
}

function DPi_MenuEntry()
{
	this.entryID = null;
	this.isEntryHidden = false;
	this.menuID = null;
	this.isMenuCollapsed = false;
	this.entries = [];
	this.selectedIndex = -1;
	this.parentMenu = null;
	
	
	// Return the ID of the entry that will used in parent menu
	this.GetEntryID = function()
	{
		return this.entryID;
	};
	
	// Return the ID of the menu itself
	this.GetMenuID = function()
	{
		return this.menuID;
	};
	
	// Return the number of menu entries that are appended to this menu
	this.GetMenuEntryCount = function()
	{
		return this.entries.length;
	};
	
	// Return a menu entry of this submenu
	this.GetMenuEntry = function(entryIndex)
	{
		if (entryIndex < this.entries.length)
		{
			return this.entries[entryIndex];
		}
	};
	
	// Return the selected menu entry index of this menu.
	this.GetSelectedMenuEntryIndex = function()
	{
		return this.selectedIndex;
	};
	
	// Check if this entry is hidden
	this.IsEntryHidden = function()
	{
		return this.isEntryHidden;
	};
	
	// Check if this menuentry is a submenu
	this.IsSubmenu = function()
	{
		if (this.entries.length > 0)
		{
			return true;
		}
		else
		{
			return false;
		}
		//return ((this.entries.length > 0) ? (true) : (false));
	};
	
	// Check if this menu is collapsed (and all children)
	this.IsMenuCollapsed = function()
	{
		return this.isMenuCollapsed;
	};
	
	// Set the ID of the entry that will used in parent menu
	this.SetEntryID = function(entryID)
	{
		this.entryID = entryID;
	};
	
	// Set the entry to hidden or not
	this.SetEntryHidden = function(hidden)
	{
		if (hidden === false)
		{
			this.isEntryHidden = false;
		}
		else
		{
			this.isEntryHidden = true;
		}
	};
	
	// Set the ID of the menu itself
	this.SetMenuID = function(menuID)
	{
		this.menuID = menuID;
	};
	
	// Set menu to collapsed or not
	this.SetMenuCollapsed = function(collapsed)
	{
		if (collapsed === false)
		{
			this.isMenuCollapsed = false;
		}
		else
		{
			this.isMenuCollapsed = true;
		}
	};
	
	// Set the selected menu entry of this menu.
	this.SetSelectedMenuEntryIndex = function(entryIndex)
	{
		if (entryIndex < this.entries.length)
		{
			this.selectedIndex = entryIndex;
			return;
		}
		else (entryIndex == -1)
		{
			this.selectedIndex = -1;
			return;
		}
	};
	
	// Set the parent menu of this submenu
	this.SetParentMenu = function(parentMenu)
	{
		this.parentMenu = parentMenu;
	};
	
	// Append a menu entry to this submenu.
	this.AppendMenuEntry = function(menuEntry)
	{
		menuEntry.SetParentMenu(this);
		this.entries.push(menuEntry);
	};

	this.ApplyPath = function(ret,path,level,_offset)
	{
		var offset = (_offset) || (0);
		var overallAnimationTime = 0;
		var selectedIndex = this.GetSelectedMenuEntryIndex();
		if (level < (path.length - 1))
		{
			var pathIndex = path[level];
			var follow = false;
			if (pathIndex == selectedIndex)
			{
				// path fragments are equal; just follow the path
				// recursive
				follow = true;
			}
			else if (selectedIndex == -1)
			{
				// no menu entry is selected; follow the path and select
				// the menu entries
				overallAnimationTime += this.SelectAndExpand(ret,pathIndex,overallAnimationTime + offset);
			}
			else if (pathIndex != selectedIndex)
			{
				overallAnimationTime += this.UnselectAndCollapse(ret,selectedIndex,overallAnimationTime + offset);
				overallAnimationTime += this.SelectAndExpand(ret,pathIndex,overallAnimationTime + offset);
			}
			
			if (follow === true)
			{
				var entry = this.GetMenuEntry(pathIndex);
				if (entry)
				{
					overallAnimationTime += entry.ApplyPath(ret,path,level + 1,overallAnimationTime + offset);
				}
			}
		}
		else if (level == (path.length - 1))
		{
			var pathIndex = path[level];
			if (pathIndex == selectedIndex)
			{
				// path leads to a selected menu entry; just toggle state
//				this.SetSelectedMenuEntryIndex(-1);
//				var entry = this.GetMenuEntry(selectedIndex);
//				if (entry)
//				{
//					overallAnimationTime += entry.CollapseAll(ret,overallAnimationTime + offset);
//				}
				//alert('Toggle ');
				overallAnimationTime += this.UnselectAndCollapse(ret,selectedIndex,overallAnimationTime + offset);
			}
			else if (selectedIndex == -1)
			{
				//alert('first select');
				overallAnimationTime += this.SelectAndExpand(ret,pathIndex,overallAnimationTime + offset);
			}
			else if (pathIndex != selectedIndex)
			{
				//alert('select something different');
				overallAnimationTime += this.UnselectAndCollapse(ret,selectedIndex,overallAnimationTime + offset);
				overallAnimationTime += this.SelectAndExpand(ret,pathIndex,overallAnimationTime + offset);
			}
		}
		
		return overallAnimationTime;
	};

	this.SelectAndExpand = function(ret,entryIndex,_offset)
	{
		var offset = _offset || 0;
		var animationTime = 0;
		this.SetSelectedMenuEntryIndex(entryIndex);
		
		var entry = this.GetMenuEntry(entryIndex);
		if (entry)
		{
			var entryElement = HTML.GetElementByID(entry.GetEntryID());
			if (entryElement)
			{
				var animation = new DNAnimation(entryElement);
				animation.AppendFrame(new DNWaitFrame(offset));
				animation.AppendFrame(new DNClassFrame(50,true,'dp_menu_entry_selected'));
				animationTime += 50;
				ret.push(animation);
			}
		}
		
		animationTime += entry.Expand(ret,1,offset + animationTime);
		return animationTime;
	};

	this.Expand = function(ret,depth,offset)
	{
		var overlapTime = 50;
		var animationTime = 0;
		if ((this.IsSubmenu()) && (depth > 0))
		{
			//alert('Try to expand ' + this.GetMenuID() + '\nMenuCollapsed: '+this.IsMenuCollapsed());
			if (this.IsMenuCollapsed() === true)
			{
				var entryNumber = 0;
				for(var i in this.entries)
				{
					var entry = this.GetMenuEntry(i);
					if ((entry) && (entry.IsEntryHidden() === true))
					{
						var entryElement = HTML.GetElementByID(entry.GetEntryID());
						if (entryElement)
						{
							var animation = new DNAnimation(entryElement);
							animation.AppendFrame(new DNWaitFrame(offset));
							animation.AppendFrame(new DNClassFrame(animationTime,false,'dp_menu_entry_hidden'));
							animation.AppendFrame(new DNRemoveClassFrame('dp_menu_entry_hidden'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_4'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_3'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_2'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_1'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_0'));
							ret.push(animation);
							animationTime += overlapTime;
							
							animationTime += entry.Expand(ret,depth - 1,offset + animationTime);
							
							entryNumber++;
						}
						
						entry.SetEntryHidden(false);
					}				
				}
				
				if (entryNumber > 0)
				{
					animationTime += 250 - overlapTime;
				}
					
				var menuElement = HTML.GetElementByID(this.GetMenuID());
				if (menuElement)
				{
					var animation  = new DNAnimation(menuElement);
					animation.AppendFrame(new DNWaitFrame(offset));
					animation.AppendFrame(new DNRemoveClassFrame('dp_menu_collapsed'));
					
					
					ret.push(animation);
				}
				
				this.SetMenuCollapsed(false);
			}
		}
		else if (depth == 0)
		{
			animationTime += this.CollapseAll(ret,animationTime + offset);
		}
		return animationTime;
	};

	this.UnselectAndCollapse = function(ret,entryIndex,offset)
	{
		var animationTime = 0;
		var entry = this.GetMenuEntry(entryIndex);
		if (entry)
		{
			var animation;
			animationTime += entry.CollapseAll(ret,animationTime + offset);
			
			var entryElement = HTML.GetElementByID(entry.GetEntryID());
			if (entryElement)
			{
				animation = new DNAnimation(entryElement);
				animation.AppendFrame(new DNWaitFrame(offset + animationTime));
				animation.AppendFrame(new DNRemoveClassFrame('dp_menu_entry_selected'));
				animation.AppendFrame(new DNWaitFrame(50));
				
				ret.push(animation);
				animationTime += 50;
				
				this.SetSelectedMenuEntryIndex(-1);
			}
		}
		else
		{
			alert("UnselectAndCollapse WTF!!");
		}
		return animationTime;
	};

	this.CollapseAll = function(ret,offset)
	{
		var animationTime = 0;
		var overlapTime = 50;
		
		if (this.IsSubmenu())
		{
			//alert('Try to collapse ' + this.GetMenuID() + '\nMenuCollapsed ' + this.IsMenuCollapsed());
			if (this.IsMenuCollapsed() === false)
			{
				//alert("CollapseAll(): MenuID:"+this.GetMenuID()+" EntryID:"+this.GetEntryID());
				
				var entryCount = this.GetMenuEntryCount();
				var entryNumber = 0;
				var animation;
				for (var i=(entryCount-1);i >= 0;i--)
				{
					var entry = this.GetMenuEntry(i);
					if (entry.IsEntryHidden() === false)
					{
						var entryElement = HTML.GetElementByID(entry.GetEntryID());
						if (entryElement)
						{
							animationTime += entry.CollapseAll(ret,animationTime);
							
							animation = new DNAnimation(entryElement);
							animation.AppendFrame(new DNWaitFrame(animationTime + offset));
							animation.AppendFrame(new DNRemoveClassFrame('dp_menu_entry_selected'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_0'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_1'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_2'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_3'));
							animation.AppendFrame(new DNClassFrame(50,false,'dp_ani_out_4'));
							animation.AppendFrame(new DNSetClassFrame('dp_menu_entry_hidden'));
							
							ret.push(animation);
							animationTime += overlapTime;
							
							entryNumber++;
						}
						
						entry.SetEntryHidden(true);
					}
				}
				
				
				if (entryNumber > 0)
				{
					animationTime += 250 - overlapTime;
				}
				
				
				var menuElement = HTML.GetElementByID(this.GetMenuID());
				if (menuElement)
				{
					animation = new DNAnimation(menuElement);
					animation.AppendFrame(new DNWaitFrame(animationTime + offset));
					animation.AppendFrame(new DNSetClassFrame('dp_menu_collapsed'));
					
					ret.push(animation);
				}
				
				this.SetMenuCollapsed(true);
				this.SetSelectedMenuEntryIndex(-1);
			}
		}
		return animationTime;
	};		
};

function DPi_ScanMenu(menuID,hidden)
{
	var menu = new DPi_MenuEntry();
	menu.SetEntryID(0);
	menu.SetMenuID(menuID);
	
	var menuElement = HTML.GetElementByID(menuID);
	if ((HTML.HasElementClass(menuElement,'dp_menu_collapsed')) || (hidden === true))
	{
		HTML.AppendElementClass(menuElement,'dp_menu_collapsed');
		menu.SetMenuCollapsed(true);
		hidden = true;
	}
	else
	{
		menu.SetMenuCollapsed(false);
	}
	
	var entryElements = DPi_GetMenuEntries(menuElement);
	//alert('Scanned ' + entryElements.length + ' for ' + menuID);
	for (var i in entryElements)
	{
		var entryElement = entryElements[i];
		var entrySelected = false;
		if (HTML.HasElementClass(entryElement,'dp_menu_entry_selected'))
		{
			entrySelected = true;
		}
		
		if (HTML.HasElementClass(entryElement,'dp_menu_entry_submenu'))
		{
			// submenu entry
			var submenuElement = DPi_GetEntrySubmenu(entryElement);
			var submenu = DPi_ScanMenu(submenuElement.id,hidden);
			submenu.SetEntryID(entryElement.id);
			menu.AppendMenuEntry(submenu);
			
			if ((HTML.HasElementClass(entryElement,'dp_menu_entry_hidden')) || (hidden === true))
			{
				//HTML.AppendElementClass(submenuElement,'dp_menu_entry_hidden');
				submenu.SetEntryHidden(true);
			}
			else
			{
				submenu.SetEntryHidden(false);
			}
		}
		else
		{
			// simple entry
			var entry = new DPi_MenuEntry();
			entry.SetEntryID(entryElement.id);
			menu.AppendMenuEntry(entry);
			
			if ((HTML.HasElementClass(entryElement,'dp_menu_entry_hidden')) || (hidden === true))
			{
				//HTML.AppendElementClass(entryElement,'dp_menu_entry_hidden');
				entry.SetEntryHidden(true);
			}
			else
			{
				entry.SetEntryHidden(false);
			}
		}
		
		if (entrySelected === true)
		{
			menu.SetSelectedMenuEntryIndex(i);
		}
	}
	
	//alert('Scanned ' + menu.GetMenuEntryCount() + ' found for ' + menuID);
	return menu;
};

// DPi_GetFirstDifferenceLevel -------------------------------------------------
// Return the first level in navigation paths where a difference is detected.
//
// @param
// currentPath - current path
// destinationPath - destination path
// ------------------------------------------------- DPi_GetFirstDifferenceLevel
function DPi_GetFirstDifferenceLevel(currentPath,destinationPath)
{
	var equalLevel = 0;
	while ( (equalLevel < currentPath.length) &&
		(equalLevel < destinationPath) &&
		(currentPath[equalLevel] == destinationPath[equalLevel]) )
	{
		equalLevel++;
	}
	return equalLevel;
}

// DPi_GetDestinationNavigationPath --------------------------------------------
// Return the navigation path to a given entry. Every part of the navigation
// path indicates the selected entry by its index.
//
// @param
// entry - reference entry
//
// @return
// array of entry indices
// -------------------------------------------- DPi_GetDestinationNavigationPath
function DPi_GetDestinationNavigationPath(entry)
{
	var path = [];
	var menu = DPi_GetEntryMenu(entry);
	var entryIndex = DPi_GetEntryIndex(menu,entry);
	if (entryIndex !== null)
	{
		var parentMenuEntry = DPi_GetMenuParentEntry(menu);
		if (parentMenuEntry)
		{
			var pathPrefix = DPi_GetDestinationNavigationPath(parentMenuEntry);
			path = pathPrefix.concat([entryIndex]);
		}
		else
		{
			path.push(entryIndex);
		}
	}
	return path;
}

// DPi_GetCurrentNavigationPath ------------------------------------------------
// Return the current navigation path. Every part of the navigation path
// indicates the selected entry by its index.
//
// @param
// menu - reference menu
//
// @return
// array of entry indices
// ------------------------------------------------ DPi_GetCurrentNavigationPath
function DPi_GetCurrentNavigationPath(menu)
{
	var path = [];
	var menuEntries = DPi_GetMenuEntries(menu);
	for (var i in menuEntries)
	{
		if (HTML.HasElementClass(menuEntries[i],'dp_menu_entry_selected'))
		{
			path = path.concat([i]);
			if (HTML.HasElementClass(menuEntries[i],'dp_menu_entry_submenu'))
			{
				var subMenu = DPi_GetEntrySubmenu(menuEntries[i]);
				if (subMenu)
				{
					path = path.concat(DPi_GetCurrentNavigationPath(subMenu));
				}
			}
			break;
		}
	}
	return path;
}

// DPi_GetMenuParentEntry ------------------------------------------------------
// Return the parent menu entry where a given menu is the submenu of it.
//
// @param
// menu - reference menu
//
// @return
// null - no parent menu exists
// otherwise - entry of the parent menu
// ------------------------------------------------------ DPi_GetMenuParentEntry
function DPi_GetMenuParentEntry(menu)
{
	var menuParent = menu.parentNode;
	if ((menuParent) && (HTML.HasElementClass(menuParent,'dp_menu_entry')))
	{
		return menuParent;
	}
	return null;
}

// DPi_GetEntryIndex -----------------------------------------------------------
// Return the index of an entry that indicates the index to that entry in a
// given menu.
//
// @param
// menu - reference menu
// entry - reference entry
//
// @return
// null - entry not found in given menu
// otherwise - index of that entry
// ----------------------------------------------------------- DPi_GetEntryIndex
function DPi_GetEntryIndex(menu,entry)
{
	var entries = DPi_GetMenuEntries(menu);
	for (var i in entries)
	{
		if (entries[i].id == entry.id)
		{
			return i;
		}
	}
	return null;
}

// DPi_GetEntrySubmenu ---------------------------------------------------------
// Return the (sub)menu structure of an entry.
//
// @param
// entry - reference entry
//
// @return
// null - no (sub)menu exists
// otherwise - submenu of entry
// --------------------------------------------------------- DPi_GetEntrySubmenu
function DPi_GetEntrySubmenu(entry)
{
	var submenu = HTML.FirstChildTag(entry);
	while (submenu)
	{
		if (HTML.HasElementClass(submenu,'dp_menu'))
		{
			return submenu;
		}
		submenu = HTML.NextSiblingTag(submenu);
	}
	return null;
}

// DPi_GetEntryMenu ------------------------------------------------------------
// Return the menu where a given entry belongs to.
//
// @param
// entry - reference entry
//
// @return
// null - no menu found
// otherwise - menu
// ------------------------------------------------------------ DPi_GetEntryMenu
function DPi_GetEntryMenu(entry)
{
	var entryParent = entry.parentNode;
	if ((entryParent) && (HTML.HasElementClass(entryParent,'dp_menu_entries')))
	{
		var menu = entryParent.parentNode;
		if ((menu) && (HTML.HasElementClass(menu,'dp_menu')))
		{
			return menu;
		}
	}
	return null;
}

// DPi_GetMenuEntries ----------------------------------------------------------
// Return all entries of a menu.
//
// @param
// menu - reference menu
//
// @return
// an array of entries
// ---------------------------------------------------------- DPi_GetMenuEntries
function DPi_GetMenuEntries(menu)
{
	var entries = [];
	var entryContainer = HTML.FirstChildTag(menu);
	while (entryContainer)
	{
		if (HTML.HasElementClass(entryContainer,'dp_menu_entries'))
		{
			var entry = HTML.FirstChildTag(entryContainer);
			while (entry)
			{
				if (HTML.HasElementClass(entry,'dp_menu_entry'))
				{
					entries.push(entry);
				}
				entry = HTML.NextSiblingTag(entry);
			}
			break;
		}
		entryContainer = HTML.NextSiblingTag(entryContainer);
	}
	return entries;
}

// DPi_GetMenuEntry ------------------------------------------------------------
// Return an entry of a menu given by its index.
//
// @param
// menu - reference menu
// entryIndex - index of the entry
//
// @return
// null - failed
// otherwise - entry
// ------------------------------------------------------------ DPi_GetMenuEntry
function DPi_GetMenuEntry(menu,entryIndex)
{
	var entries = DPi_GetMenuEntries(menu);
	return entries[entryIndex];
}

// DPi_GetSiblingEntries -------------------------------------------------------
// Return a list of sibling menu entries, except the given one.
//
// @param
// entry - reference entry
//
// @return
// sibling menu entries
// ------------------------------------------------------- DPi_GetSiblingEntries
function DPi_GetSiblingEntries(entry)
{
	var siblingEntries = [];
	var entryContainer = entry.parentNode;
	var siblingEntry = HTML.FirstChildTag(entryContainer);
	while (siblingEntry)
	{
		if (HTML.HasElementClass(siblingEntry,'dp_menu_entry'))
		{
			if (siblingEntry.id != entry.id)
			{
				siblingEntries.push(siblingEntry);
			}
		}
		siblingEntry = HTML.NextSiblingTag(siblingEntry);
	}
	return siblingEntries;
}

// DPi_GetEntryLevel -----------------------------------------------------------
// Return the menu level of an entry.
//
// @param
// entry - entry that should checked
//
// @return
// null - failed
// otherwise - menu level of entry
// ----------------------------------------------------------- DPi_GetEntryLevel
function DPi_GetEntryLevel(entry)
{
	var menu = entry.parentNode.parentNode;
	for (var i=0;i < 10;i++)
	{
		if (HTML.HasElementClass(menu,'dp_menu_level_' + i))
		{
			return i;
		}
	}
}
