<script>
	import GuessGrid from './components/GuessGrid.vue'
	import Keyboard from './components/Keyboard.vue';
	import Overlay from './components/Overlay.vue';
	import PopUp from './components/PopUp.vue';

	export default {
		data() {
			return {
				// Constants
				LETTER_ANIMATION_DURATION: 0.3,
				SHAKE_ANIMATION_DURATION: 0.3,

				// Variables
				pairs: [],

				listen: true,

				gridState: null,
				rowsAnimations: null,
				keyboardMask: null,

				currentAttempt: 0,
				currentIndex: 0,

				secretWords: null,

				gameOver: false,
				won: false,
				overlayVisible: false,

				popups: {}
			}
		},

		async created() {
			window.addEventListener('keyup', (e) => {
				if (e.key === 'Backspace') {
					this.keyPress('Delete');
				} else if (e.key === 'Delete' || e.key === 'Enter') {
					this.keyPress(e.key);
				} else if (e.key.match(/^[a-zA-Z]$/)) {
					this.keyPress(e.key.toUpperCase())
				}
			});

			let req = await fetch('./math-pairs.json');
			this.pairs = await req.json();

			this.setupGame();
		},
		components: {
			GuessGrid,
			Keyboard,
			Overlay,
			PopUp,
		},
		methods: {
			setupGame() {
				this.gridState = Array.from({length: 6}, () => {
					return Array.from({length: 5}, () => {
						return {
							letter: '',
							state: 'unset'
						};
					});
				});
				this.rowsAnimations = new Array(6).fill(false);

				this.keyboardMask = Object.fromEntries(
					Array.from({length: 26}, (v, i) => [
						String.fromCharCode(i + 65), 'unused'
					])
				);
				this.keyboardMask['Enter'] = 'unused';
				this.keyboardMask['Delete'] = 'unused';
				console.log(this.keyboardMask);

				this.currentAttempt = 0;
				this.currentIndex = 0;

				this.secretWords = this.pairs[Math.floor(Math.random() * this.pairs.length)];
				console.log(this.secretWords);
				console.log(this.secretWords[0]);
				console.log(this.secretWords[1]);

				this.gameOver = false;
				this.won = false;
				this.overlayVisible = false;
			},
			randomId() {
				let result = '';
				let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
				let charactersLength = characters.length;
				for ( let i = 0; i < 16; i++ ) {
					result += characters.charAt(Math.floor(Math.random() * 
						charactersLength));
				}
				return result;
			},
			addPopUp(message) {
				let id = this.randomId();
				this.popups[id] = message;
				setTimeout(() => delete this.popups[id], 10*1000);
			},
			checkWin(matches, oneWordMatch) {
				if (!oneWordMatch) {
					return false;
				}
				for(let i = 0; i < 5; i++) {
					if (matches[i][0] === -1 || !matches[i][1]) {
						return false;
					}
				}

				return true;
			},
			resolveGame(won) {
				this.won = won;
				this.gameOver = true;
				this.overlayVisible = true;
			},
			keyPress(event) {
				if (!this.listen) {
					return;
				}
				if (event === "Delete") {
					if(this.currentIndex > 0) {
						this.currentIndex -= 1;
						this.gridState[this.currentAttempt][this.currentIndex].letter = '';
						this.gridState[this.currentAttempt][this.currentIndex].state = 'unset';
					}
				} else if (event == "Enter") {
					if(this.currentIndex === 5) {

						// Validate Word
						let currentWord = "";
						for(let i = 0; i < 5; i++) {
							currentWord += this.gridState[this.currentAttempt][i].letter;
						}
						if(!this.words.includes(currentWord)) {
							// Invalid attempt, signal error
							this.listen = false;
							this.rowsAnimations[this.currentAttempt] = true;
							
							setTimeout(() => {
								this.listen = true;
								this.rowsAnimations[this.currentAttempt] = false;
							}, this.SHAKE_ANIMATION_DURATION * 1000);

							return;
						}

						// Calculate result
						let wordsFlags = [ this.secretWords[0].split(''), this.secretWords[1].split('') ];
						let matches = [ [-1,false], [-1,false], [-1,false], [-1,false], [-1,false] ];

						for(let i = 0; i < 5; i++) {
							let letter = this.gridState[this.currentAttempt][i].letter;
							for(let j = 0; j < 2; j++) {
								if (wordsFlags[j][i] === letter) {
									wordsFlags[j][i] = null;
									matches[i] = [j, true];
								}
							}
						}
						for(let i = 0; i < 5; i++) {
							let letter = this.gridState[this.currentAttempt][i].letter;
							for(let j = 0; j < 2; j++) {
								for(let k = 0; k < 5; k++) {
									if(wordsFlags[j][k] === letter) {
										wordsFlags[j][k] = null;
										matches[i] = [j, false];
									}
								}
							}
						}
						let oneWordMatch = true;
						for(let i = 0; i < 5; i++) {
							for(let j = 0; j < 5; j++) {
								if (matches[i][0] == 0 && matches[j][0] == 1) {
									oneWordMatch = false;
								}
							}
						}

						// Update grid to tease result
						for(let i = 0; i < 5; i++) {
							if (matches[i][0] !== -1 && matches[i][1]) {
								this.gridState[this.currentAttempt][i].state = `correct-${oneWordMatch ? 'full' : 'half'}`;
							} else if (matches[i][0] !== -1 && !matches[i][1]) {
								this.gridState[this.currentAttempt][i].state = `misplaced-${oneWordMatch ? 'full' : 'half'}`;
							} else {
								this.gridState[this.currentAttempt][i].state = 'wrong';
							}
						}
						this.listen = false;

						// Resolve
						setTimeout(() => {
							for(let i = 0; i < 5; i++) {
								if (matches[i][0] !== -1 && matches[i][1]) {
									if(this.keyboardMask[this.gridState[this.currentAttempt][i].letter] === 'unused' ||
										this.keyboardMask[this.gridState[this.currentAttempt][i].letter] === 'misplaced') {
										this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'correct';
									}
								} else if (matches[i][0] !== -1 && !matches[i][1]) {
									if(this.keyboardMask[this.gridState[this.currentAttempt][i].letter] === 'unused') {
										this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'misplaced';
									}
								} else {
									if(this.keyboardMask[this.gridState[this.currentAttempt][i].letter] === 'unused') {
										this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'wrong';
									}
								}
							}

							this.currentAttempt += 1;
							this.currentIndex = 0;


							if (this.checkWin(matches, oneWordMatch)) {
								this.resolveGame(true);
							} else if (this.currentAttempt === 6) {
								this.resolveGame(false);
							}

							this.listen = true;
						}, this.LETTER_ANIMATION_DURATION * 1000 * 5);
					}
				} else {
					if (this.currentIndex < 5) {
						this.gridState[this.currentAttempt][this.currentIndex].letter = event;
						this.gridState[this.currentAttempt][this.currentIndex].state = 'set';
						this.currentIndex += 1;
					}
				}
			}
		}
	};
</script>

<template>
	<div class="page" 
		:style="`--letter-animation-duration: ${LETTER_ANIMATION_DURATION}s; --shake-animation-duration: ${SHAKE_ANIMATION_DURATION}s`"
	>
		<div class="toolbar">
			<div class="logo">
				<img src="logo-circuit-board.svg" alt="logo" />
				/
				Qwordle
			</div>
		</div>
		<div v-if="this.gridState !== null" class="interface">
			<div class="header">
				<div>Qwordle</div>
				<div class="end">
					<div class="material-icons-outlined">info</div>
					<div v-if="gameOver" class="material-icons-outlined" @click="overlayVisible = true">military_tech</div>
				</div>
			</div>
			<GuessGrid :state="gridState" :rows-animations="rowsAnimations"/>
			<Keyboard :mask="keyboardMask" @add="keyPress($event)" :listen="true"/>
		</div>
		<Overlay v-if="overlayVisible"
			:secret-words="secretWords"
			:state="gridState"
			:won="won"
			:last-attempt="currentAttempt"
			@close="overlayVisible = false"
			@reset="setupGame()"
			@share="addPopUp('copied to clipboard!')"
		/>
		<div v-for="popup in popups" class="popup">
			{{popup}}
		</div>
	</div>
</template>

<style lang="scss">
	:root {
		font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
		font-size: 16px;
		line-height: 24px;
		font-weight: 400;

		font-synthesis: none;
		text-rendering: optimizeLegibility;
		-webkit-font-smoothing: antialiased;
		-moz-osx-font-smoothing: grayscale;
		-webkit-text-size-adjust: 100%;

		--text-color: #ffffff;
		--background-color: #0f172a;
		--border-color: #475569;

		--button-color: #475569;
		--button-highlight: #596679;

		--popup-color: #323c4b;

		--keyboard-unused: #475569;
		--keyboard-wrong: #1e293b;
		--keyboard-correct: #22c55e;
		--keyboard-misplaced: #eab308;

		--unset-cell-border: #475569;
		--set-cell-border: #ffffff;
		--correct-cell-dark: #22c55e;
		--correct-cell-light: #4ade80;
		--misplaced-cell-dark: #eab308;
		--misplaced-cell-light: #facc15;
		--wrong-cell: #334155;
	}

	* {
		box-sizing: border-box;
	}
	html, body, #app, .page {
		min-height: 100vh;
	}
	body {
		margin: 0;
		color: var(--text-color);
		background-color: var(--background-color);
		text-shadow: 1px 1px #000000;
	}
	.page {
		position: relative;

		.toolbar {
			padding: 1rem .75rem 1rem 1rem;

			display: flex;
			align-items: center;

			border-bottom: 2px solid var(--border-color);

			.logo {
				display: flex;
				align-items: center;
				img {
					max-height: 2.5rem;
				}
			}
		}

		.interface {
			display: flex;
			flex-direction: column;
			align-items: center;
			gap: 2rem;

			padding: 6rem;

			.header {
				width: 18rem;
				display: flex;
				justify-content: space-between;

				font-size: 1.5rem;
				font-weight: 600;

				.end {
					display: flex;
					gap: 0.5rem;
				}
			}
		}
	}

	.material-icons, .material-icons-outlined, .button {
		cursor: pointer;
	}


	@keyframes appear {
		0% {
			transform: translate(-50%, 0);
			opacity: 0;
		}
		20% {
			transform: translate(-50%, -3rem);
			opacity: 1;
		}
		80% {
			transform: translate(-50%, -3rem);
			opacity: 1;
		}
		100% {
			transform: translate(-50%, 0);
			opacity: 0;
		}
	}

	.popup {
		position: absolute;
		bottom: 0%;
		left: 50%;
		transform: translateX(-50%);
		opacity: 0;

		animation: ease-in-out appear 5s;

		background-color: var(--popup-color);
		border-radius: 20px;
		padding: 1rem 3rem;

		font-weight: 600;
	}

</style>
