// See https://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/

export interface FileUploadModuleOptions {

    async?: boolean;
    asyncPostUrl?: string;

}

export default class FileUploadModule {

    private options: FileUploadModuleOptions;
    private $elements: JQuery;

    constructor(selector: string, options?: FileUploadModuleOptions) {
        
        this.options = this.getOptions(options);
        this.$elements = $(selector);

        const self = this;
        this.$elements.each(function(index, element) {

            const $fileInputElement = $(element);

            if ($fileInputElement.data("file-upload-module-initialized") === true) {
                return;
            }

            if (!$fileInputElement.is("input[type=file]")) {
                throw Error("FileUploadModule expected element \"<input[type=file] />\", but was " + $fileInputElement);
            }

            const $labelElement = $fileInputElement.next();
            if ($labelElement[0].nodeName.toLowerCase() !== "label") {
                throw Error("FileUploadModule expected the next element after the file input to be \"<label></label>\", but was " + $labelElement);
            }

            const defaultLabelHtml = $labelElement.html();

            $fileInputElement.addClass("inputfile");

            $fileInputElement.on("change", function(event: JQueryEventObject) {

                const fileInputElement = this as HTMLInputElement;

                const files: FileList = fileInputElement.files;

                if (files === undefined || files === null || files.length === 0) {
                    self.setNoFiles($fileInputElement, $labelElement, defaultLabelHtml);
                    return;
                }

                self.setSelectedFiles($fileInputElement, $labelElement, files);
            });

            $fileInputElement.data("file-upload-module-initialized", true);
        });
    }

    public getFileName(file: File) {
        const size = this.formatBytes(file.size);
        return file.name + " (" + size + ")";
    }
    
    public formatBytes(bytes: number): string { // https://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-in-javascript
        if (bytes === 0) {
            return "0 Bytes";
        }

        const k = 1000;
        const dm = 0; // 0 decimals
        const units = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
        const i = Math.floor(Math.log(bytes) / Math.log(k));

        let size = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
        let unitName = units[i];

        if (i === 0) { // Do not use bytes, convert to KB
            size = parseFloat((size / 1000).toFixed(2));
            unitName = units[1];
        }
        
        return size + " " + unitName;
    }

    private getOptions(customOptions: FileUploadModuleOptions) {

        const defaultOptions: FileUploadModuleOptions = {
            async: false,
        };

        // Merge defaultOptions and customOptions
        const options = $.extend(true, {}, defaultOptions, customOptions);

        return options;
    }

    private setSelectedFiles($fileInputElement: JQuery, $labelElement: JQuery, files: FileList) {
        $labelElement.addClass("file-selected");

        const $textElement = $labelElement.find("div.text");

        if (this.options.async === false) {
            $textElement.css("width", $textElement.outerWidth());
        }

        if (files.length === 1) {
            const fileName = this.getFileName(files.item(0));
            $textElement.html(fileName);
        } else {
            console.log("Multiple files not implemented");
        }
    }

    private setNoFiles($fileInputElement: JQuery, $labelElement: JQuery, defaultLabelHtml: string) {
        $labelElement.html(defaultLabelHtml);
    }

    private allowsMultipleFiles($fileInputElement: JQuery): boolean {
        return $fileInputElement.is("[multiple]");
    }
}
