this.RemoteTextInputSearch = class RemoteTextInputSearch {
    /**
     * 
     * @param {*} inputField il campo input type text da gestire
     * @param {*} optClickCallback una funzione che viene chiamata quando si clicca su una delle opzioni
     * @param {*} searchFunction la funzione di ricerca remota
     * @param {*} elementSearchScope un eventuale sottoalbero dove cercare il box delle opzioni
     */
    constructor(inputField, optClickCallback, searchFunction, elementSearchScope) {
        this.input = inputField;
        this.optionBoxSelector = inputField.dataset.optionbox;
        this.searchSelectId = "selectsearch-" + this.generate_id();
        elementSearchScope = elementSearchScope ? elementSearchScope : document;
        this.optionBox = this.optionBoxSelector[0] === "#" ? document.getElementById(this.optionBoxSelector.substring(1)) : elementSearchScope.getElementsByClassName(this.optionBoxSelector.substring(1))[0];
        this.boundFunctions = {};
        this.remoteTextBackdrop = htmlToElement("<div id='remote-text-search-backdrop-" + this.searchSelectId + "' class='remote-text-search-backdrop'><i class='remote-text-search-backdropicon fa fa-search'/></div>");
        this.page = 0;
        this.elementsPerPage = this.input.dataset.elementsnum || 20;
        this.elementsPerPage = parseInt(this.elementsPerPage, 10);
        if(isNaN/this.elementsPerPage) {
            this.elementsPerPage = 0
        }
        if(!this.optionBox) {
            console.error("No option box found with selector " + this.optionBoxSelector);
        } else {
            this.optClickCallback = optClickCallback;
            this.searchFunction = searchFunction;
            this.classToApply = this.input.dataset.classtoapply;
            this.keyupTimeout = this.input.dataset.keyuptimeout ? parseInt(this.input.dataset.keyuptimeout, 10) : 450;
            this.selectedElement = undefined;
            this.bindFunctions(
                this.listenForBodyClick, 
                this.openBox, 
                this.closeBox, 
                this.manageOptionClick,
                this._getNewPage,
            );
            this.changeSelectionListeners = new Set();
            this.BoxListeners = new Set();
        };
    };
    _renderPager(pages) {
        const self = this;
        return ` <div> </div `
        /*
        return `
<div class="align-items-center d-flex justify-content-between p-1 remote-search-pager">
<p>${self.page + 1} - ${pages}</p>
<div class="remote-text-actions">
    <button class="prev fa fa-chevron-left"></button>
    <button class="next fa fa-chevron-right"></button>
</div>
</div>`;
*/
    };
    _attachPagerEvents() {
        const self = this;
        const prevBtn = self.optionBox.getElementsByClassName("prev")[0];
        const nextBtn = self.optionBox.getElementsByClassName("next")[0];
        prevBtn.addEventListener("click", self.getBoundFunction("_getNewPage"));
        nextBtn.addEventListener("click", self.getBoundFunction("_getNewPage"));
    };
    _getNewPage(ev) {
        const self = this;
        if(ev.target.classList.contains("prev") && self.page > 0) {
            self.page--;
            self.set_timer();
        } else if(ev.target.classList.contains("next") && self.page < self.maxpage) {
            console.log("SONO QUA1");
            self.page++;
            self.set_timer();
        }
    };
    getRandomnumber(max) {
        return Math.floor(Math.random() * max);
    }
    getQueryString(querystring) {
        var results = new RegExp('[\?&]' + querystring + '=([^&#]*)').exec(window.location.search);

        return (results !== null) ? results[1] || 0 : false;
    };
    /**
     * 
     * @param {*} length lunghezza dell' id, default 25
     * @returns 
     */
    generate_id(length) {
        let self = this;
        const res = {
            0: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
            1: "1234567890",
        };
        if(!length) {
            length = 25;
        };
        let newId = "";
        for(let i = 0; i < length; i++) {
            let resChoice = self.getRandomnumber(2);
            let resource = res[resChoice];
            let char = resource[self.getRandomnumber(resource.length)];
            newId += char;
        };
        //console.log("NEW THREAD ID: " + newId);
        return newId;
    };
    addBD() {
        let self = this;
        let currentBD = document.getElementById("remote-text-search-backdrop-" + self.searchSelectId);
        if(!currentBD) {
            document.body.append(self.remoteTextBackdrop);
        };
    };

    removeBD() {
        let self = this;
        let currentBD = document.getElementById("remote-text-search-backdrop-" + self.searchSelectId);
        if(currentBD) {
            currentBD.remove(self.remoteTextBackdrop);
        };
    };
    /** se devono succedere cose quando si cambia l'opzione selezionata */
    registerChangeSelectionListeners(...listeners) {
        let self = this;
        listeners.forEach((l) => {
            if(!self.changeSelectionListeners.has(l)) {
                self.changeSelectionListeners.add(l);
            };
        });
    };
    notifyChangeSelectionListeners() {
        let self = this;
        self.changeSelectionListeners.forEach((l) => {
            l(self.selectedElement, self);
        });
    };
    removeChangeSelectionListeners(l) {
        let self = this;
        if(self.changeSelectionListeners.has(l)) {
            self.changeSelectionListeners.delete(l);
        };
    };
    /** se devono succedere cose quando si apre il box dei risultati */
    registerOptionBoxListeners(...listeners) {
        let self = this;
        listeners.forEach((l) => {
            if(!self.BoxListeners.has(l)) {
                self.BoxListeners.add(l);
            };
        });
    };
    notifyOptionBoxListeners() {
        let self = this;
        self.BoxListeners.forEach((l) => {
            l(self.optionBox, self);
        });
    };
    removeOptionBoxListener(l) {
        let self = this;
        if(self.BoxListeners.has(l)) {
            self.BoxListeners.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];
    };
    openBox(ev) {
        let self = this;
        self.optionBox.classList.add(self.classToApply);
        document.body.removeEventListener("click", self.getBoundFunction("listenForBodyClick"));
        document.body.addEventListener("click", self.getBoundFunction("listenForBodyClick"));
        self.optionsBoxOpen = true;
        self.notifyOptionBoxListeners();
    };
    listenForBodyClick(ev) {
        let self = this;
        if(ev.target.id === self.input.id) {
            return;
        }
        let isTargetInsideOptionsBox = self.getParentNode(ev.target, "."+self.classToApply);
        if(isTargetInsideOptionsBox.nodeName === "BODY") {
            self.closeBox();
        };
    };
    closeBox(ev) {
        let self = this;
        self.optionBox.classList.remove(self.classToApply);
        document.body.removeEventListener("click", self.getBoundFunction("listenForBodyClick"));
        self.optionsBoxOpen = false;
        self.notifyOptionBoxListeners();
    };
    set_timer() {
        let self = this;
        if(!self.timeoutFn) {
            self.timeoutFn = setTimeout(() => {
                if(self.input.dataset.blockonsearch) {
                    self.addBD();
                };
                self.searchFunction(self.input.value, self.input, self).then((resp) => {
                    self.removeBD();
                    self.updateResults(resp);
                }, (err) => {
                    self.removeBD();
                    console.warn("Error with search function for remoteTextSearch: " + self.searchSelectId);
                    console.warn(err);
                })
                self.timeoutFn = undefined;
            }, self.keyupTimeout);
        } else {
            clearTimeout(self.timeoutFn);
            self.timeoutFn = undefined;
            self.set_timer();
        };
    };
    updateResults(result) {
        let self = this;
        console.log("updateResults");
        console.log(result);

        if(result.matches && result.matches.length > 0) {
            let newNodes = [];
            result.matches.forEach((m) => {
                let nod = htmlToElements(`<button type="button" class="remote-text-result" title="${m.title}" style="${m.style}" value="${m.value}">${m.label}</button>`)[0];
                if(m.data) {
                    for(let d in m.data) {
                        nod.dataset[d] = m.data[d];
                    };
                };
                newNodes.push(nod);
            });
            if(result.pages) {
                self.maxpage = result.pages
                let pager = htmlToElement(self._renderPager(result.pages));
                self.optionBox.replaceChildren(pager, ...newNodes);
                //self._attachPagerEvents();
            } else {
                self.optionBox.replaceChildren(...newNodes);
            };
            self.inputFieldClicked();
        } else {
            if(self.optionBox) {
                self.optionBox.innerHTML = "";
            };
            self.closeBox();
        };
    };
    /**
     * funzione che ritorna il parent che matcha il selettore (classe e id supportati) o il primo parent
     * se non trova nulla risale al body
     * @param {*} element 
     * @param {*} toMatch 
     * @returns 
     */
    getParentNode(element, toMatch=undefined) {
        let self = this;
        if(!toMatch) {
            return element.nodeName === "BODY" ? element:  element.parentNode;
        } else {
            if(element.nodeName === "BODY") {
                return element;
            }
            let parent = element.parentNode;
            if(toMatch[0] === ".") {
                if(element.classList.contains(toMatch.substr(1))) {
                    return element;
                }
                while(!parent.classList.contains(toMatch.substr(1)) && parent.nodeName !== "BODY") {
                    parent = parent.parentNode;
                }
                return parent;
            } else if(toMatch[0] === "#") {
                if(element.id === toMatch.substr(1)) {
                    return element;
                }
                while(!parent.id === toMatch.substr(1) && parent.nodeName !== "BODY") {
                    parent = parent.parentNode;
                };
                return parent;
            };
            return parent;
        };
    };
    manageOptionClick(ev) {
        let self = this;
        ev.stopPropagation();
        ev.preventDefault();
        if(ev.target.classList.contains("remote-text-result")) {
            self.optClickCallback(ev.target, self);
        };
    };
    manageKeyUp(ev) {
        let self = this;
        self.page = 0;
        self.maxpage = false;
        if(self.input.value && self.input.value !== "") {
            self.set_timer();
        } else {
            if(self.timeoutFn) {
                clearTimeout(self.timeoutFn);
                self.timeoutFn = undefined;
            };
            self.closeBox();
        };
    };
    inputFieldClicked(ev) {
        let self = this;
        const hasOpts = self.optionBox.getElementsByClassName("remote-text-result");
        if((self.input.value && self.input.value.trim() && self.input.value !== "") || (hasOpts.length > 0 && !self.input.dataset.searchwithnovalue)) {
            self.openBox();
        } else if(self.input.dataset.searchwithnovalue) {
            self.set_timer();
        };
    };
    add_event_listeners() {
        let self = this;
        self.input.addEventListener("keyup", self.manageKeyUp.bind(self));
        self.input.addEventListener("click", self.inputFieldClicked.bind(self));
        self.optionBox.addEventListener("click", self.getBoundFunction("manageOptionClick"));
    };
};