this.UIImageReader = class ImageReader {
    constructor(...inputs) {
        this.inputs = inputs;
        //this.previewEnabled = withPreview;
        this.loadedFiles = {};
        this.changeListeners = new Set();
        for(let inp of this.inputs) {
            //let inpName = inp.name;
            let inpId = inp.id;
            if(!inpId) {
                inp.id = "fileman-" + generateRandomId();
                inpId = inp.id;
            }
            let key = inpId;
            this.loadedFiles[key] = [];
        };
        this.boundFunctions = {};

        this.bindFunctions(
            this.manageDragEnd,
            this.manageDragStart,
            this.manageDragEnter,
            this.manageDragOver,
            this.manageDrag,
            this.loadImages,
        );
        this.attachEvents();
    };
    destroy() {
        let self = this;
        self.unregisterAllChangeListeners();
        self.detachEvents();
        self.emptyReader();
        self.inputs = undefined;
        self.loadedFiles = undefined;
    };
    emptyReader() {
        let self = this;
        for(const inp of self.inputs) {
            let inpName = inp.id;
            const FILES = self.loadedFiles[inpName];
            for(let f of FILES) {
                self.removeLoadedImage(inp, 0);
            };
        };
    };
    removeLoadedImage(inputfile, ...imgIndexes) {
        let self = this;
        let key = inputfile.id;
        if(key && self.loadedFiles[key]) {
            for(let imgInd of imgIndexes) {
                let removedFile = self.loadedFiles[key].splice(imgInd, 1)[0];
                if(removedFile && removedFile.objecturl) {
                    window.URL.revokeObjectURL(removedFile.objecturl);
                    for(let prop in removedFile) {
                        removedFile[prop] = undefined;
                    };
                };
                removedFile = undefined;
            };
            self.notifyChangeListeners({target: inputfile});
        } else {
            console.warn("No input registered in this ImageReader found with key: " + key);
        };
    };
    registerChangeListeners(...listeners) {
        let self = this;
        listeners.forEach((l) => {
            if(!self.changeListeners.has(l)) {
                self.changeListeners.add(l);
            };
        });
    };

    notifyChangeListeners(ev) {
        let self = this;
        self.changeListeners.forEach((l) => {
            l(self, ev);
        });
    };

    unregisterAllChangeListeners() {
        let self = this;
        for(let l of self.changeListeners) {
            self.changeListeners.delete(l);
        };
    };

    bindFunctions(...fns) {
        let self = this;
        fns.forEach((fn) => {
            if(typeof fn === "function") {
                if(!self.boundFunctions[fn.name]) {
                    self.boundFunctions[fn.name] = fn.bind(self);
                };
            };
        });
    };
    getBoundFunction(fName) {
        let self = this;
        return self.boundFunctions[fName];
    };

    detachEvents() {
        let self = this;
        for(let i of self.inputs) {
            if(i.type == 'file') {
                i.removeEventListener("change", self.getBoundFunction("loadImages"));
            } else {
                i.removeEventListener("drop", self.getBoundFunction("loadImages"));
            };
            i.removeEventListener("dragover", self.getBoundFunction("manageDragOver"));
            i.removeEventListener("dragenter", self.getBoundFunction("manageDragEnter"));
            i.removeEventListener("dragstart", self.getBoundFunction("manageDragStart"));
            i.removeEventListener("drag", self.getBoundFunction("manageDrag"));
            i.removeEventListener("dragend", self.getBoundFunction("manageDragEnd"));
        };
    };

    attachEvents() {
        let self = this;
        for(let i of self.inputs) {
            if(i.type == 'file') {
                i.addEventListener("change", self.getBoundFunction("loadImages"));
            } else {
                i.addEventListener("drop", self.getBoundFunction("loadImages"));
            };
            i.addEventListener("dragstart", self.getBoundFunction("manageDragStart"));
            i.addEventListener("dragend", self.getBoundFunction("manageDragEnd"));
            i.addEventListener("drag", self.getBoundFunction("manageDrag"));
            i.addEventListener("dragover", self.getBoundFunction("manageDragOver"));
            i.addEventListener("dragenter", self.getBoundFunction("manageDragEnter"));
        };
    };

    findSameNameFiles(key, filename) {
        let self = this;
        let uploadedImgForKey = self.loadedFiles[key];
        let present = false;
        if(uploadedImgForKey.length > 0) {
            for(let img of uploadedImgForKey) {
                if(!present) {
                    if(img.filename === filename) {
                        present = true;
                    };
                };
            };
        };
        return present
    };

    getAllFiles() {
        let self = this;
        let files = [];
        for(let f in self.loadedFiles) {
            files = files.concat(self.loadedFiles[f]);
        };
        return files;
    };

    getInputFiles(input) {
        const self = this;
        if(!input) {
            return;
        };
        const inputId = input.id;
        return self.loadedFiles[inputId] || [];
    };

    manageDragEnd(ev) {
        const self = this;
        ev.preventDefault();
        ev.stopPropagation();
        ev.dataTransfer.effectAllowed = "copy";
        ev.dataTransfer.dropEffect = "copy";
    }

    manageDragStart(ev) {
        const self = this;
        ev.preventDefault();
        ev.stopPropagation();
        ev.dataTransfer.effectAllowed = "copy";
        ev.dataTransfer.dropEffect = "copy";
    }

    manageDrag(ev) {
        const self = this;
        ev.preventDefault();
        ev.stopPropagation();
        ev.dataTransfer.effectAllowed = "copy";
        ev.dataTransfer.dropEffect = "copy";
    }

    manageDragEnter(ev) {
        const self = this;
        ev.preventDefault();
        ev.stopPropagation();
        ev.dataTransfer.effectAllowed = "copy";
        ev.dataTransfer.dropEffect = "copy";
    };

    manageDragOver(ev) {
        const self = this;
        ev.preventDefault();
        ev.stopPropagation();
        ev.dataTransfer.effectAllowed = "copy";
        ev.dataTransfer.dropEffect = "copy";
    };

    loadImages(ev) {
        let self = this;
        let target = ev.target;
        self.eventId = "event-" + generateRandomId(5);
        if(ev.target.classList.contains("has-error")) {
            ev.target.classList.remove("has-error");
        };
        const files = [];
        if(ev.type === "drop") {
            ev.preventDefault();
            ev.stopPropagation();
            ev.dataTransfer.effectAllowed = "copy";
            ev.dataTransfer.dropEffect = "copy";
            if(ev.dataTransfer.items) {
                const dtItems = ev.dataTransfer.items;
                for(const f of dtItems) {
                    if(f.kind === "file") {
                        files.push(f.getAsFile());
                    };
                };
            } else {
                [...ev.dataTransfer.files].forEach((f) => {
                    files.push(f);
                });
            };
        } else {
            for(const f of ev.target.files) {
                files.push(f);
            }
        };
        if(target && files.length > 0) {
            self.readInputFile(target, files).then((fileBuffer) => {
                let key = target.id;
                let finalFileArray = [];
                for(let f of fileBuffer) {
                    let filename = f.filename;
                    let is_present = false;
                    if(target.dataset.preventsamefile) {
                        is_present = self.findSameNameFiles(key, filename);
                    };
                    if(!is_present) {
                        finalFileArray.push(f);
                    };
                };
                if(target.dataset.replacefiles) {
                    self.loadedFiles[key] = finalFileArray;
                } else {
                    self.loadedFiles[key] = self.loadedFiles[key].concat(finalFileArray);
                };
                /** per riaggiornare correttamente gli indici rispettivi */
                for(let fileInd = 0; fileInd < self.loadedFiles[key].length; fileInd++) {
                    self.loadedFiles[key][fileInd].specific_list_index = fileInd;
                };
                self.notifyChangeListeners(ev);
            }, (error) => {
                ev.target.classList.add("has-error");
                console.error("Error loading the file.");
                console.error(error);
            });
        };
    };

    consumeCurrentFiles() {
        let self = this;
        if(self.currentFiles.length > 0 && self.READER) {
            const file = self.currentFiles[0];
            self.READER.readAsDataURL(file, "UTF-8");
        };
    };

    /**
     * @param {*} ev evento generato dal campo input
     * questa funzione leggerà dai file di tipo input e restituirà una promise risolta solo quando sarà letto il file
     */
    readInputFile(fileInput, filesToLoad) {
        let self = this;
        self.currentFiles = filesToLoad.length > 0 ? filesToLoad : [];
        self.currentFileInputName = fileInput.id;
        let fileBuffer = [];
        return new Promise((resolve, reject) => {
            self.READER = new FileReader();
            self.READER.addEventListener('load', (event) => {
                const result = event.target.result;
                const ReadFile = self.currentFiles[0];
                const objUrl = window.URL.createObjectURL(ReadFile);
                if(result) {
                    fileBuffer.push({
                        filename: ReadFile.name,
                        originalfile: ReadFile,
                        objecturl: objUrl,
                        content: result.substr(result.indexOf(',') + 1),
                        datatype: result.substr(0, result.indexOf(',') + 1),
                        file_id: self.currentFileInputName + "-" + ReadFile.name,
                        specific_list_index: 0,
                    });
                } else {
                    console.warn("No result found for filename " + ReadFile.name);
                    console.warn(event);
                };
                self.currentFiles.splice(0,1);
                if(self.currentFiles.length === 0) {
                    resolve(fileBuffer);
                } else {
                    self.consumeCurrentFiles();
                };
            });
            self.READER.addEventListener('error', (event) => {
                event.preventDefault();
                self.currentFiles.splice(0,1);
                if(self.currentFiles.length === 0) {
                    resolve(fileBuffer);
                } else {
                    self.consumeCurrentFiles();
                };
            });
            if(self.currentFiles.length > 0) {
                const currentFile = self.currentFiles[0];
                self.READER.readAsDataURL(currentFile, "UTF-8");
            } else {
                console.warn("Something went wrong with the file Reader");
                resolve([]);
            }
        });
    };
}