export class Screens {
	constructor(screensContainerSelector, loaderSelector, errorScreenSelector) {
		this.screens = {};
		this.routesMap = {};
		this.currentScreen = null;
		this.eventsHandlers = {
			switch: [],
			reload: [],
			reinit: [],
			errorScreen: [],
		};

		this.screensContainer = document.querySelector(screensContainerSelector);
		this.loader = document.querySelector(loaderSelector);
		this.errorScreen = document.querySelector(errorScreenSelector);
		console.log("Screens Init");
	}

	/**
	 * Добавление маршрута и объекта с параметрами экрана
	 * @param {string} route  Like `/path/name`
	 * @param {void} screen {alias, renderer, initer}
	 */
	add(route, screen) {
		if(typeof screen != "object") {
			return console.error("Screens: screens must be object");
		}
		
		if(typeof screen?.alias == "undefined") {
			return console.error("Screens: undefined alias");
		}

		if(typeof screen?.renderer != "function") {
			return console.error("Screens: renderer must be function");
		}

		this.screens[screen.alias] = {
			route: route,
			...screen
		};

		this.routesMap[route] = screen.alias;
	}

	switch(alias) {		
		this.runSwitchHandlers(alias);
		this.hideErrorScreen();
		this.showLoader();
		this.currentScreen?.DOMObject.remove();
		
		if(typeof this.screens[alias] == "undefined") {
			console.error(`Screens: "${alias}" not found`);
			return ;
		}

		this.currentScreen = this.screens[alias];
		
		const screenContainer = document.createElement("div");
		screenContainer.classList.add("screen");
		screenContainer.id = alias;
		screenContainer.dataset.alias = alias;
		screenContainer.innerHTML = this.currentScreen.renderer();
		this.currentScreen.DOMObject = screenContainer;
		this.screensContainer.append(this.currentScreen.DOMObject);

		this.currentScreen.initer(this);
	}

	reload() {
		if(!this.currentScreen) {
			return ;
		}

		this.runReloadHandlers(this.currentScreen.alias);
		this.switch(this.currentScreen.alias);
	}

	reinit() {
		if(!this.currentScreen) {
			return ;
		}

		this.currentScreen.initer(this);
		this.runReinitHandlers();
	}

	routing() {
		setInterval(() => {
			const route = document.location.hash.split("#!")[1];
			if(typeof route == "undefined" || route == "") {
				return;
			}
			
			const alias = (typeof this.routesMap[route] == "undefined") 
				? "not-found-screen"
				: this.routesMap[route];

			if(!this.currentScreen || this.currentScreen.alias != alias) {
				this.switch(alias);
			}
		}, 50);
	}

	ready() {
		if(this.currentScreen == null) {
			return;
		}

		if(!this.currentScreen.DOMObject) {
			this.currentScreen.DOMObject = document.getElementsById(this.currentScreen.alias);
		}

		this.hideLoader();
		this.currentScreen.DOMObject.classList.add("a-show");
	}

	error(title, text) {
		this.currentScreen?.DOMObject.remove();
		this.errorScreen.querySelector(".error-title").innerHTML = title;
		this.errorScreen.querySelector(".error-text").innerHTML = text;
		this.showErrorScreen();
		this.runErrorScreenHandlers();
	}

	hideLoader() {
		this.loader.classList.remove("a-show");
	}

	showLoader() {
		this.loader.classList.add("a-show");
	}

	showErrorScreen() {
		this.errorScreen.classList.add("a-show");
	}

	hideErrorScreen() {
		this.errorScreen.classList.remove("a-show");
	}

	getScreens() {
		return this.screens;
	}

	getRoutesMap() {
		return this.routesMap;
	}

	onSwitch(cb) {
		this.eventsHandlers.switch.push(cb);
	}

	onReaload(cb) {
		this.eventsHandlers.reload.push(cb);
	}

	onReinit(cb) {
		this.eventsHandlers.reinit.push(cb);
	}

	onErrorScreen(cb) {
		this.eventsHandlers.errorScreen.push(cb);
	}

	runSwitchHandlers(alias) {
		const route = Object.keys(this.routesMap).filter(route => this.routesMap[route] == alias);
		for(let handler of this.eventsHandlers.switch) {
			handler(this, alias, route.length ? route[0] : undefined);
		}
	}

	runReloadHandlers(alias) {
		for(let handler of this.eventsHandlers.reload) {
			handler(this, alias);
		}
	}

	runReinitHandlers() {
		for(let handler of this.eventsHandlers.reinit) {
			handler(this);
		}
	}

	runErrorScreenHandlers() {
		for(let handler of this.eventsHandlers.errorScreen) {
			handler(this);
		}
	}
}