"use strict";

let audioLoadedCounter = 0;

class Lesofon {
	constructor() {
		this.appState = {
			'gameRunning': false,
			'globalMute': false,
			'allContentLodaded': false,
			'startGameHintArrowLoaded': false,
			'audioLoopBarNum': 0,
			'barLengthMs': 2857.1428571428571428571428571429,
			'showBottomBarY': 0,
			'easterEggClicks': 0,
		};
		this.elements = {
			'gameContainerInner': document.querySelector('.game-container-inner'),
			'theather': document.querySelector('.theather'),
			'bubbleTemplates': document.querySelectorAll('.ti-bubliny-template'),
			'clickDetectorContainer' : document.querySelector('.click-detector-container'),
			'clickDetectorAreas': [...document.querySelector('map[name="click-detector-map"]').children],
			'guide': document.querySelector('.guide'),
			'curtain': document.querySelector('.curtain'),
			'curtainColor': document.querySelector('.curtain-color'),
			'info': document.querySelector('.info-container'),
			'year': document.querySelector('.info-year'),
			'loadingInfoContainer': document.querySelector('.loading-info-container'),
			'loadingInfoData': {
				'filetype': document.querySelector('.loading-info-text_filetype'),
				'fraction': document.querySelector('.loading-info-text_fraction')
			},
			'startGameHintArrow': document.querySelector('.start-game-hint-arrow'),
			'bottomBar': document.querySelector('.bottom-bar'),
			'waitOverlay': document.querySelector('.wait-overlay'),
			'gameAudios': [...document.querySelector('.audio-wrapper').children],
			'playingAudios' : document.querySelector('#playing-audios')
		};
		this.controls = {
			'start': document.querySelector('[data-button-id="start"]'),
			'guide': document.querySelector('[data-button-id="guide"]'),
			'info': document.querySelector('[data-button-id="info"]'),
			'end': document.querySelector('[data-button-id="end"]'),
			'fullscreen': document.querySelector('[data-button-id="fullscreen"]'),
			'mute': document.querySelector('[data-button-id="mute"]')
		};
		this.storage = {
			'resizeTimeout': undefined,
			'mouseMoveTimeout': undefined,
			'currentClientY': 0,
			'clickDetectorAreasCoords': []
		};

		this._storeImgMapCoords();

		this._adaptGameWindowSize();

		this._addEventListeners();

		this._loadCurtainImgs();
		this._loadHintArrow();
		this._loadLoadingImage();
	}

	_storeImgMapCoords() {
		for (const areaElement of this.elements.clickDetectorAreas) {
			this.storage.clickDetectorAreasCoords
				.push(areaElement.getAttribute('coords')
					.split(',')
						.map((coord) => Number(coord))
				);
		}
	}

	_adaptGameWindowSize() {
		const showBottomBarEdge = 8	// [0,9]

		const screenWidth = window.innerWidth;
		const screenHeight = window.innerHeight;
		let resUnit = Math.floor(Math.min(screenWidth / 16, screenHeight / 9));
		if (resUnit > 120) {
			if (resUnit < 130)
				resUnit = 120;
			else
				resUnit -= 10;
		}
		const fhdToCurrentResRatio = resUnit / 120;

		this.appState.showBottomBarY = Math.floor((resUnit * showBottomBarEdge) + (screenHeight - (resUnit * 9)) / 2);
		this.elements.gameContainerInner.style.fontSize = fhdToCurrentResRatio + 'px';

		// responsive imgMap
		const clickDetectorAreasCoords = this.storage.clickDetectorAreasCoords;
		const imgMapAreasCount = clickDetectorAreasCoords.length;
		let imgAreaCoordsResized = [];
		for (let i = 0; i < imgMapAreasCount; ++i) {
			imgAreaCoordsResized = [];
			const imgAreaCoords = clickDetectorAreasCoords[i];
			const imgAreaCoordsCount = imgAreaCoords.length;
			for (let j = 0; j < imgAreaCoordsCount; ++j) {
				imgAreaCoordsResized[j] = Math.floor(imgAreaCoords[j] * fhdToCurrentResRatio);
			}
			this.elements.clickDetectorAreas[i].coords = imgAreaCoordsResized.join(',');
		}
	}

	_addEventListeners() {
		window.addEventListener('resize', () => this._resizeGameWindowHandler());

		this.controls.start.addEventListener('click', () => this.start());
		this.controls.guide.addEventListener('click', () => this.toggleGuide());
		this.controls.info.addEventListener('click', () => this.toggleInfo());
		this.controls.end.addEventListener('click', () => this.end())
		this.controls.fullscreen.addEventListener('click', () => this.toggleFullscreen());
		this.controls.mute.addEventListener('click', () => this.toggleGlobalMute());

		this.elements.guide.addEventListener('click', () => this.hideGuide());
		this.elements.info.addEventListener('click', (e) => this._handleInfoClick(e.target));

		this.elements.year.addEventListener('click', () => this._handleYearClick());

		const touchScreenDetected = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0);
		if (!touchScreenDetected)
			window.addEventListener('mousemove', (e) => this._mouseMoveHandler(e));
		else
			this.elements.bottomBar.classList.add('bottom-bar-always-show');

		document.addEventListener('fullscreenchange', () => this._updateFullscreenButonLook());

		const clickDetectorAreas = this.elements.clickDetectorAreas;
		for (const area of clickDetectorAreas)
			area.addEventListener('click', e => this.toggleTrack(e.target.dataset.clickNum));
	}

	_loadCurtainImgs() {
		const imgs = [...document.querySelectorAll('[data-curtain-src]')];
		const imgsCount = imgs.length;
		let imgsLoadedCount = 0;

		for (const img of imgs) {
			img.addEventListener('load', () => {
				++imgsLoadedCount;
				if (imgsLoadedCount >= imgsCount) {
					this.elements.curtain.classList.add('curtain-images-loaded');
					this.elements.curtainColor.classList.add('transparent');
				}
			});
			img.src = img.dataset.curtainSrc;
		}
	}

	_loadHintArrow() {
		const arrow = this.elements.startGameHintArrow;
		arrow.addEventListener('load', () => {
			this.appState.startGameHintArrowLoaded = true;
			arrow.classList.remove('start-game-hint-arrow-hidden');
		});
		arrow.src = arrow.dataset.arrowSrc;
	}

	_loadLoadingImage() {
		const img = document.querySelector('.loading-info-img');
		img.addEventListener('load', () => img.classList.add('loading-info-img-loaded'));
		img.src = img.dataset.loadingInfoSrc;
	}



	_resizeGameWindowHandler() {
		const msDebounce = 1000;

		this.elements.waitOverlay.classList.remove('hidden');
		clearInterval(this.storage.resizeTimeout);
		this.storage.resizeTimeout = setTimeout(() => {
			this._adaptGameWindowSize();
			this.elements.waitOverlay.classList.add('hidden');
		}, msDebounce);
	}

	_mouseMoveHandler(e) {
		this.storage.currentClientY = e.clientY;
		console.info('mousemove');

		// throttling
		if (this.storage.mouseMoveTimeout)
			return;

		const msThrottle = 80;

		const updateBottomBarVisibility = () => {
			if (this.storage.currentClientY > this.appState.showBottomBarY)
				this.elements.bottomBar.classList.remove('bottom-bar-hidden');
			else
				this.elements.bottomBar.classList.add('bottom-bar-hidden');
		}

		this.storage.mouseMoveTimeout = setTimeout(() => {
			clearTimeout(this.storage.mouseMoveTimeout);
			this.storage.mouseMoveTimeout = undefined;
			updateBottomBarVisibility();
		}, msThrottle);
	}



	async start() {
		if (this.appState.gameRunning)
			return;

		this.appState.gameRunning = true;

		this.controls.start.classList.add('disabled');
		this.controls.info.classList.add('disabled');
		this.elements.info.classList.add('invisible');

		if (this.appState.allContentLodaded) {
			this._hideCurtain();
			return;
		}

		const arrow = this.elements.startGameHintArrow;
		if (this.appState.startGameHintArrowLoaded) {
			arrow.classList.add('start-game-hint-arrow-hidden');
			await sleep(400);
		}
		arrow.remove();
		await sleep(600);
		this._loadTheatherImgs();
	}

	_loadTheatherImgs() {
		const theaterImgsElms = [...document.querySelectorAll('.theather-img')];
		const theaterImgsCount = theaterImgsElms.length;
		let theatherImgsLoaded = 0;

		const refreshDisplayedLoadedCount = () => {
			this.elements.loadingInfoData.fraction.textContent
				= `${theatherImgsLoaded}/${theaterImgsCount}`;
		};

		refreshDisplayedLoadedCount();
		this.elements.loadingInfoData.filetype.textContent = 'obrazů';
		this.elements.loadingInfoContainer.classList.remove('loading-info-container-hidden');

		for (const theaterImgElm of theaterImgsElms) {
			theaterImgElm.addEventListener('load', () => {
				++theatherImgsLoaded;
				refreshDisplayedLoadedCount();
				if (theatherImgsLoaded >= theaterImgsCount) {
					this._loadGraphics();
				}
			});
			theaterImgElm.src = `img/${theaterImgElm.dataset.theatherId}`;
		}
	}

	_loadGraphics() {
		const imgElems = [...document.querySelectorAll('[data-graphics-load]')];
		const imgsCount = imgElems.length;

		const refreshDisplayedLoadedCount = (imgsLoaded, imgsCount) => {
			this.elements.loadingInfoData.fraction.textContent
				= `${imgsLoaded}/${imgsCount}`;
		};

		const getImgsLoadedCount = () => {
			let count = 0;
			for (const imgElem of imgElems)
				if (imgElem.dataset.graphicsLoaded === 'true')
					++count;
			return count;
		};

		const handleImgLoad = (imgElem) => {
			imgElem.dataset.graphicsLoaded = 'true';
			const imgsLoadedCount = getImgsLoadedCount();
			refreshDisplayedLoadedCount(imgsLoadedCount, imgsCount);
			if (imgsLoadedCount >= imgsCount)
				this._loadAudios();
		}

		refreshDisplayedLoadedCount('0', imgsCount);
		this.elements.loadingInfoData.filetype.textContent = 'grafiky';

		for (const imgElem of imgElems) {
			imgElem.addEventListener('load', () => handleImgLoad(imgElem));
			imgElem.setAttribute('src', imgElem.dataset.graphicsLoad);
		}
	}

	_loadAudios() {
		const audioElements = this.elements.gameAudios;
		const audiosCount = audioElements.length;

		const refreshDisplayedLoadedCount = (audiosLoaded, audiosCount) => {
			this.elements.loadingInfoData.fraction.textContent
				= `${audiosLoaded}/${audiosCount}`;
		};

		const getAudiosLoadedCount = () => {
			let count = 0;
			for (const audioElement of audioElements)
				if (audioElement.dataset.audioLoaded === 'true')
					++count;
			return count;
		};

		const handleAudioLoad = (audioElement) => {
			audioElement.dataset.audioLoaded = 'true';
			const audiosLoadedCount = getAudiosLoadedCount();
			refreshDisplayedLoadedCount(audiosLoadedCount, audiosCount);
			if (audiosLoadedCount >= audiosCount) {
				this.appState.allContentLodaded = true;
				this._hideCurtain();
			}
		};

		refreshDisplayedLoadedCount('0', audiosCount);
		this.elements.loadingInfoData.filetype.textContent = 'zvuků';

		for (const audioElement of audioElements) {
			const audioToLoad = new Audio();
			audioToLoad.addEventListener('canplaythrough', () => handleAudioLoad(audioElement));
			audioToLoad.src = audioElement.dataset.audioSrc;
			audioElement.setAttribute('src', audioElement.dataset.audioSrc);
		}
	}

	async _hideCurtain() {
		this.elements.loadingInfoContainer.classList.add('loading-info-container-hidden');
		await sleep(1600);
		this.elements.curtain.classList.remove('curtain-dark');
		await sleep(2000);
		this.elements.curtain.classList.remove('curtain-drawn');
		await sleep(4000);
		this.elements.theather.classList.remove('theather-darkness');
		await sleep(1000);
		this.elements.clickDetectorContainer.classList.remove('deactivated');
		this.controls.guide.classList.remove('disabled');
		this.controls.info.classList.remove('disabled');
		this.controls.end.classList.remove('disabled');
	}





	_updateFullscreenButonLook() {
		if (document.fullscreenElement)
			this.controls.fullscreen.classList.add('fullscreen-active');
		else
			this.controls.fullscreen.classList.remove('fullscreen-active');
	}

	toggleFullscreen() {
		if (!document.fullscreenElement)
		  document.documentElement.requestFullscreen();
		else
		  document.exitFullscreen();
	}

	toggleGlobalMute() {
		const mute = this.appState.globalMute = !this.appState.globalMute;
		const audioElements = [...this.elements.playingAudios.children];
		for (const audioElement of audioElements)
			audioElement.muted = mute;

		// update icon
		if (mute)
			this.controls.mute.classList.add('mute-btn-muted')
		else
			this.controls.mute.classList.remove('mute-btn-muted')
	}



	toggleAudio(audio) {
		const audioSrc = audio.dataset.audioSrc;
		const clock = document.querySelector('[data-bind-audio-src="' + audioSrc + '"]');

		if (audio.dataset.activated) {
			audio.dataset.activated = '';
			if (clock)
				clock.classList.add('clock-hidden');
		} else {
			audio.dataset.activated = 'true';
			if (clock)
				clock.classList.remove('clock-hidden');
		}

		const audioLoop = this.audioLoop;
		if (!audioLoop) {
			this._startAudioLoop();
		} else {
			let noActiveAudio = true;
			const audios = this.elements.gameAudios;
			for (const audio of audios) {
				if (audio.dataset.activated) {
					noActiveAudio = false;
					break;
				}
			}

			if (noActiveAudio)
				this._stopAudioLoop();
		}
	}

	toggleTrack(trackNum) {
		const audio = this.elements.gameAudios[trackNum - 1];
		this.toggleAudio(audio);
	}

	_startAudioLoop() {
		this.appState.audioLoopBarNum = 0;
		// this._audioLoopTick();
		this.audioLoop = setInterval(() => this._audioLoopTick(), this.appState.barLengthMs);
	}

	_audioLoopTick() {
		let barNum = this.appState.audioLoopBarNum;
		if (barNum >= 8) {
			barNum = 0;
			this.appState.audioLoopBarNum = 0;
		}

		const globalMute = this.appState.globalMute;

		// crazy optimalization; defining func nor one way or another
		let playFromSrc;
		if (globalMute) {
			playFromSrc = (audioSrc, durationMs) => {
				let tmpAudio = new Audio(audioSrc);
				tmpAudio.muted = 'true';
				this.elements.playingAudios.appendChild(tmpAudio);
				tmpAudio.play();
				setTimeout(() => {
					tmpAudio.remove();
					tmpAudio = undefined;
				}, durationMs);
			}
		} else {
			playFromSrc = (audioSrc, durationMs) => {
				let tmpAudio = new Audio(audioSrc);
				this.elements.playingAudios.appendChild(tmpAudio);
				tmpAudio.play();
				setTimeout(() => {
					tmpAudio.remove();
					tmpAudio = undefined;
				}, durationMs);
			}
		}

		const animateElements = (elmsClasses, ttl) => {
			if (!elmsClasses)
				return;
			const classes = elmsClasses.split(' ');
			for (const className of classes) {
				const elm = document.querySelector('.' + className);
				elm.classList.add('animate');
				setTimeout(() => elm.classList.remove('animate'), ttl);
			}
		}

		const animateSpecial = (key) => {
			if (!key)
				return;
			if (key === 'ti-bubliny')
				this._animateBubbles();
		}

		const audios = this.elements.gameAudios;

		for (const audio of audios) {
			const activated = Boolean(audio.dataset.activated);
			if (activated) {
				const interval = audio.dataset.interval;
				const intervalMatch = barNum % interval === 0;
				if (intervalMatch) {
					playFromSrc(audio.dataset.audioSrc, audio.duration * 1000);
					const ttl = interval * this.appState.barLengthMs - 100;
					animateElements(audio.dataset.animateElements, ttl);
					animateSpecial(audio.dataset.specialAnimation);
				}
			}
		}

		console.log('Takt č. ' + barNum + ' proveden.');

		++this.appState.audioLoopBarNum;
	}

	async _stopAudioLoop() {
		const step = 0.01;
		const stepMs = 10;

		clearInterval(this.audioLoop);
		this.audioLoop = undefined;
		// fadeout and remove
		const audios = [...this.elements.playingAudios.children];

		for (let i = 1 - step; i >= 0; i -= step) {
			for (const audio of audios) {
				audio.volume = i;
			}
			await sleep(stepMs)
		}
		for (const audio of audios) {
			audio.remove();
		}
	}



	_sendBubble(bubble) {
		const ttlMs = 10000;

		const tempBubble = bubble.cloneNode();
		tempBubble.classList.remove('ti-bubliny-template');
		bubble.parentElement.appendChild(tempBubble, bubble);
		tempBubble.classList.add('animate');
		setTimeout(() => tempBubble.remove(), ttlMs);
	}

	_animateBubbles() {
		const bubbleTemplates = this.elements.bubbleTemplates;
		const bubbleElemsCount = bubbleTemplates.length;

		// randomize bubble elements order
		const bubbles = [...bubbleTemplates];
		let temp;
		for (let i = 0; i < bubbleElemsCount; ++i) {
			const randomIndex = Math.floor(Math.random() * bubbleElemsCount);
			temp = bubbles[i];
			bubbles[i] = bubbles[randomIndex];
			bubbles[randomIndex] = temp;
		}

		// shooting bubbles up
		const timeStamps = [1.2, 1.4, 1.5, 2.1, 2.5, 2.6, 2.7, 2.74, 3.1, 3.2, 3.32, 3.38, 3.42, 3.6, 4.0, 4.6];
		for (let i = 0; i < bubbleElemsCount; ++i) {
			setTimeout(() => {
				this._sendBubble(bubbles[i]);
			}, timeStamps[i] * 1000);
		}
	}



	hideGuide() {
		this.elements.guide.classList.add('invisible');
	}

	toggleGuide() {
		this.elements.guide.classList.toggle('invisible');
	}



	hideInfo() {
		this.elements.info.classList.add('invisible');
	}

	_handleInfoClick(target) {
		if (target === this.elements.info)
			this.hideInfo();
	}

	toggleInfo() {
		this.elements.info.classList.toggle('invisible');
	}



	_handleYearClick() {
		if (!this.appState.gameRunning)
			return
		if (this.appState.easterEggClicks === 4) {
			this.toggleTrack('16');
			this.elements.year.classList.toggle('underlined');
			this.appState.easterEggClicks = 0;
			return
		}
		++this.appState.easterEggClicks;
		setTimeout(() => {this.appState.easterEggClicks = 0;}, 5000);
	}



	async end() {
		if (!this.appState.gameRunning)
			return;
		// znepovolit tlačítka
		this.controls.guide.classList.add('disabled');
		this.controls.info.classList.add('disabled');
		this.controls.end.classList.add('disabled');
		// vypnout hudbu
		const gameAudios = this.elements.gameAudios;
		for (const gameAudio of gameAudios)
			if (gameAudio.dataset.activated)
				this.toggleAudio(gameAudio);
		this._stopAudioLoop();
		await sleep(500);
		// zavřít info
		this.hideInfo();
		await sleep(100);
		// zatáhnout oponu
		this.elements.curtain.classList.add('curtain-drawn');
		this.elements.theather.classList.add('theather-darkness');
		await sleep(5000);
		// odnastavit helpku
		this.hideGuide();
		await sleep(200);
		//povolit tlačítka
		this.appState.gameRunning = false;
		this.controls.start.classList.remove('disabled');
		this.controls.info.classList.remove('disabled');
	}
}