<template>
    <!-- Contains the file input, transcribe button and transcription progress indicator -->
    <div class="audio-file-picker">
        <div class="input-bar">
            <div class="text-input-sl" style="cursor: pointer;" @click="openFilePicker">
                <span class="text-placeholder material-icons" v-if="!anyFiles">&#xe226;</span> <!-- attach_file -->
                <span class="text-placeholder" v-if="!anyFiles">{{ translations.get("AudioUpload_SelectFileText") }}</span>
                <span v-if="anyFiles">{{ getFileNameList }}</span>
            </div>

            <input ref="audioFileInput" type="file" @input="onFilesChanged"
                accept="audio/wav" multiple :disabled="isTranscribing" />

            <button class="btn-primary" type="submit" :disabled="!anyFiles || isTranscribing" @click="doTranscription">
                <span class="material-icons">&#xEA3E;</span> <!-- history_edu -->
                <span>{{ translations.get("AudioUpload_TranscribeFiles") }}</span>
            </button>
        </div>

        <div v-if="isTranscribing" class="progress-bar-container">
            <div class="progress-bar-value progress-bar-indeterminate" />
        </div>

        <ul class="list-view" v-if="failures.size > 0">
            <li v-for="[id, failure] in failures" :key="failure.id" class="list-view__item transcription-error-container">
                <div>
                    {{ translations.get("AudioUpload_TranscriptionFailed_Message") }} <i v-if="failure.fileName">{{ failure.fileName }}</i><br />
                    <span v-if="failure.message">{{ translations.get("AudioUpload_TranscriptionFailed_Reason") }}: {{ failure.message }}</span>
                </div>

                <button class="btn-fab theme-flat" @click="dismissFailure(id)">
                    <span class="material-icons">&#xE14C;</span> <!-- clear -->
                </button>
            </li>
        </ul>
    </div>
</template>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.audio-file-picker {
    display: flex;
    gap: 0.5em;
    flex-direction: column;

    input {
        display: none;
    }

    .text-container {
        cursor: pointer;
    }
}

.input-bar {
    display: grid;
    align-items: stretch;
    gap: 0.5em;
    grid-template-columns: 1fr auto;
}

.transcription-error-container {
    display: grid;
    gap: 0.5em;
    grid-template-columns: 1fr auto;
    align-items: center;

    border-left: 3px solid red;
}
</style>

<script>
import { mapState } from "vuex";
import TranscribeService from "../js/TranscribeModule";
import Util, { log } from "../js/Util";
import { TranscriptionViewModel } from "../main"

class TranscriptionViewFailure {
    /**
     * @param {String} fileName The name of the file for which this failure occured.
     * @param {String} message The reason for this failure.
     */
    constructor(fileName, message) {
        /** @type {String} The UUID of this failure. */
        this.id = Util.generateUuid();

        /** @type {String | null} The name of the file for which this failure occured. */
        this.fileName = fileName;

        /** @type {String} The reason for the failure. */
        this.message = message;
    }
}

const transcribeService = new TranscribeService();

export default {
    name: "AudioUpload",
    data() {
        return {
            /** @type {File[]} */
            files: [],
            /** @type {Map<String, TranscriptionViewFailure>} */
            failures: new Map(),
            canTranscribe: false,
            isTranscribing: false
        }
    },
    computed: mapState({
        translations: state => state.translations,
        anyFiles() {
            return this.files?.length > 0;
        },
        getFileNameList() {
            let fileNameList = "";

            for (const file of this.files) {
                fileNameList += file.name + ", ";
            }

            return fileNameList.slice(0, fileNameList.length - 2);
        }
    }),
    emits: [ "newTranscription" ],
    methods: {
        openFilePicker() {
            this.$refs.audioFileInput.click();
        },
        onFilesChanged() {
            this.files = [];
            const files = this.$refs.audioFileInput.files;

            if (files?.length !== undefined && files?.length > 0) {
                for (const file of files) {
                    this.files.push(file);
                }
            }
        },
        async doTranscription() {
            this.isTranscribing = true;

            await this.getTranscriptions(this.files);

            this.files = []; // Clear the file list, as there is no reason the user would want to transcribe the same file multiple times over
            this.isTranscribing = false;
            // TODO: Push files to queue. If currently transcribing, good to go; it'll pull off it.
            // Else call getTranscriptions();
            // Then, we don't have to worry about preventing the user from transcribing multiple items.
        },
        /**
         * Dismisses a failure
         * @param {String} id The UUID of the failure.
         */
        dismissFailure(id) {
            this.failures.delete(id);
        },
        /**
         * Gets the transcription of each submitted audio file.
         * @param {File[]} files The files to transcribe.
         */
        async getTranscriptions(files) {
            const validFiles = [];

            // Skip any files that have already been transcribed
            for (const file of files) {
                if (this.$store.getters.hasTranscriptionOfFile(file)) {
                    this.createFailure(file.name, this.translations.get("AudioUpload_AlreadyTranscribed"));
                }
                else {
                    validFiles.push(file);
                }
            }

            if (validFiles.length === 0) {
                return;
            }

            try {
                for await (const batch of transcribeService.batchTranscribeFiles(validFiles)) {
                    for (const t of batch) {
                        if (!t.success) {
                            this.createFailure(t.file_name, t.log);
                            continue;
                        }

                        const f = files.find(f => f.name === t.file_name);
                        if (f === undefined) {
                            this.createFailure(t.file_name, this.translations.get("ErrorTryAgain"));
                            continue;
                        }

                        const tvm = new TranscriptionViewModel(t, f);

                        try {
                            this.$store.commit("rawTranscriptionAdd", tvm);
                        }
                        catch (TranscriptionExistsError) {
                            this.createFailure(t.file_name, this.translations.get("AudioUpload_AlreadyTranscribed"));
                        }
                    }
                }
            }
            catch (e) {
                log("Failed to transcribe files", "error");
                log(e);

                this.createFailure(e.fileName, e.message)
            }
        },
        createFailure(fileName, reason) {
            const failure = new TranscriptionViewFailure(fileName, reason);
            this.failures.set(failure.id, failure);
        }
    }
}
</script>
