'use strict';

var
	_ = require('underscore'),
	ko = require('knockout'),
	hasher = require('hasher'),
	
	UrlUtils = require('%PathToCoreWebclientModule%/js/utils/Url.js'),
	Screens = require('%PathToCoreWebclientModule%/js/Screens.js')
;

/**
 * @constructor
 */
function CRouting()
{
	this.currentHash = ko.observable(this.getHashFromHref());
	this.previousHash = ko.observable(this.getHashFromHref());
}

/**
 * Initializes object.
 */
CRouting.prototype.init = function ()
{
	hasher.initialized.removeAll();
	hasher.changed.removeAll();
	hasher.initialized.add(this.parseRouting, this);
	hasher.changed.add(this.parseRouting, this);
	hasher.init();
	hasher.initialized.removeAll();
};

/**
 * Finalizes the object and puts an empty hash.
 */
CRouting.prototype.finalize = function ()
{
	hasher.dispose();
};

/**
 * Sets a new hash.
 * 
 * @param {string} sNewHash
 * 
 * @return {boolean}
 */
CRouting.prototype.setHashFromString = function (sNewHash)
{
	var bSame = (location.hash === sNewHash);
	
	if (!bSame)
	{
		location.hash = sNewHash;
	}
	
	return bSame;
};

/**
 * Sets a new hash without part.
 * 
 * @param {string} sUid
 */
CRouting.prototype.replaceHashWithoutMessageUid = function (sUid)
{
	if (typeof sUid === 'string' && sUid !== '')
	{
		var sNewHash = location.hash.replace('/msg' + encodeURIComponent(sUid), '');
		this.replaceHashFromString(sNewHash);
	}
};

/**
 * Sets a new hash.
 * 
 * @param {string} sNewHash
 */
CRouting.prototype.replaceHashFromString = function (sNewHash)
{
	if (location.hash !== sNewHash)
	{
		location.replace(UrlUtils.getAppPath() + window.location.search + sNewHash);
	}
};

/**
 * Sets a new hash made ​​up of an array.
 * 
 * @param {Array} aRoutingParts
 * 
 * @return boolean
 */
CRouting.prototype.setHash = function (aRoutingParts)
{
	return this.setHashFromString(this.buildHashFromArray(aRoutingParts));
};

/**
 * @param {Array} aRoutingParts
 */
CRouting.prototype.replaceHash = function (aRoutingParts)
{
	this.replaceHashFromString(this.buildHashFromArray(aRoutingParts));
};

/**
 * @param {Array} aRoutingParts
 */
CRouting.prototype.replaceHashDirectly = function (aRoutingParts)
{
	hasher.stop();
	this.replaceHashFromString(this.buildHashFromArray(aRoutingParts));
	this.currentHash(location.hash.replace(/^#/, ''));
	hasher.init();
};

CRouting.prototype.setPreviousHash = function ()
{
	var
		sPrevHash = this.previousHash(),
		aPrevHash = sPrevHash.split(/[-|\/]/)
	;
	if (this.currentHash() === sPrevHash)
	{
		sPrevHash = aPrevHash.length > 0 && aPrevHash[0] !== sPrevHash ? aPrevHash[0] : '';
	}
	location.hash = sPrevHash;
};

CRouting.prototype.stopListening = function ()
{
	hasher.stop();
};

CRouting.prototype.startListening = function ()
{
	hasher.init();
};

/**
 * Makes a hash of a string array.
 *
 * @param {(string|Array)} aRoutingParts
 * 
 * @return {string}
 */
CRouting.prototype.buildHashFromArray = function (aRoutingParts)
{
	var
		iIndex = 0,
		iLen = 0,
		sHash = ''
	;

	if (_.isArray(aRoutingParts))
	{
		for (iLen = aRoutingParts.length; iIndex < iLen; iIndex++)
		{
			aRoutingParts[iIndex] = encodeURIComponent(aRoutingParts[iIndex]);
		}
	}
	else
	{
		aRoutingParts = [encodeURIComponent(aRoutingParts.toString())];
	}
	
	sHash = aRoutingParts.join('/');
	
	if (sHash !== '')
	{
		sHash = '#' + sHash;
	}

	return sHash;
};

/**
 * Returns the value of the hash string of location.href.
 * location.hash returns the decoded string and location.href - not, so it uses location.href.
 * 
 * @return {string}
 */
CRouting.prototype.getHashFromHref = function ()
{
	var
		iPos = location.href.indexOf('#'),
		sHash = ''
	;

	if (iPos !== -1)
	{
		sHash = location.href.substr(iPos + 1);
	}

	return sHash;
};

/**
 * @param {Array} aRoutingParts
 * @param {Array} aAddParams
 */
CRouting.prototype.goDirectly = function (aRoutingParts, aAddParams)
{
	hasher.stop();
	this.setHash(aRoutingParts);
	this.parseRouting(aAddParams);
	hasher.init();
};

/**
 * @param {string} sNeedScreen
 */
CRouting.prototype.historyBackWithoutParsing = function (sNeedScreen)
{
	hasher.stop();
	location.hash = this.currentHash();
	hasher.init();
};

/**
 * @returns {String}
 */
CRouting.prototype.getScreenFromHash = function ()
{
	var
		sHash = this.getHashFromHref(),
		aHash = sHash.split('/')
	;
	return decodeURIComponent(aHash.shift());
};

/**
 * Checks if there are changes in current screen and continues screen change or discards changes.
 * @param {Array} aAddParams
 */
CRouting.prototype.parseRouting = function (aAddParams)
{
	var
		App = require('%PathToCoreWebclientModule%/js/App.js'),
		fContinueScreenChange = _.bind(this.chooseScreen, this, aAddParams),
		fRevertScreenChange = _.bind(this.historyBackWithoutParsing, this, aAddParams),
		oCurrentScreen = _.isFunction(Screens.getCurrentScreen) ? Screens.getCurrentScreen() : null
	;
	
	if (oCurrentScreen && _.isFunction(oCurrentScreen.hasUnsavedChanges) && oCurrentScreen.hasUnsavedChanges())
	{
		App.askDiscardChanges(fContinueScreenChange, fRevertScreenChange, oCurrentScreen);
	}
	else if (_.isFunction(fContinueScreenChange))
	{
		fContinueScreenChange();
	}
};

/**
 * Parses the hash string and opens the corresponding routing screen.
 * 
 * @param {Array} aAddParams
 */
CRouting.prototype.chooseScreen = function (aAddParams)
{
	var
		sHash = this.getHashFromHref(),
		aParams = _.map(sHash.split('/'), function (sHashPart) {
			return decodeURIComponent(sHashPart);
		})
	;
	
	this.previousHash(this.currentHash());
	this.currentHash(sHash);
	
	aAddParams = _.isArray(aAddParams) ? aAddParams : [];

	Screens.route(aParams.concat(aAddParams));
};

var Routing = new CRouting();

module.exports = {
	init: _.bind(Routing.init, Routing),
	buildHashFromArray: _.bind(Routing.buildHashFromArray, Routing),
	replaceHashWithoutMessageUid: _.bind(Routing.replaceHashWithoutMessageUid, Routing),
	setHash: _.bind(Routing.setHash, Routing),
	replaceHash: _.bind(Routing.replaceHash, Routing),
	finalize: _.bind(Routing.finalize, Routing),
	currentHash: Routing.currentHash,
	replaceHashDirectly: _.bind(Routing.replaceHashDirectly, Routing),
	setPreviousHash: _.bind(Routing.setPreviousHash, Routing),
	clearPreviousHash: function () {
		Routing.previousHash('');
	},
	stopListening: _.bind(Routing.stopListening, Routing),
	startListening: _.bind(Routing.startListening, Routing),
	goDirectly: _.bind(Routing.goDirectly, Routing),
	getCurrentHashArray: function () {
		return Routing.currentHash().split('/');
	},
	getAppUrlWithHash: function (aRoutingParts) {
		return UrlUtils.getAppPath() + Routing.buildHashFromArray(aRoutingParts);
	}
};
