(function ($) {
$.widget("ui.swimMenu", {
	options: {
		header: 'ul:first > li',
		animated: true,
		duration: 'fast',
		cursor: 'pointer',		
		crumbClass: 'crumb',
		itemClass: 'item',
		submenuClass: 'sub',
		dividerClass: 'divider',
		placeHolderClass: 'placeholder',
		
		event: 'click',
		autoHeight: 200,
		icons: {header: 'icon_go'}
//		navigation: true,
//		navigationFilter: function () {
//			return this.href.toLowerCase() == location.href.toLowerCase();
//		}
	},
   
	_create: function () {
		var o = this.options;
		var self = this;
		this.level = [];
		this.container = this.element;
		this.headers = this.element.find(o.header);
		this.menu = $('<div />').addClass('menu').appendTo(this.container);
		
		// append home button
		this._addNavBar();
		
		// setup menu for the first time
		this.container.data('originalList.swimMenu', this.headers);		
		this._addItems(this.headers);
		this.headers.parent().remove();
		
		this.resize();
		
		// trigger events
		var ui = {
			submenus: this.submenus,
			items: this.menu.children('.' + o.itemClass).not(this.submenus),
			crumb: this.nav.find('.' + o.crumbClass).first()
		};
		this._trigger('start', null, ui);
		this._trigger('change', null, ui);
	},
		
	resize: function () {
		var o = this.options;
		if (o.autoHeight) {
			
			// set the desired height of the menu
			this.menu.css('height', o.autoHeight - this.nav.outerHeight());
			 
			// fill the remaining menu space? Useful for animating over the menu section's entire viewport.
			var menuItemsHeight = 0;
			this.menu.children().each(function () {
				menuItemsHeight += $(this).outerHeight();
			});
			var placeHolderHeight = this.menu.outerHeight() - menuItemsHeight;
			
			// only create the placeHolder if there is space to fill
			if (placeHolderHeight > 0) {
				$('<div />')
					.css('height', placeHolderHeight)
					.addClass(o.placeHolderClass)
					.appendTo(this.menu);
			}
		}
	},
	
	activate: function (level) {
		this.level = level;
		this._submenuEventHandler();
	},
	
	destroy: function () {
		$.Widget.prototype.destroy.apply(this, arguments); // default destroy
		// TODO: put back the original list
	},
	
	restart: function () {
		var o = this.options;		
		this.menu.empty();
		this.nav.remove();
		this.level = [];
		this._addNavBar();
		this._addItems(this.container.data('originalList.swimMenu'));
		this.resize();
		this._trigger('change', null, {
			submenus: this.submenus,
			items: this.menu.children('.' + o.itemClass).not(this.submenus),
			crumb: this.nav.find('.' + o.crumbClass).first()
		});
	},
	
	refresh: function () {
		var o = this.options;
		var toShow = this.container.data('originalList.swimMenu');
		var toShowContainer;
		$(this.level).each(function () {			
			toShowContainer = toShow.eq(this);
			toShow = toShowContainer.find(o.header);
		});
		this._addItems(toShow);
	},
	
	setList: function (elems) {
		this.container.data('originalList.swimMenu', elems);
		this.restart();
	},
	
	_addNavBar: function () {
		var self = this;
		var o = this.options;
		this.nav = $('<div />').addClass('nav').prependTo(this.container);
		
		$('<span/>')
			.addClass('root ' + o.crumbClass)
			.html('Home')
			.click(function () {
				self.restart();
			})
			.appendTo(this.nav);
		
		this.nav.delegate(':not(root).' + o.crumbClass, 'click', function (e) {
			var index = self.nav.children('.' + o.crumbClass).index($(e.target));
			var level = $(e.target).data('level.swimMenu').slice(0, index);
			
			// remove the excess crumbs and dividers
			self.nav.children('.' + o.crumbClass).filter(':eq(' + index + '), :gt(' + index + ')').remove();
			self.nav.children('.' + o.dividerClass).filter(':eq(' + (index - 1) + '), :gt(' + (index - 1) + ')').remove();
			
			self.activate(level);
			e.preventDefault();
		});
		
		this.resize();
	},
	
	_addNav: function (crumb) {
		var self = this;
		var o = this.options;
		
		// append divider
		$('<span />').addClass(o.dividerClass)
			.html(' > ')
			.appendTo(this.nav);
		
		// append crumb
		crumb.addClass(o.crumbClass)
			.data('level.swimMenu', this.level)
			.appendTo(this.nav);
	},
	
	_addItems: function (elems) {
		var self = this;
		var o = this.options;
		
		var newMenu = $('<div />').addClass('menu');
				
		// transform elems into new menu
		elems.each(function () {
			$('<div />').data('origElement.swimMenu', this)
				.html($(this).children().first().html())
				.addClass(o.itemClass + ($(this).find(o.header).length ? ' ' + o.submenuClass : ''))
				.mouseover(function () { $(this).addClass('hover'); })
				.mouseout(function () { $(this).removeClass('hover'); })
				.appendTo(newMenu);
		});
		
		// transition to new menu?
		if (o.animated) {
			this._animate(newMenu);
		} else {
			this.menu.remove();
			newMenu.appendTo(this.container);
		}
		
		// rebind menu
		this.menu = newMenu;
		this._refreshMenu();
	},
	
	_animate: function (newMenu) {
		var o = this.options;
		var oldMenu = this.menu;
		var index = this.level.slice(-1);
		var lt = oldMenu.children(':eq(' + index + '), :lt(' + index + ')');
		var gt = oldMenu.children().not(lt);
		
		// create the new menu and hide it behind the old menu
		//oldMenu.css('position', 'absolute');
		newMenu.offset(oldMenu.offset())
			.css({
				position: 'absolute',
				width: oldMenu.css('width'),
				height: oldMenu.css('height')
			})
			.hide()
			.appendTo(this.container);
		
		// create the top and bottom half of the animation
		var topHalf = $('<div />');
		var bottomHalf = $('<div />');
		topHalf = lt.wrapAll(topHalf).parent();
		bottomHalf = gt.wrapAll(bottomHalf).parent();
		
		// slide each half away from the center, and reveal the new menu
		topHalf.hide("slide", {direction: 'up'}, o.duration);
		bottomHalf.hide("slide", {direction: 'down'}, o.duration);
		//newMenu.animate({'background-color': ('#0' + (Math.round(255 * Math.random()))).toString(16).replace(/^#0([0-9a-f]{6})$/i, '#$1')}, {duration: o.duration, queue: false});
		newMenu.fadeIn(o.duration, function () {
			oldMenu.remove();
			$(this).css({
				'position': '',
				'top': '',
				'left': ''
			});
		});
	},
	
	_refreshMenu: function () {
		var self = this;
		var o = this.options;
		
		// find sub-menus
		this.submenus = this.menu.find('.sub')
			.css('cursor', o.cursor);
		
		// append icons
		this._createIcons();
		
		// trigger resize
		this.resize();
		
		// bind header activation
		if (o.event) {
			this.submenus.bind((o.event) + '.swimMenu', function (e) {
				self.level.push($(this).index());
				self._submenuEventHandler.apply(self, arguments);
				e.preventDefault(); // stop default action, but do not stop event propagation
			});
		}
	},
	
	_createIcons: function () {
		var o = this.options;		
		if (o.icons) {
			this.submenus.append($('<span />').addClass('icon ' + o.icons.header));
		}
	},
	
	_destroyIcons: function () {
		this.submenus.children('.' + o.itemClass).remove();
	},
	
	_submenuEventHandler: function () {		
		var o = this.options;
		var self = this;
		var toShow = this.container.data('originalList.swimMenu');
		var toShowContainer;
		
		// traverse the levels of the originalList
		$(this.level).each(function () {			
			toShowContainer = toShow.eq(this);
			toShow = toShowContainer.find(o.header);
		});
		
		// trigger pre change event
		this._trigger('preChange', null, {
			submenus: this.submenus,
			items: this.menu.children('.' + o.itemClass).not(this.submenus),
			crumb: toShowContainer.children().first().clone()
		});
		
//		// traverse the levels of the originalList
//		$(this.level).each(function () {			
//			toShowContainer = toShow.eq(this);
//			toShow = toShowContainer.find(o.header);
//		});
		
		// append bread-crumb
		var crumb = toShowContainer.children().first().clone();
		this._addNav(crumb);
		this.resize();
		
		// refresh the menu		
		this._addItems(toShow);
		
		// trigger change event
		this._trigger('change', null, {
			submenus: this.submenus,
			items: this.menu.children('.' + o.itemClass).not(this.submenus),
			crumb: crumb
		});
	}
});
})(jQuery);

