// NibblePoker - Excel Password Remover 2.0
// Author: Herwin Bozet (@NibblePoker)
// License: Public Domain (This code)

// Implied Globals: JSZip
if(JSZip == null) {
    alert("JSZip isn't available !");
}

import {initCore} from "../../js/nibblepoker-core.mjs"
import {cloneTemplate} from "../../js/nibblepoker-template.mjs"

export const excelFileRegex = /^.*\.xls[xm]$/gi;
export const excelWorksheetRegex = /^xl\/worksheets\/.*.xml$/gi;

/**
 * Checks if the given filename appears to be for an Excel file.
 * @param fileName {string} Filename to be checked
 * @returns {boolean} `true` if it appears to be an Excel file, `false` otherwise.
 */
export function isExcelExtension(fileName) {
    return fileName.match(excelFileRegex) !== null;
}

// Tool-centric stuff
{
    initCore();

    //let outputZip;
    //let outputZipFilename = "default-filename.error.zip";
    //let filesTotalCount = 0;
    //let filesProcessedCount = 0;
    //let passwordsRemoved = 0;

    /** @type {string} */
    const appletId = "excel-password-remover";

    /** @type {HTMLInputElement} */
    const eFileInput = document.querySelector(`input[type=file]#${appletId}-input-file`);
    /** @type {HTMLButtonElement} */
    const eFileInputClearButton = document.querySelector(`button#${appletId}-input-file-reset`);

    /** @type {HTMLElement} */
    const eResultEmptyText = document.querySelector(`#${appletId}-details-empty`);
    /** @type {HTMLElement} */
    const eResultPopulatedText = document.querySelector(`#${appletId}-details-populated`);

    /** @type {HTMLElement} */
    const eResultContainer = document.querySelector(`#${appletId}-result-container`);

    /** @type {HTMLTemplateElement} */
    const eSuccessTemplate = document.querySelector(`template#tmpl-success-root`);
    /** @type {HTMLTemplateElement} */
    const eWarningTemplate = document.querySelector(`template#tmpl-warning-root`);
    /** @type {HTMLTemplateElement} */
    const eErrorTemplate = document.querySelector(`template#tmpl-error-root`);

    /** @type {HTMLButtonElement} */
    const eFileDownloadAllButton = document.querySelector(`button#${appletId}-files-download-all`);
    /** @type {HTMLButtonElement} */
    const eFileClearButton = document.querySelector(`button#${appletId}-files-clear`);

    class ExcelFileData {
        /** @type {File} */
        originalFile;

        /** @type {any} */
        processedZipFile;

        filesTotalCount = 0;
        filesProcessedCount = 0;
        passwordsRemoved = 0;

        constructor(originalFile) {
            this.originalFile = originalFile;
        }

        /** @returns {string} */
        getOutputName() {
			let outputZipExtension = "." + this.originalFile.name.split(".").pop();
			let outputZipFilename = this.originalFile.name.substring(0, this.originalFile.name.length - outputZipExtension.length);
			return outputZipFilename + "_no-password" + outputZipExtension;
        }
    }

    /** @type {ExcelFileData[]} */
    let rawWorksheetFiles = [];

    function onFileAddedToDom() {
        eResultEmptyText.hidden = true;
        eResultPopulatedText.hidden = false;
    }

    function onFilesRemovedFromDom() {
        eResultEmptyText.hidden = false;
        eResultPopulatedText.hidden = true;
    }

    /**
     * @param excelFile {ExcelFileData}
     */
    function onFileHavingPasswordRemoved(excelFile) {
        console.debug(`Removed ${excelFile.passwordsRemoved} password(s) from '${excelFile.originalFile.name}'`);

        cloneTemplate(
                eSuccessTemplate,
                {
                    "tmpl-success-filename": excelFile.originalFile.name,
                    "tmpl-success-password-count": excelFile.passwordsRemoved
                },
                true
            ).then(eFragment => {
                // Doesn't work on BS4 and RAW, it returns " "...
                //const eFileOutput = eFragment.firstChild;

                const eFileOutput = eFragment.querySelector("*");
                eFileOutput.addEventListener("click", function() {
                    downloadProcessedFile(excelFile);
                });
                eResultContainer.appendChild(eFileOutput);

                onFileAddedToDom();
            });
    }

    /**
     * @param excelFile {ExcelFileData}
     */
    function onFileWithoutPasswords(excelFile) {
        console.warn(`No password(s) were found in ${excelFile.originalFile.name} !`);

        cloneTemplate(
                eWarningTemplate,
                {
                    "tmpl-warning-filename": excelFile.originalFile.name,
                    "tmpl-warning-message-extension": "",
                    //"tmpl-warning-message-no-password": "",
                },
                true
            ).then(eFragment => {
                console.debug(eFragment);
                eResultContainer.appendChild(eFragment);
                onFileAddedToDom();
            });
    }

    /**
     * @param excelFile {ExcelFileData}
     */
    function onFileWithInvalidExtension(excelFile) {
        console.warn(`The given file '${excelFile.originalFile.name}' doesn't have an Excel extension`);

        cloneTemplate(
                eWarningTemplate,
                {
                    "tmpl-warning-filename": excelFile.originalFile.name,
                    //"tmpl-warning-message-extension": "",
                    "tmpl-warning-message-no-password": "",
                },
                true
            ).then(eFragment => {
                console.debug(eFragment);
                eResultContainer.appendChild(eFragment);
                onFileAddedToDom();
            });
    }

    /**
     * @param excelFile {ExcelFileData}
     * @param error {any}
     */
    function onExtractionFailure(excelFile, error) {
        console.error("Failed to extract the content of the file in the browser ! ("+error.message+")");

        cloneTemplate(
                eErrorTemplate,
                {
                    "tmpl-error-filename": excelFile.originalFile.name,
                    //"tmpl-error-message-jszip": ""
                },
                true
            ).then(eFragment => {
                console.debug(eFragment);
                eResultContainer.appendChild(eFragment);
                onFileAddedToDom();
            });
    }

    /**
     * @param excelFile {ExcelFileData}
     */
    function downloadProcessedFile(excelFile) {
        excelFile.processedZipFile.generateAsync({type:"base64"}).then(function(b64Data) {
            const eLink = document.createElement('a');
            eLink.download = excelFile.getOutputName();
            eLink.href = 'data:application/zip;base64,' + b64Data;
            eLink.click();
        }, function(err) {
            console.error(err);
        });
    }

    window.onload = function () {
        eFileDownloadAllButton.addEventListener("click", function() {
            document.querySelectorAll(".epr-download-all-click").forEach(eResultLine => {
                console.debug(eResultLine);
                eResultLine.click();
            });
        });

        eFileClearButton.addEventListener("click", function() {
            while (eResultContainer.firstChild) {
                eResultContainer.firstChild.remove();
            }
            onFilesRemovedFromDom();
            rawWorksheetFiles = [];
            eFileInputClearButton.click();
        });

		eFileInput.addEventListener('change', function(e) {
            rawWorksheetFiles = [];

            for (let i = 0; i < e.target.files.length; i++) {
                console.debug(e.target.files[i]);
                rawWorksheetFiles.push(new ExcelFileData(e.target.files[i]));
            }

            rawWorksheetFiles.forEach(excelFile => {
                if(!isExcelExtension(excelFile.originalFile.name)) {
                    onFileWithInvalidExtension(excelFile);
                    return;
				}

                JSZip.loadAsync(excelFile.originalFile).then(function(zip) {
                    console.group(`JSZip - ${excelFile.originalFile.name}`);

                    excelFile.processedZipFile = new JSZip();
                    excelFile.filesTotalCount = 0;
                    excelFile.filesProcessedCount = 0;
                    excelFile.passwordsRemoved = 0;

                    for(const[fileKey, fileValue] of Object.entries(zip.files)) {
                        excelFile.filesTotalCount++;

                        if(fileKey.match(excelWorksheetRegex)) {
                            console.debug("Checking: "+fileKey);

                            fileValue.async("string").then(function(fileText) {
                                console.group(`JSZip - ${excelFile.originalFile.name} - ${fileKey}`);

                                let startIndex = fileText.indexOf('<sheetProtection ');

                                if(startIndex === -1) {
                                    // No password found.
                                    excelFile.processedZipFile.file(fileKey, fileText);
                                    console.debug("Analysed: "+fileKey);
                                } else {
                                    // Removing the password.
                                    let endIndex = fileText.indexOf('/>', startIndex) + 2;
                                    fileText = fileText.replace(fileText.substr(startIndex, endIndex-startIndex), "");
                                    excelFile.processedZipFile.file(fileKey, fileText);
                                    console.debug("Processed: "+fileKey);
                                    excelFile.passwordsRemoved++;
                                }

                                excelFile.filesProcessedCount++;

                                console.groupEnd();
                            });
                        } else {
                            // Other files.
                            console.debug("Ignoring: "+fileKey);
                            fileValue.async("string").then(function(fileText) {
                                console.group(`JSZip - ${excelFile.originalFile.name} - ${fileKey}`);
                                console.debug(`Copying as-is`);

                                excelFile.processedZipFile.file(fileKey, fileText);
                                excelFile.filesProcessedCount++;

                                console.groupEnd();
                            });
                        }
                    }

                    console.debug("Waiting for all the files to be processed !");

                    function waitFilesBeingProcessed() {
                        console.debug("Processed "+excelFile.filesProcessedCount+" file(s) out of "+excelFile.filesTotalCount);

                        if(excelFile.filesTotalCount !== excelFile.filesProcessedCount) {
                            setTimeout(waitFilesBeingProcessed, 50);
                        } else {
                            console.debug("Done !");
                            if(excelFile.passwordsRemoved > 0) {
                                onFileHavingPasswordRemoved(excelFile);
                            } else {
                                onFileWithoutPasswords(excelFile);
                            }
                        }
                    }

                    setTimeout(waitFilesBeingProcessed, 50);

                    console.groupEnd();
                }, function (e) {
                    onExtractionFailure(excelFile, e);
                });
            });

        });
    }
}
