
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: function (x)
			{
				this._window = x;
				x.addEventListener ('hashchange', this._hashChangedHandler);
				this._hashChanged ();
			}
			,get: function ()
			{
				return this._window;
			}
		}
	}

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

	,_paramsChanged: function ()
	{
		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: function (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: function ()
	{
		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: function ()
	{
		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: function (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: function (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: function (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: function (v)
	{
		if (v == null)
			return v;

		v = decodeURIComponent (v);

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

		return v;
	}

	,_destroy: function ()
	{
		this._window.removeEventListener ('hashchange', this._hashChangedHandler);
		this._window = null;
		this.parent ();
	}
});
