if (typeof window.debug == 'undefined') {
	window.debug = function(){};
}

var PageLoader = new function() {
	
	//Enabled unless cookie says no
	var enabled = !document.cookie.match(/nopageloader/i);
	
	//Disable page load for entire session
	if (document.location.hash.match(/(gaso|nopageloader)/i) !== null ||
	    document.location.href.match(/XDEBUG_SESSION_START/) !== null) {
		enabled = false;
		document.cookie = 'nopageloader = 1';
	}

	var container = null;
	var listeners = [];
	var rsh = null;
	
	var currentUser = null;
	var baseURL = document.location.protocol + '//' + document.location.host;
	var proxyBaseURL = baseURL + '/content';
	var request = null;
	
	var scripts = [];
	
	//Show over a loading page.
	var currentLoading = false;
	var blocker = $('<div id="blocker"></div>').css('opacity', '0.66');
	
	//Show page is loading
	var pageLoading = function(loading) {
		if (loading && !currentLoading) {
			debug("Loading...");
			blocker.
				css('width', (container.innerWidth() - 20) + 'px').
				css('height', (container.innerHeight() + 20) + 'px').
				css('top', container.offset().top + 'px').
				css('left', (container.offset().left + 10) + 'px').
				hide();
			container.after(blocker);
			
			blocker.fadeIn('slow');
			container.addClass('loading');
			currentLoading = true;
		} else if (!loading) {
			debug("Loading done.");
			currentLoading = false;
			blocker.remove();
			container.removeClass('loading');
		}
	};

	//Loaded page data.
	var pageSuccess = function(data) {
		debug('Got success');
		debug(data);
		if (data.redirect) {
			var url = data.redirect;
			debug("Redirecting to " + url);
			
			if (!get(url)) {
				document.location.href = url;
			}
		} else if (data.page) {		
			//Load new container, after our old one. This means the DOM loading is happening while
			//the old page is still visible, which makes everything smoother.
			var newContainer = $('<div id="body"></div>');
			newContainer.hide().html(data.page);
			container.after(newContainer);
			
			//Swap in new container
			container.remove();
			newContainer.show();
			container = newContainer;
			
			//Remove blocker and scroll to top (like when a new page really loads)
			pageLoading(false);			
			
			//Todo - check if there is a second hash in URL and scroll screen to target.
			$(document).scrollTop(0);
						
			//Update the title.
			document.title = data.title;
			
			//Show current tab.
			if (typeof data.tab != 'undefined') {
				$('#navigation .active').removeClass('active');
				$('#navigation #' + data.tab + '-tab').addClass('active');
			}
		
			//Append any CSS files.
			var i, j;
			for (i = 0; i < data.resources.css.length; i++) {
				if ($('link[href*=' + data.resources.css[i] + ']').length == 0) {
					$('head').append($('<link rel="stylesheet" type="text/css" href="" />').attr('href', data.resources.css[i]));
				}
			}

			//Append script tags
			//Todo - check that this is blocking, so that these scripts will be included before we run dispatch()
			for (i = 0; i < data.resources.js.length; i++) {
				//Check if the script already exists
				var found = false;
				for (j = 0; j < scripts.length; j++) {
					if (scripts[j] == data.resources.js[i]) {
						found = true;
						break;
					}
				}
				
				if (!found) {
					debug('Append script', data.resources.js[i]);
					$('head').append($('<script type="text/javascript" src=""></script>').attr('src', data.resources.js[i]));
					scripts.push(data.resources.js[i]);
				}
			}
			/*
			 * This causes a serious hang on ajax page load
			 
			for (i = 0; i < data.resources.player.length; i++) {
				if (window.player) {
					window.player.loadPlaylist(data.resources.player[i]['class'], data.resources.player[i]['href'], false);
				}
			}
			*/

			//Show user state
			if (data.user.id != currentUser)
			{
				currentUser = data.user.id;
				var userForm = $('<div id="user">' + data.user.navigation + '</div>');
				$('#user').replaceWith(userForm);
				dispatch(userForm);
			}

			//Run events
			dispatch(container);
		} else if (data.success == 0 && data.message) {
			pageLoading(false);
			showErrorBar(data.message);
		} else {
			debug('Fail');
			pageFail(request.request, 'invalid');
		}
	};
	
	//Show error bar at top of contetn
	var showErrorBar = function(messages)
	{
		if (typeof messages == 'string')
		{
			messages = [messages];
		}
		
		var bar = $("<div class=\"error\" id=\"page-loader-error\"></div>");
		for (var i = 0; i < messages.length; i++)
		{
			bar.append('<p>' + messages[i] + '</p>');
		}
		
		container.find('#page-loader-error').remove();
		container.prepend(bar);		
	};
	
	//Failed to load data.
	var pageFail = function(req, message, exception) {
		if (req == request.request) {
			debug("Failed to load", request.request, req, message, exception);
			pageLoading(false);
			
			var title = 'Unable to load page' + (message == 'error' && req.status != 200 ? ' - ' + req.status : '');

			//Transform the error message to something friendly?
			var errorMessages = {
				parsererror: "The server didn't send a readable reply.",
				empty: "The server didn't send a reply.",
				invalid: "The server didn't send the expected information.",
				timeout: "The server took too long to respond.",
				error: ""
			};

			if (message == 'parsererror' && req.response == '') {
				message = errorMessages.empty;
			} else if (typeof (errorMessages[message]) != 'undefined') {
				message = errorMessages[message];
			}
			message = "Sorry, it looks like we encountered an error loading the page. " + (message ? message : '');

			if (req.method == 'post') {
				showErrorBar([message, 'Please try again.'])
			} else {
				var page = $("<div id=\"content\"><h1>" + title + "</h1><p>" + message + "</p><p><a href=\"#\">Please try again.</a></p></div>");
				
				page.find('a').
					click(function(e) {
						get(request.url);
						e.stopImmediatePropagation();
						return false;
					}).
					attr('href', request.url);
				
				document.title = title;
				container.empty().html(page);
			}
		}
	};
	
	//Get querystring (including ?) from object dontaining data.
	var querystring = function(data) {
		if (typeof data == "object") {
			data = $.param(data);
		}
		
		if (typeof data == "string" && data.length > 0) {
			return '?' + data;
		} else {
			return "";
		}
	};
	
	//URL to request data from.
	var requestURL = function(url, data) {
		if (!url) {
			url = '';
		} else if (url.indexOf(baseURL) === 0) {
			url = url.substr(baseURL.length);
		} else if (url.charAt(0) != '/') {
			return null;
		}
		
		var hash = '';
		var hashIndex = url.indexOf('#');
		if (hashIndex !== -1) {
			hash = url.substr(hashIndex);
			url = url.substr(0, hashIndex);
		}
		
		return url + querystring(data) + hash;
	};

	//Simulate a GET request
	var get = function(url, data) {
		url = requestURL(url, data);
		
		if (rsh && url !== null) {
			debug("Get: push state for " + url);
			rsh.add(url);
			navigated(url);

			//Google Analytics tracking
			if (typeof pageTracker !== 'undefined') {
				pageTracker._trackPageview(url);
			}
			
			return true;
		} else {
			document.location.href = url;
			return false;
		}
	};
	
	//Simulate a POST request
	var post = function(url, data) {
		url = requestURL(url);
		if (url !== null && enabled) {
			debug("POST " + url);
			pageLoading(true);
			
			request = {
				request : $.ajax({
					type: 'post',
					url: proxyBaseURL + url,
					data: data, 
					success: pageSuccess,
					error: pageFail,
					dataType: 'json',
					timeout: 10000
				}),
				url : url,
				method : 'post'
			};
			return true;
		} else {
			return false;
		}
	};
			
	//Hijack links and forms to work via AJAX instead of 
	//by page load.
	var hijack = function(cont) {
		if (!enabled) {
			return;
		}
		
		debug("Hijacking " + cont);
		cont.find('a').click(function(e) {
			if (this.target == '' && this.href.indexOf(baseURL) === 0) {
				return !get(this.href);
			} else {
				return true;
			}
		});
		cont.find('form').submit(function() {
			if (this.action.indexOf(baseURL) === 0) {
				debug("Submitting form");
				if (this.method.toLowerCase() == 'get') {
					return !get(this.action, $(this).serialize());
				} else {
					return !post(this.action, $(this).serialize());
				}
			}
			return true;
		});
	};
	
	//The user has navigatied to a new history page.
	var navigated = function(url, data) {
		pageLoading(true);
		debug('navigated: GET ' + url);
		
		//Has a second hash (e.g. the real one).
		request = {
			request : $.ajax({
				type: 'get',
				url: proxyBaseURL + url,
				data: [], 
				success: pageSuccess,
				error: pageFail,
				dataType: 'json',
				timeout: 10000
			}),
			url : url,
			method : 'get'
		};
	};
	
	//Call all listeners on the container, and hijack links.
	//Call event.stopImmediatePropagation() in listener event to
	//stop the hijack event from firing.
	var dispatch = function(cont) {	
		debug('Dispatch', cont);
		
		for (var i = 0; i < listeners.length; i++) {
			listeners[i].call(cont);
		}
		
		//Call hijack last, so that the listeners can override our .click or .submit events.
		hijack(cont);		
	};
	
	//Register an event to be called whenever new content is loaded.
	//Run in context of container element.
	var register = function(listener) {	
		if (container !== null) {
			//Not necessary? Script loading should be blocking and we always
			//call dispatch() after loading scripts.
			listener.call(container);
		}
		listeners.push(listener);
	};		

	debug("Initialising page loader", enabled, document.location.pathname);
	
	//Check that the user is at the root path, so that we get nice URLs like /#/foo and not /bar#/foo	
	if (enabled && document.location.pathname != '/' && document.location.pathname != '/blank.html') {
		debug("Not at root path");
		document.location.href = baseURL + '#' + document.location.pathname;
		return;
	}

	$(function() {
		if (container == null) {
			debug('Initializing page loader');
			container = $('#body');
			dispatch($('html'));
			
			//Must quit after we've run the first dispatch pass.
			if (!enabled) {
				return;
			}

			//Get list of scripts that were loaded with page
			$('script').each(function(){
				scripts.push(this.src);
			});
		
			//We're just setting up, but the hash seems to suggest that the user is looking for something else.
			//That is, the user followed a link to http://thebeatles.com/#/something
			var loadPageOnInit = (document.location.hash.length > 2);
			
			if (loadPageOnInit) {
				container.html('');
			}
			
			//Must be run on window load (NOT document ready) for IE compatibility.
			$(window).bind('load', function(){	
				debug("Initialising RSH");
				window.dhtmlHistory.initialize();
				window.dhtmlHistory.addListener(navigated);
				rsh = window.dhtmlHistory;
				
				//The user started out with a URL like /#/somepage
				if (enabled && loadPageOnInit)
				{
					debug("Redirecting to requested content");
					get(rsh.getCurrentLocation());
				}
			});
		}
	});	

	//Must be run before document is loaded.
	window.dhtmlHistory.create({
		toJSON: function(o) {
        	return JSON.stringify(o);
		},
		fromJSON: function(s) {
            return JSON.parse(s);
		}
	});
	
	this.register = register;
	this.get = get;
	this.post = post;
}();
