
var StateManager = Class.create();

StateManager.prototype = {

	initialize: function(listener, options) {
		this.options = Object.extend({
			iframesrc: 'blank.html'
		}, options || {});
		
		this.listener = listener;
		this.monitor = this.getMonitor();
	},
	
	startObserving: function() {
		this.monitor.start();
	},
	
	stateChanged: function(location) {
		this.listener(location);
	},
	
	setState: function(hash) {
		this.monitor.setLocation(hash);
	},
	
	getMonitor: function() {
		var detect = navigator.userAgent.toLowerCase();
		
		if (detect.indexOf('msie') > -1) {
			return new IFrameMonitor(this.stateChanged.bind(this), this.options.iframesrc);
		}
		if (detect.indexOf("safari") > -1) {
			return new DummyMonitor();
		}

		return new URLMonitor(this.stateChanged.bind(this));
	}
};

var URLMonitor = Class.create();

URLMonitor.prototype = {
	
	initialize: function(listener) {
		this.listener = listener;
		this.currentHash = '';
	},
	
	start: function() {
		new PeriodicalExecuter(this.checkWhetherChanged.bind(this), 200);
	},
	
	checkWhetherChanged: function() {
		var location = window.location.hash;
		
		if(this.currentHash != location) {
			this.listener(location);
			this.currentHash = location;
		}
	},
	
	setLocation: function(location) {
		window.location.hash = location;
	}
};

var IFrameMonitor = Class.create();

IFrameMonitor.prototype = {

	initialize: function(listener, iframesrc) {
		this.iframeid = 'monitorframe';
		this.listener = listener;
		this.iframesrc = iframesrc;
		
		this.locator = new PageLocator("document.frames['"+this.iframeid+"'].location.href", "?hash=");
		this.windowlocator = new PageLocator("window.location.href", "#");
	},
	
	start: function() {
		setTimeout(this.checkBookmark.bind(this), 100);
	},
	
	checkBookmark: function() {
		window.location.hash = this.locator.getHash();
		this.checkWhetherChanged(0);
	},
	
	checkWhetherChanged: function(location) {
		// get hash from iframe
		var currenthash = this.locator.getHash();
		
		if(currenthash != location) {
			// update main window hash, then notify listener
			window.location.hash = currenthash;
			this.listener(currenthash);
		}
		
		setTimeout(this.checkWhetherChanged.bind(this, currenthash), 200);
	},
	
	setLocation: function(location) {
		$(this.iframeid).setAttribute('src', this.locator.makeNewLocation(location));
	}
};

var DummyMonitor = Class.create();

DummyMonitor.prototype = {

	initialize: function(listener) {
		// do nothing
	},
	
	start: function() {
		// do nothing
	},
	
	setLocation: function(location) {
		// do nothing
	}
};


var PageLocator = Class.create();

PageLocator.prototype = {
	
	initialize: function(propertyToUse, dividingChar, defaultVal) {
		this.propertyToUse = propertyToUse || 'window.location.href';
		this.dividingChar = dividingChar || '#';
		this.defaultVal = defaultVal || '';
	},
	
	getLocation: function() {
		return eval(this.propertyToUse);
	},
	
	getHash: function() {
		var url = this.getLocation();
		
		if (url.indexOf(this.dividingChar) > -1) {
			return $A(url.split(this.dividingChar)).last();
		}
		
		return this.defaultVal;
	},

	getHref: function() {	
		return this.getLocation().split(this.dividingChar)[0];
	},

	makeNewLocation: function(newVal) {
		return this.getHref() + this.dividingChar + newVal;
	}
};

