/**
 * MooBack
 * 
 * adapted from the HistoryManager from Harald Kirschner <mail [at] digitarald.de>
 */
var MooBack = {

	/**
	 * Default options - Can be overridden with setOptions
	 * 
	 * observeDelay: Duration for checking the state, default 100ms
	 * iframeSrc: Scr for IE6/7 iframe, must exist on server!
	 * onStart: Fires on start
	 * onStateChange: ... module changes
	 * onObserverChange: ... history change
	 */
	options: {
		observeDelay: 100,
		iframeSrc: 'blank.html',
		onStart: Class.empty,
		onStateChange: Class.empty,
		onObserverChange: Class.empty
	},

	/**
	 * Constructur - Class.initialize
	 * 
	 * Options:
	 *  - observeDelay: duration in ms, default 100 - BackBuddy observe the hash for changes periodical
	 *  - iframeSrc: string, default 'blank.html' - File for the iframe (IE6/7), must exist on the server!
	 *  - Events: onStart, onStateChange, onObserverChange
	 * 
	 * @return	this
	 * 
	 * @param	{Object} options
	 */
	initialize: function(options) {
		this.setOptions(options);
		this.count = history.length;
		this.states = [];
		this.states[this.count] = this.getHash();
		this.state = null;
		this.start();
		return this;
	},

	/**
	 * Start - Check hash and start observer
	 * 
	 * Call start after registering ALL modules. This start the observer,
	 * reads the state from the hash and calls onMatch for effected modules.
	 * 
	 * @return	this
	 * 
	 */
	start: function() {
		this.observe.periodical(this.options.observeDelay, this);
		this.started = true;
		this.observe();
		this.fireEvent('onStart', [this.state]);
		return this;
	},

	observe: function() {
		if (this.timeout) return;
		var state = this.getState();
		if (this.state == state) return;
		if ((window.ie || window.webkit419) && (this.state !== null)) this.setState(state, true);
		else this.state = state;
		this.fireEvent('onStateChange', [state]).fireEvent('onObserverChange', [state]);
	},
	
	observeTimeout: function() {
		if (this.timeout) this.timeout = $clear(this.timeout);
		else this.timeout = this.observeTimeout.delay(200, this);
	},

	getHash: function() {
		var href = top.location.href;
		var pos = href.indexOf('#') + 1;
		return (pos) ? href.substr(pos) : '';
	},

	parseHash: function(state) {
		var temp, output = {};
		var states = state.split('&');
		for (var i = 0; i < states.length; i++) {
			temp = states[i].split('=');
			output[temp[0]] = temp[1];
		}
		return output;
	},

	generateState: function(pageState) {
		if (!pageState) return;
		var state = [];
		for (var i in pageState) {
			state.push(i + '=' + pageState[i]);
		}
		state = state.join('&');
		this.setState(state);
	},
	
	getState: function() {
		var state = this.getHash();
		if (this.iframe) {
			var doc = this.iframe.contentWindow.document;
			if (doc && doc.body.id == 'state') {
				var istate = doc.body.innerText;
				if (this.state == state) return istate;
				this.istateOld = true;
			} else return this.istate;
		}
		if (window.webkit419 && history.length != this.count) {
			this.count = history.length;
			return $pick(this.states[this.count - 1], state);
		}
		return state;
	},

	setState: function(state, fix) {
		state = $pick(state, '');
		if (window.webkit419) {
			if (!this.form) this.form = new Element('form', {method: 'get'}).injectInside(document.body);
			this.count = history.length;
			this.states[this.count] = state;
			this.observeTimeout();
			this.form.setProperty('action', '#' + state).submit();
		} else top.location.hash = state || '#';
		if (window.ie && (!fix || this.istateOld)) {
			if (!this.iframe) {
				this.iframe = new Element('iframe', {
					src: this.options.iframeSrc,
					styles: 'visibility: hidden;'
				}).injectInside(document.body);
				this.istate = this.state;
			}
			try {
				var doc = this.iframe.contentWindow.document;
				doc.open();
				doc.write('<html><body id="state">' + state + '</body></html>');
				doc.close();
				this.istateOld = false;
			} catch(e) {};
		}
		this.state = state;
	},

	extend: $extend
};

MooBack.extend(Events.prototype);
MooBack.extend(Options.prototype);
