var VnDate = require('./date');
var Lot = require('./lot');

/**
 * Class to handle the hash part of the URL as a key-value javascript object.
 * It also handles dates and objects as a value.
 */
module.exports = new Class({
	Extends: Lot
	,Properties: {
		/**
		 * The main window object.
		 */
		window:
		{
			type: Window
			,set(x) {
				this._window = x;
				x.addEventListener('hashchange', this._hashChangedHandler);
				this._hashChanged();
			}
			,get() {
				return this._window;
			}
		}
	}

	,initialize(props) {
		Object.assign(this, {
			 _hash: null
			,_hashLock: false
			,_window: null
			,_hashChangedHandler: this._hashChanged.bind(this)
		});
		Lot.prototype.initialize.call(this, props);
	}

	,_paramsChanged() {
		this.updateHash();
	}

	/**
	 * Creates a URL with the given hash data.
	 *
	 * @param {Object} params A key-value object
	 * @param {boolean} add %true to combine with the current params, %false otherwise
	 * @return {string} The URL
	 */
	,make(params, add) {
		if (add) {
			params = Object.assign({}, params);

			for (var key in this._params)
			if (!params[key])
				params[key] = this._params[key];
		}

		return this.renderHash(params);
	}

	/**
	 * Updates the window hash with current params.
	 */
	,updateHash() {
		if (this._hashLock)
			return;

		this._hash = this.renderHash(this._params);

		this._hashLock = true;
		location.hash = this._hash;
		this._hashLock = false;
	}

	/*
	 * Called when window hash changes.
	 */
	,_hashChanged() {
		var newHash = location.hash;
	
		if (this._hashLock || this._hash === newHash)
			return;

		this._hash = newHash;

		this._hashLock = true;
		this.params = this.parseHash(newHash);
		this._hashLock = false;
	}

	/**
	 * Creates a URL with the given hash data.
	 *
	 * @param {Object} params The key-value object
	 * @return {string} The URL
	 */
	,renderHash(params) {
		var hash = '#!';

		for (var key in params)
		if (params[key] !== undefined) {
			if (hash.length > 2)
				hash += '&';

			hash += encodeURIComponent(key) +'='+ this.renderValue(params[key]);
		}
	
		return hash;
	}

	/**
	 * Parses a hash string to a key-value object.
	 *
	 * @param {string} hashString The hash string
	 * @return {Object} The key-value object
	 */
	,parseHash(hashString) {
		var newMap = hashMap = {};
		var kvPairs = hashString.substr(2).split('&');

		for (var i = 0; i < kvPairs.length; i++) {
			var kvPair = kvPairs[i].split('=', 2);
			
			if (kvPair[0])
				newMap[decodeURIComponent(kvPair[0])] = this.parseValue(kvPair[1]);
		}

		return newMap;
	}

	,renderValue(v) {
		if (v == null)
			return '';

		switch (typeof v) {
			case 'object':
			if (v instanceof Date)
				return VnDate.strftime(v, '%Y-%m-%d');
			else
				return JSON.stringify(v)
		}

		return v;
	}

	,parseValue(v) {
		if (v == null)
			return v;

		v = decodeURIComponent(v);

		if (v === '')
			return null;

		return v;
	}

	,_destroy() {
		this._window.removeEventListener('hashchange', this._hashChangedHandler);
		this._window = null;
		Lot.prototype._destroy.call(this);
	}
});