export default class Spot {

	constructor(asGlobals) {
		this.consts = asGlobals.consts;
		this.consts.hash_sep = '-';
		this.consts.title = 'Spotty';
		this.consts.default_page = 'project';
		this.consts.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || this.consts.default_timezone;

		this.pages = {};

		//Variables & constants from php
		this.vars('tmp', 'object');
		this.vars('page', 'string');
		$.each(asGlobals.vars, (sKey, oValue) => {this.vars(sKey, oValue)});

		//page elem
		this.elem = {};
	}

	/* Initialization */

	init() {
		this.elem.container = $('#container');
		this.elem.main = $('#main');

		//On Key down
		$('html').on('keydown', (oEvent) => {this.onKeydown(oEvent);});

		//on window resize
		$(window).on('resize', () => {this.onResize();});

		//Hash management
		$(window)
			.on('hashchange', () => {this.onHashChange();})
			.trigger('hashchange');
	}

	/* Variable Management */

	vars(oVarName, oValue) {
		var asVarName = (typeof oVarName == 'object')?oVarName:[oVarName];

		//Set, name & type / default value (init)
		if(typeof oValue !== 'undefined') setElem(this.vars, copyArray(asVarName), oValue);

		//Get, only name parameter
		return getElem(this.vars, asVarName);
	}

	tmp(sVarName, oValue) {
		var asVarName = (typeof sVarName == 'object')?sVarName:[sVarName];
		asVarName.unshift('tmp');
		return this.vars(asVarName, oValue);
	}

	/* Interface with server */

	get(sAction, fOnSuccess, oVars, fOnError, fonProgress) {
		oVars = oVars || {};
		fOnError = fOnError || function(sError) {console.log(sError);};
		fonProgress = fonProgress || function(sState){};
		fonProgress('start');

		oVars['a'] = sAction;
		oVars['t'] = this.consts.timezone;
		return $.ajax(
			{
				url: this.consts.process_page,
				data: oVars,
				dataType: 'json'
			})
			.done((oData) => {
				fonProgress('done');
				if(oData.desc.substr(0, this.consts.lang_prefix.length)==this.consts.lang_prefix) oData.desc = this.lang(oData.desc.substr(5));

				if(oData.result==this.consts.error) fOnError(oData.desc);
				else if(fOnSuccess) fOnSuccess(oData.data, oData.desc);
			})
			.fail((jqXHR, textStatus, errorThrown) => {
				fonProgress('fail');
				fOnError(textStatus+' '+errorThrown);
			});
	}

	async get2(sAction, oVars, bLoading) {
		oVars = oVars || {};
		oVars['a'] = sAction;
		oVars['t'] = this.consts.timezone;
		bLoading = true;

		let oUrl = new URL(this.consts.server+this.consts.process_page);
		oUrl.search = new URLSearchParams(oVars).toString();

		try {
			let oUrl = new URL(this.consts.server+this.consts.process_page);
			oUrl.search = new URLSearchParams(oVars).toString();
			const oRequest = await fetch(oUrl, {method: 'GET', /*body: JSON.stringify(oVars),*/ headers: {"Content-Type": "application/json"}});
			if(!oRequest.ok) {
				bLoading = false;
				throw new Error('Error HTTP '+oRequest.status+': '+oRequest.statusText);
			}
			else {
				let oResponse = await oRequest.json();
				bLoading = false;
				if(oResponse.desc.substr(0, this.consts.lang_prefix.length)==this.consts.lang_prefix) oResponse.desc = this.lang(oData.desc.substr(this.consts.lang_prefix.length));

				if(oResponse.result == this.consts.error) return Promise.reject(oResponse.desc);
				else return Promise.resolve(oResponse.data, oResponse.desc);
			}
		}
		catch(oError) {
			bLoading = false;
			throw oError;
		}
	}

	lang(sKey, asParams = []) {
		if(sKey == '') return '';
		if(typeof asParams !== 'object') asParams = [asParams];

		var sLang = '';
		if(sKey in this.consts.lang) {
			sLang = this.consts.lang[sKey];
			for(let i in asParams) {
				sLang = sLang.replace('$'+i, asParams[i]);
			}
		}
		else {
			console.log('missing translation: '+sKey);
			sLang = sKey;
		}

		return sLang;
	}

	isMobile() {
		return $('#mobile').is(':visible');
	}

	/* Page Switch - Trigger & Event catching */

	onHashChange() {
		var asHash = this.getHash();
		if(asHash.hash !='' && asHash.page != '') this.switchPage(asHash); //page switching
		else if(this.vars('page')=='') this.setHash(this.consts.default_page); //first page
	}

	getHash() {
		var sHash = this.hash();
		var asHash = sHash.split(this.consts.hash_sep);
		var sPage = asHash.shift() || '';
		return {hash:sHash, page:sPage, items:asHash};
	}

	setHash(sPage, asItems, bReboot) {
		bReboot = bReboot || false;
		sPage = sPage || '';
		asItems = asItems || [];
		if(typeof asItems == 'string') asItems = [asItems];

		if(sPage != '') {
			var sItems = (asItems.length > 0)?this.consts.hash_sep+asItems.join(this.consts.hash_sep):'';
			this.hash(sPage+sItems, bReboot);
		}
	}

	hash(hash, bReboot) {
		bReboot = bReboot || false;
		if(!hash) return window.location.hash.slice(1);
		else window.location.hash = '#'+hash;

		if(bReboot) location.reload();
	}

	updateHash(sType, iId) {
		sType = sType || '';
		iId = iId || 0;
	
		var asHash = this.getHash();
		if(iId) this.setHash(asHash.page, [asHash.items[0], sType, iId]);
	}
	
	flushHash(asTypes) {
		asTypes = asTypes || [];
		var asHash = this.getHash();
		if(asHash.items.length > 1 && (asTypes.length == 0 || asTypes.indexOf(asHash.items[1]) != -1)) this.setHash(asHash.page, [asHash.items[0]]);
	}

	/* Page Events */

	pageInit(asHash) {
		let sPage = this.vars('page');
		if(this.pages[sPage].pageInit) this.pages[sPage].pageInit(asHash);
		else console.log('no init for the page: '+asHash.page);
	}

	onSamePageMove(asHash) {
		let sPage = this.vars('page');
		return (this.pages[sPage] && this.pages[sPage].onSamePageMove)?this.pages[sPage].onSamePageMove(asHash):false;
	}

	onQuitPage() {
		let sPage = this.vars('page');
		return (this.pages[sPage] && this.pages[sPage].onQuitPage)?this.pages[sPage].onQuitPage():true;
	}

	onResize() {
		let sPage = this.vars('page');
		if(this.pages[sPage].onResize) this.pages[sPage].onResize();
	}

	onFeedback(sType, sMsg, asContext) {
		asContext = asContext || {};
		let sPage = this.vars('page');
		if(this.pages[sPage].onFeedback) this.pages[sPage].onFeedback(sType, sMsg, asContext);
		else console.log({type:sType, msg:sMsg, context:asContext});
	}
	
	onKeydown(oEvent) {
		let sPage = this.vars('page');
		if(this.pages[sPage].onKeydown) this.pages[sPage].onKeydown(oEvent);
	}

	/* Page Switch - DOM Replacement */

	getActionLink(sAction, oVars) {
		if(!oVars) oVars = {};
		let sVars = '';
		
		for(i in oVars) sVars += '&'+i+'='+oVars[i];

		return this.consts.process_page+'?a='+sAction+sVars;
	}

	addPage(sPage, oPage) {
		this.pages[sPage] = oPage;
	}

	switchPage(asHash) {
		var sPageName = asHash.page;
		var bSamePage = (this.vars('page') == sPageName);
		var bFirstPage = (this.vars('page') == '');

		if(!this.consts.pages[sPageName]) { //Page does not exist
			if(bFirstPage)	this.setHash(this.consts.default_page);
			else 			this.setHash(this.vars('page'), this.vars(['hash', 'items']));
		}
		else if(this.onQuitPage(bSamePage) && !bSamePage || this.onSamePageMove(asHash))
		{
			//Delete tmp variables
			this.vars('tmp', {});

			//Officially a new page
			this.vars('page', sPageName);
			this.vars('hash', asHash);
			
			//Update Page Title
			this.setPageTitle(sPageName+' '+(asHash.items[0] || ''));

			//Replacing DOM
			var $Dom = $(this.consts.pages[sPageName]);
			if(bFirstPage) this.splash($Dom, asHash, bFirstPage); //first page
			else this.elem.main.stop().fadeTo('fast', 0, () => {this.splash($Dom, asHash, bFirstPage);}); //Switching page
		}
		else if(bSamePage) this.vars('hash', asHash);
	}

	splash($Dom, asHash, bFirstPage)
	{
		//Switch main content
		this.elem.main.empty().html($Dom);

		//Page Bootstrap
		this.pageInit(asHash, bFirstPage);

		//Show main
		var $FadeInElem = bFirstPage?this.elem.container:this.elem.main;
		$FadeInElem.hide().fadeTo('slow', 1);
	}

	setPageTitle(sTitle) {
		document.title = this.consts.title+' - '+sTitle;
	}

	getNaturalDuration(iHours) {
		var iTimeMinutes = 0, iTimeHours = 0, iTimeDays = Math.floor(iHours/8); //8 hours a day
		if(iTimeDays > 1) iTimeDays = Math.round(iTimeDays * 2) / 2; //Round down to the closest half day
		else {
			iTimeDays = 0;
			iTimeHours = Math.floor(iHours);
			iHours -= iTimeHours;
	
			iTimeMinutes = Math.floor(iHours * 4) * 15; //Round down to the closest 15 minutes
		}
		return '~ '
			+(iTimeDays>0?(iTimeDays+(iTimeDays%2==0?'':'½')+' '+this.lang(iTimeDays>1?'unit_days':'unit_day')):'')	//Days
			+((iTimeHours>0 || iTimeDays==0)?iTimeHours+this.lang('unit_hour'):'')									//Hours
			+((iTimeDays>0 || iTimeMinutes==0)?'':iTimeMinutes)														//Minutes
	
	}

	checkClearance(sClearance) {
		return (this.vars(['user', 'clearance']) >= sClearance);
	}
}