/**
 * StateManager Description:
 * 
 * This object try to keep a meaningful history/location bar, to allow user
 * press Back and Refresh and respond ok.
 * 
 * This is difficult for IE due several bugs in history managment or other weird
 * behaviours:
 * 
 * a) If you set document.location.hash="ABC" (or change hash in any other way),
 * that new URL is saved in History ONLY if there is an element '<a name="ABC"></a>'
 * already in the DOM. Creating it dinamically semms not to work (weird). I have
 * read that having a generic anchor and changing it's name dinamically works.
 * Changing document.location.query always reloads the page and it is saved in
 * history. b) History not only contains 'main view' pages, also iframes's
 * pages. In other words, window.history is a mixture of main history and
 * iframes history. So if a iframe is reloaded to a new page and the user clicks
 * on 'Back' button the main window doesn't change but the iframe one does (the
 * 'back' action is handled by the iframe, no by the main document). c) Say that
 * we have several URLs in history that only differs on their hash. If the user
 * goes back and forth across hstory, 'document.location' DOESN'T reflect
 * current page, just the last one. d) If any iframe has 'document.domain' set
 * then ALL iframes must have the same 'document.domain' set. Although you set
 * the same 'document.domain' for every frame, access to 'iframe.document' is
 * DENIED.
 * 
 * (Thanks to IE developers for this 'funny' stuff...)
 * 
 * Now, some notes about how the system works and how overcome that bugs:
 * 
 * 1) We can't use the 'main' history to save states (bug c). So we created an
 * iframe and use it keep history working. Just think about it as we don't use
 * 'main' history but 'iframe' history to save states. 2) Application sates are
 * stored on url hash (like #m=home&func=index). So when the application want to
 * save some state, we add save the state-hash to iframe.location.hash. 3) Then,
 * to force History save the new iframe-url, we also change
 * iframe.location.query. That must be different each time to force a reload
 * (bug a), so basically we do iframe.location.query=iframe.location.hash. A
 * better solution is 'iframe.document.open(); iframe.document.close();' to
 * avoid an extra HTTP request, but this doesn't work on our case (bug d). 4)
 * There is a process running in background (each 50 ms). If the iframe URL is
 * different from 'main' URL, that process change 'main' hash to iframe hash
 * (basically we do document.location.hash=1 or 0). So when the user press
 * 'Refresh' button he gets the correct state. We switch between hash=1 or
 * hash=0 to allow the browser save the request in its cache. 5) When the user
 * press 'Back' button, iframe's url change (bug b) to previous one. The process
 * described in 5) also loads the application state saved on iframe's url. 6) As
 * the main page never reloads (the iframe is reloaded), the user doesn't see
 * the laoding bar again.
 * 
 * In short: States are stored (2+3), the new state in the locationbar (4), back
 * button works as expected (5) and there is no reload window (6). The bad deals
 * are that we need an extra iframe in our DOM (1) and we need to do a HTTP
 * request for each state to be saved (3).
 */

var StateManager = {
	hash : "",
	_current_url_hash : undefined,
	_poll_interval : undefined,

	_state_iframe_id : "statemanager",
	/*
	 * _state_iframe_url:
	 * (window.STATIC_PATHS[Math.floor(Math.random()*window.STATIC_PATHS.length)] ||
	 * window.STATIC_PATH) + "/request_engine/statemanager.html",
	 * _state_iframe_slots: 2, _state_iframe_slots_for_IE6: 3, _counter: 1,
	 */

	is_state_iframe_loaded : function() {
		var iframe = document.getElementById(StateManager._state_iframe_id);
		var is_loaded = false;

		if (iframe) {
			// Avoid access problems
			try {
				is_loaded = iframe.readyState == "complete";
			} catch (e) {
				is_loaded = false;
			}

			// Clean DOM references to avoid memory leaks
			delete iframe;
		}

		return is_loaded;
	},

	check : function() {
		if (Browser.ie) {
			if (StateManager.is_state_iframe_loaded()) {
				// Get HASH from iframe URL
				var iframe = document
						.getElementById(StateManager._state_iframe_id);
				var h = iframe.contentWindow.location["hash"];

				// Check undefined
				if (StateManager.equalHashes(h, "#undefined") || StateManager.equalHashes(h, "") ) {
					h = "#action=login";
				}
				
				// If hash is new, update locationbar
				if (!StateManager.equalHashes(h, window.location.hash)) {
					window.location.hash = h;
				}

				// If hash is new from previous hash, do the new reqest
				if (!StateManager.equalHashes(h, StateManager.hash)) {
					StateManager.hash = h;
					StateManager._current_url_hash = URLInterpreter
							.interpret(h);
					window.location.hash = h;
					StateManager.onHashChanged();
				}

				// Clean DOM references to avoid memory leaks
				delete iframe;
			}
		} else {
			var h = document.location["hash"];

			// Check undefined
			if (StateManager.equalHashes(h, "#undefined") || StateManager.equalHashes(h, "") ) {
				h = "#action=login";
			}
			
			// If hash is new from previous hash, do the new reqest
			if (!StateManager.equalHashes(h, StateManager.hash)) {
				StateManager.hash = h;
				StateManager._current_url_hash = URLInterpreter.interpret(h);
				StateManager.onHashChanged();
			}
		}

	},

	stop : function() {
		window.clearInterval(this._poll_interval);
	},

	get_current_url_hash : function() {
		if (typeof StateManager._current_url_hash === 'undefined') {
			StateManager.init();
		}
		return StateManager._current_url_hash;
	},

	hashCheckTimer : function() {
		return window.setInterval(StateManager.check, 50);
	},
	
	getHash : function() {
		return StateManager.hash.split("#")[1];
	},

	init : function() {
		if( window.location.hash == "" || window.location.hash == "#undefined" )
			window.location.hash = "#action=login";
		
		StateManager.hash = window.location.hash;
		StateManager._current_url_hash = URLInterpreter
				.interpret(window.location.hash);
		CallUrl(window.location.hash.split("#")[1], '');

		// poll for changes of the hash
		// Its curious that we dont need to check the hash all the time at ie4
		if (typeof StateManager._poll_interval != undefined) {
			window.clearInterval(StateManager._poll_interval);
		}

		if (Browser.ie) {
			// Creaates IFRAME to keep history working on IE browsers
			var iframe = document.createElement("iframe");
			iframe.src = StateManager._state_iframe_url + "?0"
					+ window.location.hash;
			iframe.id = StateManager._state_iframe_id;
			iframe.className = "hide";
			document.body.appendChild(iframe);
			// Clean DOM references to avoid memory leaks
			delete iframe;
		}

		StateManager._poll_interval = StateManager.hashCheckTimer();
	},
	setUrl : function(s) {
		/* StateManager.hash = "#" + s; */
		if (StateManager.equalHashes(s, "undefined") || StateManager.equalHashes(s, "") ) {
			s = "action=login";
		}
		
		if (StateManager.equalHashes("#" + s, StateManager.hash)) {
			StateManager.onHashChanged();
		}
		else
		{
			window.location.hash = "#" + s;
		}
	},

	equalHashes : function(a, b) {
		if (Browser.firefox) {
			// Firefox decides on its own to change the hash from:
			// "Pepe%20Perez" to "Pepe Perez" => Clean it!
			a = a.replace(/ /g, '%20');
			b = b.replace(/ /g, '%20');
		}
		return a.replace(/&anchor=[^&#\?]+/i, '') === b.replace(
				/&anchor=[^&#\?]+/i, '');
	},

	onHashChanged : function() {		
		CallUrl(StateManager.hash.split("#")[1], '');
	},

	// We need this for IE browsers
	cleanUrl4theIframe : function(stringUrl) {
		var amp_or_not = '';
		var pairs = stringUrl.split('&');
		stringUrl = '';
		if (pairs !== undefined) {
			for ( var i in pairs) {
				var val = pairs.split('=');
				if (val[0] !== 'ajax_target' && val[0] !== 'ajax'
						&& val[0] !== 'csfr' && val[0] !== 'store') {
					if (stringUrl === '') {
						amp_or_not = '';
					} else {
						amp_or_not = '&';
					}
					stringUrl += amp_or_not + val[0] + '=' + val[1];
				}
			}
		}
		return stringUrl;
	},

	store_in_history_if_needed : function(url) {
		var url_hash = URLInterpreter.interpret(url);
		var should_store_in_history = url_hash.should_store_in_history();
		if (should_store_in_history) {
			StateManager.store_hash_in_history(url_hash);
		}
		return should_store_in_history;
	},

	store_in_history : function(url) {
		var url_hash = URLInterpreter.interpret(url);
		StateManager.store_hash_in_history(url_hash);
	},

	store_hash_in_history : function(url_hash) {

		StateManager._current_url_hash = StateManager.get_current_url_hash()
				.integrate_with_hash(url_hash);
		StateManager.hash = StateManager._current_url_hash.string();

		if (Browser.ie) {

			var iframe = document.getElementById(StateManager._state_iframe_id);

			if (Browser.ie6) {
				iframe.contentWindow.location
						.assign(StateManager._state_iframe_url + '?'
								+ (StateManager._counter++)
								% StateManager._state_iframe_slots_for_IE6
								+ StateManager.hash);
			} else {
				iframe.contentWindow.location
						.assign(StateManager._state_iframe_url + '?'
								+ (StateManager._counter++)
								% StateManager._state_iframe_slots
								+ StateManager.hash);
			}

			// Clean DOM references to avoid memory leaks
			delete iframe;
		} else {
			window.location.hash = StateManager.hash;
		}

	},

	/**
	 * Sets a hash in the browser without reloading the page.
	 * 
	 * @param string
	 *            new_url_hash The new hash to set.
	 */
	set_hash_with_no_request : function(new_url_hash) {
		StateManager._current_url_hash = URLInterpreter.interpret('#');
		StateManager.store_in_history(new_url_hash);
	},

	/**
	 * Sets a hash with request
	 * 
	 * @param string
	 *            new_url_hash
	 * @return void
	 */
	go_to_hash : function(new_url_hash) {
		StateManager._current_url_hash = URLInterpreter.interpret('#');
		Request.IFRAME
				.send('?' + new_url_hash.split("#")[1] + '&ajax_target=canvas&ajax=1');
		StateManager.store_in_history(new_url_hash);
	}
};

var URLInterpreter = {

	interpret : function(hash_string) {
		// Creates the object
		var url_hash = {
			parameters_list : undefined,
			parameters_dictionary : undefined,

			/**
			 * INIT
			 */
			init : function(hash_string) {
				// Interprets the parameters
				this.parameters_list = [];
				this.parameters_dictionary = [];
				var hash_param_chunks = hash_string.replace(/^(\?|#)/g, '')
						.split('&');
				var i;
				for (i = 0; i < hash_param_chunks.length; i++) {
					var param_sides = hash_param_chunks[i].split('=');
					var key = param_sides[0];
					this.parameters_list.push(key);
					this.parameters_dictionary[key] = param_sides[1];
				}
			},

			/**
			 * return boolean True if the hash provided produces the same result
			 * as this url
			 */
			equals_hash_string : function(hash_string) {
				var equals = true;
				var i;
				var params = hash_string.match(/[?#&]([^=]+)=([^&]+)/g);
				if (params && params.length <= this.parameters_list.length) {
					for (i = 0; equals && i < params.length; i++) {
						var chunks = /[?#&]([^=]+)=([\w%_-]+)(?:&|$)/g
								.exec(params[i]);
						var key = chunks[1];
						if (!key.match(/^ajax|^store$|^func$/i)
								|| this.parameters_dictionary[key]
										.toLowerCase() != 'index') {
							equals = (this.parameters_dictionary[key] === chunks[2]);
						}
					}
				} else {
					equals = false;
				}
				return equals;
			},

			/**
			 * return boolean True if this hash should be stored in history
			 */
			should_store_in_history : function() {
				// Return true if the url explicitely requests it
				return parseInt(this.parameters_dictionary['store']) === 1;
			},

			/**
			 * Produces the new url to be shown in the url bar and to be stored
			 * in the browser's history
			 */
			integrate_with_hash : function(other_url_hash) {
				var resulting_hash;
				if (this.parameters_dictionary['m'] === other_url_hash.parameters_dictionary['m']
						&& other_url_hash.parameters_dictionary['ajax_target'] !== 'canvas') {
					// Integrates one with the other
					var i;
					for (i = 0; i < other_url_hash.parameters_list.length; i++) {
						var key = other_url_hash.parameters_list[i];
						if (key != 'm' && key != 'func') {
							if (typeof this.parameters_dictionary[key] === 'undefined') {
								this.parameters_list.push(key);
							}
							this.parameters_dictionary[key] = other_url_hash.parameters_dictionary[key];
						}
					}
					resulting_hash = this;
				} else {
					resulting_hash = other_url_hash;
				}

				return resulting_hash;
			},

			string : function() {
				var string = '#';
				var i;
				var key;
				if (0 < this.parameters_list.length) {
					key = this.parameters_list[0];
					string += key
							+ '='
							+ /* encodeURIComponent( */this.parameters_dictionary[key]/* ) */;
				}
				for (i = 1; i < this.parameters_list.length; i++) {
					key = this.parameters_list[i];
					if (!key.match(/^ajax|^store$/i)) {
						// We don't want to include in the list the ajax and
						// store entries
						string += '&'
								+ key
								+ '='
								+ /* encodeURIComponent( */this.parameters_dictionary[key]/* ) */;
					}
				}
				return string;
			}
		};

		// Initializes and returns the url_hash object
		url_hash.init(hash_string);
		return url_hash;
	}
};
