var MenuBase = {
	menu_name: 'menu-container', 
	initial_state: [],
	level_count: null,
	
	levels: [],
	level_open: [],
	level_menu: [],
	level_item: [],
	
	locked: false,
	mouse_over_timer: null,
	mouse_move_timer: null,
	
	entered: false,
	x: 0,
	y: 0,
	
	initialize: function(level_count, menu_name)
	{
		this.level_count = level_count || 3;
		this.menu_name = menu_name || 'menu-container';
		
		// Build the menu level containers
		for ( var i = 0; i < this.level_count; i++ )
		{
			if ( i == 0 )
			{
				$('menu-container').insert({top: '<div id="menu-level-0"></div>'});
			}
			else
			{
				$('menu-container').insert({bottom: '<div id="menu-level-' + i + '"></div>'});
				$('menu-level-' + i).hide();				
			}
			
			this.levels.push($('menu-level-' + i));
			
			this.level_open.push(( i == 0 ) ? true : false);
			
			this.level_menu.push(null);
			this.level_item.push(null);
		}
		
		// Build the menu levels
		this.build($('menu'));
		
		// Open to initial state
		this.openInitialState();
		
		// Watch for the cursor leaving the menu to restore initial state
		Element.observe(document, 'mousemove', this.mouseMove.bind(this));
	},
	
	build: function(container, level, parent_identifier)
	{
		level = level || 0;
		parent_identifier = parent_identifier || "";
		
		elements = container.childElements();
		
		// Make sure the first element exists and that it is a link
		var first = elements[0];
		if ( elements.length == 0 || first.tagName.toLowerCase() != "a" )
		{
			return;
		}
		
		// Remove all submenus
		var sub_menus = first.adjacent('div');
		sub_menus.each(function(element) {
			elements = elements.without(element);
		});
		
		var count = 0;
		elements.each(function(element) {
			var next_el = element.next(0);
			
			// Process submenus
			if ( next_el && next_el.tagName.toLowerCase() == "div" )
			{
				var next = level + 1;
				var identifier = parent_identifier + "-" + count;
				var id = "m-" + next + identifier;
				
				this.levels[next].insert({bottom: '<div id="' + id + '"></div>'});
				this.build(next_el, next, identifier);
			}
			
			// Process the element
			element.id = "mi-" + level + parent_identifier + "-" + count;
			element.observe('mouseover', this.mouseOver.bind(this));
			
			if ( element.hasClassName('selected') )
			{
				this.initial_state.unshift(count);
			}
			
			// Process the holding container
			var holder = ( level == 0 ) ? this.levels[level] : $('m-' + level + parent_identifier);
			
			holder.insert(element.remove());
			
			if ( level != 0 )
			{
				holder.setStyle({
					position: 'absolute',
					display: 'none'
				});
				
				$('menu-level-' + level).setStyle({
					position: 'relative',
					display: 'none'
				});
				
				if ( count != 0 )
				{
					//element.insert({before: '<div class="sprite-image sub-menu-link-separators">&nbsp;</div>'});
				}
			}
			
			/*if ( level == 0 ) 
			{
				// insert divs for rounded corners around href
				element.insert({before: '<div class="sub-menu-link-separator-left">&nbsp;</div>'});
				element.insert({after: '<div class="sub-menu-link-separator-right">&nbsp;</div>'});
			}*/
			
			
			count++;
		}.bind(this));
		
		// Remove the original holding containers
		container.remove();
	},
	
	openInitialState: function()
	{
		var next, item, submenu;
		
		for ( var i = 0, length = this.initial_state.length; i < length; i++ )
		{
			next = i + 1;
			
			item = "mi-" + i;
			submenu = "m-" + next;
			
			// Build the element and submenu id's
			for ( j = 0; j < next; j++ )
			{
				item += "-" + this.initial_state[j];
				submenu += "-" + this.initial_state[j];
			}
			
			item = $(item);
			submenu = $(submenu);
			
			if ( submenu )
			{
				submenu.show();
				this.levels[next].show();
					
				this.level_menu[next] = submenu;
				this.level_open[next] = true;
			}
				
			if ( item )
			{
				this.select(item);
				this.level_item[i] = item;
			}
		}
	},
	
	restoreInitialState: function()
	{
		if ( this.locked || this.isUnchanged() )
		{
			return;
		}
		
		for ( var i = 0, length = this.initial_state.length; i < length; i++ )
		{
			next = i + 1;
			
			item = "mi-" + i;
			submenu = "m-" + next;
			
			// Build the element and submenu id's
			for ( j = 0; j < next; j++ )
			{
				item += "-" + this.initial_state[j];
				submenu += "-" + this.initial_state[j];

			}
			
			item = $(item);
			submenu = $(submenu);
			
			this.animate(item, i, submenu);
		}
	},
	
	animate: function(element, level, submenu)
	{
		this.locked = true;
		
		this.deselect(this.level_item[level]);
		this.select(element);
		
		this.level_item[level] = element;
		
		submenu = $(submenu);
		
		var next = level + 1;
		
		// Element has a submenu but the submenu is not visible
		if ( submenu && (submenu != this.level_menu[next] || !submenu.visible()) )
		{
			if ( this.level_open[next] )
			{
				this.deselect(this.level_item[next]);
				this.closeDescendants(next, false);
				
				if ( this.level_menu[next] )
				{
					new Effect.Fade(this.level_menu[next], {
						duration: 0.3
					});
				}
				new Effect.Appear(submenu, {
					duration: 0.3,
					afterFinish: this.unlock.bind(this)
				});
			}
			else
			{
				if ( this.level_menu[next] )
				{
					this.level_menu[next].hide();
				}
				
				submenu.show();
				
				this.levels[next].setStyle({height: ''});
				
				new Effect.BlindDown(this.levels[next], {
					duration: 0.3,
					queue: {position:'end', scope: 'menuopen'},
					afterFinish: this.unlock.bind(this)
				});
				
				this.level_open[next] = true;				
			}
			
			// Set the new stuff
			this.level_menu[next] = submenu;
		}
		// Element doesn't have a submenu and the next level is open
		else if ( !submenu && this.descendantOpen(level) )
		{
			this.closeDescendants(level);
		}
		else
		{
			this.locked = false;
		}
	},
	
	mouseOver: function(e)
	{
		e = e || window.event;
		
		setTimeout(this.mouseOverDelay.bind(this, e), 150);
	},
	
	mouseOverDelay: function(e, count)
	{
		if ( this.locked )
		{
			if ( this.mouse_over_timer == null )
			{
				count = count || 0;
				
				if ( count < 20 )
				{
					count++;
					this.mouse_over_timer = setTimeout(this.mouseOverDelay.bind(this, e, count), 100);
				}
			}
			
			return;
		}
		
		this.mouse_over_timer = null;
		
		var link = this.getLinkFromEvent(e);
		
		if ( this.mouseIn(link) )
		{
			var data = this.getLinkData(link);
		
			this.animate(link, data.level, data.submenu);
		}
	},
	
	mouseMove: function(e, count)
	{
		e = e || window.event;

		var x, y;
		
		// Get the cursors position
		if (e.pageX || e.pageY)
		{
			x = e.pageX;
			y = e.pageY;
		}
		else if (e.clientX || e.clientY)
		{
			x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
			y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
		}
		
		if ( x == undefined || y == undefined )
		{
			return;
		}
		
		this.x = x;
		this.y = y;
		
		if ( this.mouseIn($('menu-container')) )
		{
			this.entered = true;
			return;
		}
		
		if ( !this.locked && this.entered )
		{
			this.deselectLast();
			
			if ( this.isUnchanged() )
			{
				this.openInitialState();
			}
			
			this.entered = false;
		}
	},
	
	closeDescendants: function(level, unlock)
	{
		unlock = unlock || true;
		
		for ( var i = (this.level_count - 1); i > level; i-- )
		{
			if ( !this.level_open[i] )
			{
				continue;
			}
			
			var do_unlock = ( i == (level + 1) ) ? unlock : false;
			
			new Effect.BlindUp(this.levels[i], {
				duration: 0.3,
				queue: {position:'end', scope: 'menuclose'},
				afterFinish: this.afterHide.bind(this, i, do_unlock)
			});
		}
	},
	
	afterHide: function(level, unlock)
	{
		if ( this.level_item[level] )
		{
			this.deselect(this.level_item[level]);
			this.level_item[level] = null;			
		}
		
		if ( this.level_menu[level] )
		{
			this.level_menu[level].hide();
			this.level_menu[level] = null;
		}
		
		this.level_open[level] = false;
		
		if ( unlock == undefined )
		{
			unlock = true;
		}
		
		if ( unlock )
		{
			this.unlock();
		}
	},
	
	select: function(element)
	{
		if ( element )
		{
			element.addClassName('selected');
		}
	},
	
	deselect: function(element)
	{
		if ( element )
		{
			element.removeClassName('selected');
		}
	},
	
	deselectAll: function()
	{
		for ( i = 0; i < this.level_count; i++ )
		{
			this.deselect(this.level_item[i]);
			this.level_item[i] = null;
		}
	},
	
	deselectLast: function()
	{
		var item;
		
		for ( i = this.level_count - 1; i >= 0 ; i-- )
		{
			item = this.level_item[i];
			
			if ( this.level_open[i] )
			{
				if ( this.level_item[i] != null )
				{
					this.deselect(item);
				}
				
				return;
			}
		}
	},

	unlock: function()
	{
		this.locked = false;
	},
	
	isUnchanged: function()
	{
		var menu_level, submenu, item;
		
		for ( var level = 1, length = this.initial_state.length; level < length; level++ )
		{
			submenu = "m-" + level;
			item = "mi-" + level;
			
			for ( var i = 0; i < level; i++ )
			{
				submenu += "-" + this.initial_state[i];				
			}
			
			for ( var i = 0; i < level + 1; i++ )
			{
				item += "-" + this.initial_state[i];
			}
			
			menu_level = $('menu-level-' + level);
			submenu = $(submenu);
			item = $(item);
			
			// Check if the menu level is visible and marked as opened
			if ( !menu_level.visible() || !this.level_open[level] )
			{
				return false;
			}
			
			// Check if the submenu is visible and marked as selected
			if ( !submenu || !submenu.visible() || this.level_menu[level] != submenu )
			{
				return false;
			}
			
			// Check if the item is marked as selected
			if ( !item || this.level_item[level] != item )
			{
				return false;
			}
		}
		
		menu_level = $('menu-level-' + this.initial_state.length);
		
		// Check that a lower level is not opened for levels less than the current level
		if ( menu_level && (menu_level.visible() || this.level_open[level]) )
		{
			return false;
		}
		
		return true;
	},
	
	descendantOpen: function(level)
	{
		var opened = false;
		for ( var i = (this.level_count - 1); i > level; i-- )
		{
			if ( this.level_open[i] )
			{
				opened = true;
				break;
			}
		}
		
		return opened;
	},
	
	mouseIn: function(element)
	{
		element = $(element);
		
		var coords = this.getBoxCoordinates(element);
		
		if ( this.y > coords.top && this.x > coords.left && this.y < coords.bottom && this.x < coords.right )
		{
			return true;
		}
		else
		{
			return false;
		}
	},
	
	getLinkFromEvent: function(e)
	{
		var link;
		
		if( e.target )
		{
			link = e.target;
		}
		else if( e.srcElement )
		{
			link = e.srcElement;
		}
		
		if ( link.tagName.toLowerCase() != "a" ) // defeat Safari bug, node type 3 = text node
		{
			link = link.up('a');
		}
		
		return link;
	},
	
	getLinkData: function(element)
	{
		var id = element.id;
		
		var start = id.indexOf('-') + 1;
		var end = id.indexOf('-', start);
		
		var level = parseInt(id.substring(start, end));
		
		var identifier = id.substring(end + 1);
		
		var submenu = "m-" + (level + 1) + "-" + identifier;
		
		return {id: id, level: level, submenu: submenu};
	},
	
	getBoxCoordinates: function(element)
	{
		var offset = $(element).cumulativeOffset();
		var dimensions = $(element).getDimensions();
		
		var top_y = offset.top;
		var top_x = offset.left;
		var bottom_y = offset.top + dimensions.height;
		var bottom_x = offset.left + dimensions.width;
		
		return {top: top_y, left: top_x, bottom: bottom_y, right: bottom_x};
	}
}

var Menu = Class.create(MenuBase);