/**
     * su inputField dovranno esserci i seguenti data attributes
     * data-class_to_apply='' <- classe da applicare alla optionsBox per aprirla (style da gestire autonomamente)
     * data-option_box='' <- selettore di classe per il box delle opzioni (id univoco raccomandato, in alternativa classe)
     * data-option_element='' <- nome tag delle opzioni (viene cercato dentro l'option_box) N.B. ogni elemento deve avere un attributo checkvalue per la ricerca
     * data-keyup_delay='' <- numero ms da applicare alla keyup timeout
     * data-display_type='' <- tipo di "display: " da dare alle opzioni da mostrare (default block)
     */
this.SearchSelect = class SearchSelect {
    constructor(inputField, optClickCallback) {
        this.input = inputField;
        this.optionBoxSelector = inputField.dataset.option_box;
        this.optionBox = this.optionBoxSelector[0] === "#" ? document.getElementById(this.optionBoxSelector.substring(1)) : document.getElementsByClassName(this.optionBoxSelector.substring(1))[0];
        if(!this.optionBox) {
            console.error("No option box found with selector " + this.optionBoxSelector);
        } else {
            this.stringToSearch
            this.optionsTagName = inputField.dataset.option_element;

            this.inputSearchSelectStyleSelector = this.buildSelector(this.input);
            this.optionBoxSearchSelectStyleSelector = this.buildSelector(this.optionBox);
            this.visibleOptionsDisplayType = inputField.dataset.display_type ? inputField.dataset.display_type : "block";
            this.notVisibleOptionsDisplayType = inputField.dataset.notvisible_display_type ? inputField.dataset.notvisible_display_type : "none";
            this.defaultOptionsDisplayType = inputField.dataset.default_display_type? inputField.dataset.default_display_type : "block";
            this.searchSelectId = "selectsearch-" + this.generate_id();
            this.options = this.optionsTagName ? this.optionBox.getElementsByTagName(this.optionsTagName) : this.optionBox.children;
            this.selectedOption = undefined;
            this.classToApply = inputField.dataset.class_to_apply;
            this.timeoutFn = undefined;
            this.keyupDelay = inputField.dataset.keyup_delay ? parseInt(inputField.dataset.keyup_delay, 10) : 500;
            if(!this.optionStyleElement) {
                const optionsStyleElement = document.createElement('style');
                optionsStyleElement.id = this.searchSelectId;
                this.optionStyleElement = optionsStyleElement;
                document.head.appendChild(optionsStyleElement);
            };
            this.boundFunctions = {};
            this.optClickCallback = optClickCallback;
            //console.log(this);
            this.bindFunctions(this.openBox, this.closeBox, this.listenForBodyClick, this.defaultOptClickedCallback);
            if(!this.optClickCallback) {
                this.optClickCallback = this.getBoundFunction("defaultOptClickedCallback");
            };
            this.applyStyleToOptions();
            this.add_event_listeners();
            let inputName = this.input.name;
            let qsValue = this.getQueryString(inputName);
            if(qsValue) {
                this.selectFromQuerystringValue(qsValue);
            };
        };
    };
    defaultOptClickedCallback(target, selectedOption, searchSelect) {
        let self = this;
        if(target) {
            let value = target.dataset.value ? target.dataset.value : target.getAttribute("value");
            if(value) {
                self.input.dataset.selectedValue = value;
            } else {
                delete self.input.dataset.selectedValue;
            };
        };
    };
    selectFromQuerystringValue(value) {
        let self = this;
        for(let o = 0; o < this.options.length; o++) {
            if(this.options[o].value == value) {
                let clickEvent = new Event("click", {cancelable: true, bubbles: true});
                self.options[o].dispatchEvent(clickEvent);
                break;
            };
        };
    };
    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];
    };
    buildSelector(element) {
        let self = this;
        let inputSelector = element.localName;
        if(element.id && element.id !== "") {
            inputSelector += "#" + element.id;
        };
        if(element.classList.length > 0) {
            inputSelector += ("." + Array.from(element.classList).join("."));
        };
        return inputSelector;
    };
    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;
    };
    openBox(ev) {
        let self = this;
        self.optionBox.classList.add(self.classToApply);
        document.body.addEventListener("click", self.getBoundFunction("listenForBodyClick"));
    };
    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"));
        if(self.selectedOption) {
            self.input.value = self.getNodeText(self.selectedOption);
        } else {
            self.selectedOption = undefined; // per evitare ""
            self.input.value = "";
        };
        self.applyStyleToOptions(self.input.value);
        if(self.timeoutFn) {
            clearTimeout(self.timeoutFn);
            self.timeoutFn = undefined;
        };
    }
    manageKeydown(ev) {
        let self = this;
        self.closeBox(ev);
    };
    applyStyleToOptions(searchValue) {
        let self = this;
        if(!searchValue) {
            let attrSelector = self.optionBoxSearchSelectStyleSelector + " " + self.optionsTagName;
            self.optionStyleElement.innerHTML = `${attrSelector} {display:${self.defaultOptionsDisplayType} !important;}`;
        } else {
            let toHideSelector = self.optionBoxSearchSelectStyleSelector + " " + self.optionsTagName;
            let attrSelector = toHideSelector + "[checkvalue*='" + searchValue.toLowerCase() + "']";
            self.optionStyleElement.innerHTML = `${toHideSelector} {display:${self.notVisibleOptionsDisplayType} !important;} ${attrSelector} {display:${self.visibleOptionsDisplayType} !important;}`;
        };
    };
    set_timer(time) {
        let self = this;
        if(!self.timeoutFn) {
            self.timeoutFn = setTimeout(() => {
                self.applyStyleToOptions(self.input.value);
                self.timeoutFn = undefined;
            }, time);
        } else {
            clearTimeout(self.timeoutFn);
            self.timeoutFn = undefined;
            self.set_timer(time);
        };
    };
    /**
     * 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;
        };
    };
    manageKeyUp(ev) {
        let self = this;
        /*console.log(" === manageKeyUp === ");
        console.log(ev);
        console.log(self);*/
        self.set_timer(self.keyupDelay);
    };
    manageOptionClick(ev) {
        let self = this;
        ev.stopPropagation();
        ev.preventDefault();
        /*console.log(" === manageOptionClick === ");
        console.log(ev);
        console.log("Clicked Option");
        console.log(self);*/
        ev.stopPropagation();
        self.optClickCallback(ev.target, self.selectedOption, self);
        self.selectedOption = ev.target;
        self.input.value = self.getNodeText(ev.target);
        self.closeBox(ev);
    };
    getNodeText(elm) {
        //let self = this;
        let text = "";
        for(let i = 0; i<elm.childNodes.length; i++) {
            if(elm.childNodes[i].nodeName === "#text") {
                text = elm.childNodes[i].wholeText;
                break;
            };
        };
        return text;
    };
    add_event_listeners() {
        let self = this;
        self.input.addEventListener("click", self.getBoundFunction("openBox"));
        self.input.addEventListener("keyup", self.manageKeyUp.bind(self));
        for(const opt of self.options) {
            opt.addEventListener("click", self.manageOptionClick.bind(self));
        }
    };
};