(function($) {
	var Navigation = (function() {
		var _enrich = function(navigation) {
			// insert helpful node attributes: parent, level, order
			// filter for actnav
			if (navigation.enriched) { return;}
			
			var walk = function(nodes, parent, level) {
				for(var i = 0, l = nodes.length; i < l; ++i) {
					var node = nodes[i];
					node.level = level;
					node.order = i;
					if (parent) { node.parent = parent;}
					if (node.pages) {
						if (node.properties.actnav) {
							walk(node.pages, node, level + 1);
						} else {
							node.hiddenPages = node.pages;
							delete node.pages;
						}
					}
				}
			};
			walk(navigation.roots, false, 0);
			navigation.enriched = true;
		};

		var _findByUNID = function(unid, nodes) {
			for(var i = 0, l = nodes.length; i < l; ++i) {
				var node = nodes[i];
				if (node.properties.unid == unid) { return node;}
				if (node.pages) {
					var result = _findByUNID(unid, node.pages);
					if (result) { return result;}
				}
			}
		};

		var _createState = function(activeNode) {
			var state = {
				active: activeNode,
				path: {},
				selected: {}
			};
			if (state.active) {
				state.selected[state.active.properties.unid] = state.active;
				for(var node = state.active.parent; node; node = node.parent) {
					state.path[node.properties.unid] = node;
				}
			}
			return state;
		};

		var _display; // cyclic referencing

		var _mouseenterDeferred = function() {
			if (this.breadcrumb) {
				this.state.breadcrumbHover = this.node;
				this.state.hover = this.node;
				this.state.breadcrumbHoverDirect = this.node;
				delete this.state.hoverDirect;
			} else {
				this.state.hover = this.node;
				this.state.hoverDirect = this.node;
				delete this.state.breadcrumbHoverDirect;
			}
			_display(this.navigation, this.state);
		};

		var _mouseenter = function(event) {
			var data = event.data;

			// disable events during fading
			if (data.fading || (data.fading = ($(event.target).parents('.fading').length > 0))) { return;}

			// kill all pending mouseleaves
			if (data.state.deferredLeave) { clearTimeout(data.state.deferredLeave);}
			if (data.node) {
				// defer mouseenter
				if (data.state.deferredEnter) { clearTimeout(data.state.deferredEnter);}
				data.state.deferredEnter = setTimeout($.proxy(_mouseenterDeferred, data), 150);
			}
		};

		var _mouseleaveDeferred = function() {
			delete this.state.breadcrumbHover;
			delete this.state.breadcrumbHoverDirect;
			delete this.state.hover;
			delete this.state.hoverDirect;

			_display(this.navigation, this.state);
		};

		var _mouseleave = function(event) {
			var data = event.data;
			// disable events during fading
			if (data.fading || (data.fading = ($(event.target).parents('.fading').length > 0))) { return;}
			// defer mouseleave
			if (data.state.deferredLeave) { clearTimeout(data.state.deferredLeave);}
			data.state.deferredLeave = setTimeout($.proxy(_mouseleaveDeferred, data), 400);
		};

		var _click = function(event) {
			return;
			var data = event.data;
			// kill all pending mouseleaves
			if (data.state.deferredEnter) { clearTimeout(data.state.deferredEnter);}
			if (data.state.deferredLeave) { clearTimeout(data.state.deferredLeave);}
			// change navigation
			if (data.node) {
				event.preventDefault();
				data.navigation.displayFor(data.node.properties.unid);
			}
		};

		var _orderPathFirst = function(node, state) {
			var unid = node.properties.unid;
			return ((state.selected[unid] || state.path[unid])? -1: node.order);
		};
		
		var _cmpPathFirst = function(a, b) {
			return _orderPathFirst(a, this.state) - _orderPathFirst(b, this.state);
		};

		_display = function(navigation, state) {
			// completely redraw the navigation according to state
			var node;

			// step	1: redraw breadcrumb
			// 1.1: find elements
			var jBreadcrumb = $(navigation.selectors.breadcrumb);
			var jBreadcrumbLast = jBreadcrumb.children().addClass('fading fading-free');
			var jBreadcrumbNext = $('<div />');//.hide();
			jBreadcrumb.append(jBreadcrumbNext);
			
			// 1.2: find breadcrumb endries
			var entries = [];
			for(node = state.active; node && node.parent; node = node.parent) { entries.unshift(node);}

			// 1.3: draw entries
			$.each(entries, function(index, node) {
				if (index) { jBreadcrumbNext.append(' | ');}
				var data = {navigation: navigation, state: state, node: node, breadcrumb: true};
				var a = $('<a><' + '/a>')
					.attr('href', node.properties.url.split('jsonview').join('contentview')+'?open&testnav')
					.text(node.properties.subject)
					.addClass('level' + node.level)
					.bind("click", data, _click)
					.bind("mouseleave", data, _mouseleave);
				if (node === state.breadcrumbHover) { 
					a.addClass('hover');
				} 
				if (node !== state.breadcrumbHoverDirect) {
					a.bind("mouseenter", data, _mouseenter);
				}
				jBreadcrumbNext.append(a);
			});

			// 1.4: animate tranistion
			/*
			jBreadcrumbLast.stop(true, true).fadeOut().queue(function(next) { $(this).remove(); next();});
			jBreadcrumbNext.fadeIn();
			*/
			jBreadcrumbLast.remove();

			// step 2: redraw levels
			// 2.1: find elements and initialize level-info
			var levels = $.map(navigation.selectors.levels, function(selector) {
				var level = {jLevel: $(selector)};
				level.jLevelLast = level.jLevel.children().addClass('fading');
				level.jLevelNext = $('<div />');//.hide();
				level.jLevel.append(level.jLevelNext);
				return level;
			});

			// 2.2: compute level-info
			if (state.breadcrumbHover) {
				// display levels from active node up to state.breadcrumbHover
				// don't display levels 0 and 1
				var minLevel = state.active.level;
				for(node = state.active; node && node.level >= 2; node = node.parent) {
					levels[node.level].nodes = node.parent.pages;
					minLevel = node.level;
					if (node === state.breadcrumbHover || node.level <= 2) { break;}
				}
				if (state.hover) {
					// overwrite displayed levels with state.hover and parents
					for(node = state.hover; node && node.level >= 2; node = node.parent) {
						levels[node.level].nodes = node.parent.pages;
						levels[node.level].hover = node;
						if (node.level <= minLevel) { break;}
					}
					var lastLevel = state.hover.level; 
					if (state.hover.pages) {
						// display children of state.hover (if any)
						levels[++lastLevel].nodes = state.hover.pages;
					}
					// clear deeper levels
					while(++lastLevel < levels.length) {
						delete levels[lastLevel].nodes;
					}
				}
			}

			// 2.3: draw levels
			$.each(levels, function(index, level) {
				if (level.nodes) {
					var nodes = $.merge([], level.nodes);
					//nodes.sort($.proxy(_cmpPathFirst, {state: state}));
					for(var i = 0, l = nodes.length; i < l; ++i) {
						var node = nodes[i];
						if (i) { level.jLevelNext.append('&nbsp;&nbsp;&nbsp;&nbsp;');}
						var data = {navigation: navigation, state: state, node: node};
						var a = $('<a><' + '/a>')
							.attr('href', node.properties.url.split('jsonview').join('contentview')+'?open&testnav')
							.text(node.properties.subject)
							.addClass('level' + node.level)
							.bind("click", data, _click)
							.bind("mouseleave", data, _mouseleave);
						if (node === state.hover || node === level.hover) { 
							a.addClass('hover');
						}
						if (node !== state.hoverDirect) { 
							a.bind("mouseenter", data, _mouseenter);
						}
						if (state.selected[node.properties.unid]) { a.addClass('selected');}
						if (state.path[node.properties.unid]) { a.addClass('path');}
						level.jLevelNext.append(a);
					}					
				}

				var isEmpty = !level.nodes;
				var wasEmpty = (level.jLevelLast.length === 0);
				if (isEmpty) {
					if (!wasEmpty) {
						//level.jLevelLast.stop(true, true).slideUp().queue(function(next) { $(this).remove(); next();});
						level.jLevelLast.remove();
					}
					level.jLevelNext.remove();
				} else {
					if (!wasEmpty) {
						//level.jLevelLast.stop(true, true).addClass('fading-free').fadeOut(200, 'linear').queue(function(next) { $(this).remove(); next();});
						//level.jLevelNext.fadeIn(200, 'linear');
						level.jLevelLast.remove();
					} else {
						//level.jLevelNext.slideDown();
					}
				}
			});
		};

		var _withContent = function(node) {
			if (node && node.properties.type === 0) {
				node = _withContent(node.pages && node.pages[0]);
			};
			return node;
		};

		var constructor = function(roots, selectors) {
			this.roots = roots;
			this.selectors = selectors;
		};

		constructor.prototype.displayFor = function(activeUnid) {
			_enrich(this);
			var state = _createState(_withContent(this.findByUNID(activeUnid)));
			var data = {navigation:this, state: state};
			$($.merge([this.selectors.breadcrumb], this.selectors.levels).join(', '))
				.addClass('anchor')
				//.bind('mouseenter', data, _mouseenter)
				.bind('mouseleave', data, _mouseleave);
			_display(this, state);
		};

		constructor.prototype.findByUNID = function(unid) {
			return _findByUNID(unid, this.roots);
		};

		return constructor;
	})();

	$(function() {
		if(location.href.indexOf('testnav') == -1) return;
		$('#naviFirst').hide();
		$('#naviTest').show();

		$.ajax({
			url: homepath + 'systemcontentview/x_jsonmap.txt',
			dataType: 'json',
			dataFilter: function(data, type) {
				return data.replace(/\/\*.+?\*\/\s+/g, '').replace(/\n|\r/g, '');
			},
			success: function(data, status, xhr) {
				var navigation = new Navigation(
					data, 
					{
						breadcrumb: '#navBreadcrumb',
						levels: ['#navLevel0', '#navLevel1', '#navLevel2', '#navLevel3', '#navLevel4']
					}
				);
		
				navigation.displayFor(pageunid );
				//navigation.displayFor();

				$.each(navigation.roots[0].pages, function(index, node) {
					var data = {navigation: navigation, node: node};
					var a = $('<a><' + '/a>')
						.attr('href', node.properties.url)
						.text(node.properties.subject)
						.addClass('level' + node.level)
						.bind("click", data, function(event) {
							var data = event.data;
							if (data.node) {
								event.preventDefault();
								data.navigation.displayFor(data.node.properties.unid);
							}
						});
					$('#content').append($('<p><' + '/p>').append(a));
				});
			}
		});
	});
})(jQuery);
