<script>
import { mapState } from "vuex";
import { saveAs } from "file-saver"
import Util, { log } from "../js/Util"
import MacronRestorationModule from "../js/MacronRestorationModule"
import { SnackController } from "./SnackBar.vue"

const macroniser = new MacronRestorationModule();

export default {
    name: "DirectInput",
    data() {
        return {
            macroniseWaitTimeout: null,
            waitingToMacronise: false,
            errorState: false,
            preserveExistingMacrons: true,
            enableSpellcheck: false,
            showMacronisedWords: true,
            normaliseLinebreaks: false
        }
    },
    computed: {
        canDownload() {
            return this.restored.length > 0 && !this.waitingToMacronise;
        },
        input: {
            get() {
                return this.$store.state.directInput;
            },
            set(newValue) {
                this.$store.commit("setDirectInput", newValue);
                this.startMacronisationWait();
            }
        },
        restored: {
            get() {
                return this.$store.state.directOutput;
            },
            set(newValue) {
                this.$store.commit("setDirectOutput", newValue);
            }
        },
        htmlFormattedRestored() {
            let restored = "";

            for (const word of this.restored) {
                if (word.w) {
                    if (word[0] !== "-") {
                        restored += " ";
                    }

                    if (word.macronised && this.showMacronisedWords) {
                        restored += `<mark class="highlight">${word.w}</mark>`;
                    }
                    else {
                        restored += word.w;
                    }
                }

                if (word.linebreaks) {
                    restored += this.normaliseLinebreak(word.linebreaks);
                }
            }

            return restored.trim();
        },
        formattedRestored() {
            let restored = "";

            for (const word of this.restored) {
                if (word.w) {
                    if (word[0] !== "-") {
                        restored += " ";
                    }

                    restored += word.w;
                }

                if (word.linebreaks) {
                    restored += this.normaliseLinebreak(word.linebreaks);
                }
            }

            return restored.trim();
        },
        ...mapState({
            translations: state => state.translations
        })
    },
    watch: {
        preserveExistingMacrons(newValue) {
            localStorage.setItem("preserveExistingMacrons", newValue);
        },
        enableSpellcheck(newValue) {
            localStorage.setItem("enableSpellcheck", newValue);
        },
        showMacronisedWords(newValue) {
            localStorage.setItem("showMacronisedWords", newValue);
        },
        normaliseLinebreaks(newValue) {
            localStorage.setItem("normaliseLinebreaks", newValue);
        }
    },
    methods: {
        /**
         * Handles input on an element, and grows its height so that the text is always visible.
         * @param {InputEvent} event The input event.
         */
        onTextInput(event) {
            event.target.height = "auto";
            event.target.height = event.target.scrollHeight;
        },
        startMacronisationWait() {
            if (this.input === null || this.input === "") {
                return;
            }

            this.waitingToMacronise = true;

            if (this.macroniseWaitTimeout !== null) {
                clearTimeout(this.macroniseWaitTimeout);
            }

            this.macroniseWaitTimeout = setTimeout(this.updateMacronisedText, 1000);
        },
        async updateMacronisedText() {
            this.errorState = false;

            try {
                this.restored = await macroniser.directMacronisation(this.input, this.preserveExistingMacrons);
                this.waitingToMacronise = false;
            }
            catch (ex) {
                this.errorState = true;
                log(ex, "warn");
            }
        },

        downloadAsText() {
            const blob = new Blob([ this.formattedRestored ], { type: "text/plain;charset=utf-8" });
            saveAs(blob, "restored.txt");
        },
        async copyToClipboard() {
            await navigator.clipboard.writeText(this.formattedRestored);
            SnackController.addSnack(this.translations.get("DirectInput_CopySuccess"));
        },

        /**
         * Normalises a linebreak.
         * @param {String} linebreakCount The linebreak sequence.
         */
        normaliseLinebreak(linebreakCount) {
            if (!this.normaliseLinebreaks) {
                return this.repeatString("\n", linebreakCount);
            }

            linebreakCount = Math.min(2, linebreakCount);
            return this.repeatString("\n", linebreakCount);
        },
        repeatString(string, count) {
            let result = "";

            for (let i = 0; i < count; i++) {
                result += string;
            }

            return result;
        }
    },
    beforeMount() {
        // The null check is used to default this setting to true
        this.preserveExistingMacrons = (localStorage.getItem("preserveExistingMacrons") ?? "true") === "true";
        this.showMacronisedWords = (localStorage.getItem("showMacronisedWords") ?? "true") === "true";
        this.normaliseLinebreaks = localStorage.getItem("normaliseLinebreaks") === "true";

        const enableSpellcheck = localStorage.getItem("enableSpellcheck");
        if (enableSpellcheck) {
            this.enableSpellcheck = enableSpellcheck === "true";
        }
        else {
            this.enableSpellcheck = Util.getNavigatorLanguage().startsWith("mi");
        }
    }
}
</script>

<template>
<div class="root">
    <textarea class="text-input input-area" @input="onTextInput" autocomplete="off"
        v-model="input" :placeholder="translations.get('DirectInput_InputPlaceholder')"
        :spellcheck="enableSpellcheck" />

    <div class="text-container">
        <span v-if="errorState" class="material-icons mdi-m error-text">error</span>
        <span v-if="errorState" class="error-text">{{ translations.get('DirectInput_UnknownError') }}</span>

        <span class="preserveWhitespace" v-html="htmlFormattedRestored" />
    </div>

    <div class="flex">
        <input type="checkbox" id="i-preserve-existing-macrons" v-model="preserveExistingMacrons" @input="startMacronisationWait" />
        <label for="i-preserve-existing-macrons">{{ translations.get('DirectInput_PreserveExistingMacrons') }}</label>
    </div>

    <div class="flex">
        <input type="checkbox" id="i-show-macronised-words" v-model="showMacronisedWords" :disabled="restored.length === 0" />
        <label for="i-show-macronised-words">{{ translations.get('DirectInput_ShowMacronisedWords') }}</label>
    </div>

    <div class="flex">
        <input type="checkbox" id="i-enable-spellcheck" v-model="enableSpellcheck" />
        <label for="i-enable-spellcheck">{{ translations.get('DirectInput_EnableSpellcheck') }}</label>
    </div>

    <div class="flex">
        <input type="checkbox" id="i-show-macronised-words" v-model="normaliseLinebreaks" :disabled="restored.length === 0" />
        <label for="i-show-macronised-words">{{ translations.get('DirectInput_NormaliseLinebreaks') }}</label>
    </div>

    <button class="btn-primary right-column" :disabled="!canDownload" @click="copyToClipboard">
        {{ translations.get('DirectInput_CopyToClipboard') }}
    </button>

    <button class="btn-primary right-column" :disabled="!canDownload" @click="downloadAsText">
        {{ translations.get('DirectInput_Download') }}
    </button>
</div>
</template>

<style scoped lang="scss">
.root {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1em;
}

.preserveWhitespace {
    white-space: pre-line;
}

.input-area {
    resize: vertical;
    min-height: 4em;
}

.flex {
    display: flex;
    align-items: center;
    gap: 0.5em;
}

.left-column {
    grid-column: 1;
}

.right-column {
    grid-column: 2;
}

.highlight {
    background-color: var(--highlighted-word-bg);
}

.error-text {
    color: rgb(185, 3, 3);
}
</style>
