//====Constants====//
const CATEGORY      = 1;
const ID            = 2;
const DESCRIPTION   = 3;
const CURRENT_FIELD = 4;
const MIN_TOP       = 20;

//====Keys Class====//
var keys = {
    enter:  13,
    up:     38,
    down:   40,
    left:   37,
    right:  39,
    tab:    9,
    shift:  16,
    esc:    27,

    contains: function(inputKey) {
        for (let key in keys) {
            if (keys[key] === inputKey) return true;
        }
        return false;
    }
};

//====Callback Object====//
jOmnis.callbackObject = {
   //====Variables====//
    categories:         null,
    categoriesElement:  null,
    clearButton:        null,
    dataSet:            null,
    fieldInputElement:  null,
    fields:             null,
    fieldsArray:        null,
    fieldsListElement:  null,
    showAllFields:      null,

    //====Standard Methods====//
    addCategories: function() {
        var allElement = document.createElement("option");
        allElement.value = allElement.text = "All";
        this.categoriesElement.add(allElement);

        var categories = Object.keys(this.fields);
        categories.forEach(element => {
            var categoryElement = document.createElement("option");
            categoryElement.value = categoryElement.text = element;
            this.categoriesElement.add(categoryElement);
        });

        this.categoriesElement.size = categories.length + 1;
    },
    addFields: function(fields) {
        var fieldDescriptions = fields.map(field => field.description);
        fields.forEach((element) => {
            element.duplicate = fieldDescriptions.indexOf(element.description) != fieldDescriptions.lastIndexOf(element.description);
            
            var field       = document.createElement("option");
            field.value     = element.id;
            field.id        = element.omnisLine;
            field.text      = element.description + (element.duplicate && this.showAllFields ? ` (${element.category})` : '');
            field.selected  = element.currentField;
            this.fieldsListElement.add(field);
        });

        if (fields.length == 0 && this.fieldInputElement.value) {
            var field       = document.createElement("option");
            field.value     = field.text = "No Results";
            field.disabled  = true;
            this.fieldsListElement.add(field);
        }

        if (fields.length > this.categoriesElement.size) {
            this.fieldsListElement.size = fields.length;
            return;
        }

        this.fieldsListElement.size = this.categoriesElement.size;
    },

    clearListElement: function(listElement) {
        while (listElement.firstChild) {
            listElement.removeChild(listElement.firstChild);
        }
    },
    clearFieldInputElement: function() {
        this.fieldInputElement.value        = "";
        this.clearButton.style.visibility   = "hidden";
        this.filterFields("");
    },

    escape: function() {
        jOmnis.sendControlEvent({
            "description": "escape"
        });
    },

    filterFields: function(input) {
        input = input.toLowerCase();
        var filterFieldsArray = this.fieldsArray.filter(element => {
            return element["description"].toLowerCase().includes(input)
        });

        this.clearListElement(this.fieldsListElement);
        this.addFields(filterFieldsArray);
    },

    getHeight: function() {
        var categoriesElementHeight  = this.categoriesElement.offsetHeight;
        var fieldsListElementHeight  = this.fieldsListElement.offsetHeight;

        return categoriesElementHeight > fieldsListElementHeight ? categoriesElementHeight : fieldsListElementHeight;
    },
    getWidth: function() {
        const PADDING                = 8;
        var categoriesElementWidth   = this.categoriesElement.offsetWidth;
        var fieldsListElementWidth   = this.fieldsListElement.offsetWidth;
        this.fieldsListElement.style = `min-width: ${fieldsListElementWidth}px; margin-left: ${categoriesElementWidth}px;`;

        return categoriesElementWidth + fieldsListElementWidth + PADDING;
    },

    init: function() {
        this.initEventHandlers();
        this.addCategories();
        this.categoriesElement.value = "All";

        this.clearListElement(this.fieldsListElement)
        this.fieldsArray = [];
        this.clearFieldInputElement();
        this.openCategory("All");
        this.sendDimensions();
    },
    initEventHandlers: function() {
        var opener = this;

        this.initCategoriesElementEventHandlers(opener);
        this.initFieldInputElementEventHandlers(opener);
        this.initFieldsListElementEventHandlers(opener);

        window.addEventListener("keydown", function(e) {
            if (e.keyCode == keys.esc) {
                opener.escape();
                return;
            }
            if (keys.contains(e.keyCode)) return;

            opener.fieldInputElement.focus();
        })
    },
    initCategoriesElementEventHandlers: function(opener) {
        opener.categoriesElement.addEventListener("change", function(e) {
            opener.clearListElement(opener.fieldsListElement)
            opener.fieldsArray = [];
            opener.clearFieldInputElement();
            opener.openCategory(e.target.value);
            opener.fieldInputElement.placeholder = `Search ${e.target.value} Fields`;
            opener.sendDimensions();
        });

        opener.categoriesElement.addEventListener("keydown", function(e) {
            if (e.shiftKey || (e.keyCode != keys.right && e.keyCode != keys.tab)) return;
            e.preventDefault();

            if (!document.querySelector("#fields-list option:checked")) {
                document.querySelector("#fields-list option").selected = true;
                opener.loadChoice();
            }

            opener.fieldsListElement.focus();
        });
    },
    initFieldInputElementEventHandlers: function(opener) {
        // Handles non-system keys
        opener.fieldInputElement.addEventListener("keydown", function(e) {
            if (keys.contains(e.keyCode)) return;

            var input = "";
            if (e.key == "Backspace") {
                if (e.target.value) input = e.target.value.slice(0, -1);
            }
            else input = e.target.value + e.key;

            opener.filterFields(input);
            opener.toggleClearButtonVisibility(e.key);
            opener.loadChoice();
        });

        // Handles system keys
        opener.fieldInputElement.addEventListener("keydown", function(e) {
            if (!keys.contains(e.keyCode)) return;
            
            switch (e.keyCode) {
                case keys.enter:
                    opener.submitChoice();
                    break;
                case keys.tab:
                    e.preventDefault();
                    if (e.shiftKey) opener.escape();
                    else opener.categoriesElement.focus();
                    break;
                case keys.down:
                    e.preventDefault();
                    opener.moveCheckedField(false);
                    break;
                case keys.up:
                    e.preventDefault();
                    opener.moveCheckedField(true);
                    break;
                default:
                    break;
            }
        });
    },
    initFieldsListElementEventHandlers: function(opener) {
        opener.fieldsListElement.addEventListener("change", function() {
            opener.keepOnScreen();
            opener.loadChoice();
        });

        opener.fieldsListElement.addEventListener("click", function() {
            opener.submitChoice();
        });

        opener.fieldsListElement.addEventListener("keydown", function(e) {
            if (e.keyCode == keys.enter || (e.keyCode == keys.tab && !e.shiftKey)) {
                opener.submitChoice();
                return;
            }
            if (e.keyCode == keys.left) opener.categoriesElement.focus();
        });
    },

    keepOnScreen: function() {
        var currentSelection = document.querySelector("#fields-list option:checked");
        if (!currentSelection) return;

        var bounding = currentSelection.getBoundingClientRect();
        if (bounding.top < MIN_TOP) window.scrollBy(0, -(bounding.botton - bounding.top));
        if (bounding.bottom > window.innerHeight) window.scrollBy(0, (bounding.bottom - bounding.top));
    },

    // keyPressed: function(key) {
    //     this.fieldInputElement.value = key;
    //     this.filterFields(key);
    //     this.toggleClearButtonVisibility(key);
    //     this.loadChoice();
    // },
    
    loadChoice: function() {
        jOmnis.sendClickEvent(this.omnisGetCurrentLine());
    },

    moveCheckedField: function(moveUp) {
        var selectedField   = document.querySelector("#fields-list option:checked");
        var sibling         = moveUp ? selectedField.previousSibling : selectedField.nextSibling;
        if (sibling.tagName != "OPTION") return;

        selectedField.selected  = false;
        sibling.selected        = true;
        this.keepOnScreen();
    },

    openCategory: function(category) {
        this.showAllFields = category == "All";
        if (this.showAllFields) {
            for (var category in this.fields) {
                this.fields[category].forEach(element => {
                    element.category = category;
                    this.fieldsArray.push(element);
                });
            }
        } else {
            this.fields[category].forEach(element =>  {
                this.fieldsArray.push(element);
            });
        }

        this.sortFieldsArray();
        this.addFields(this.fieldsArray);
    },

    // startup gets called by Omnis when the html-controls have finished loading
    startup: function(key) {
        // Restore this functionality upon resolution for Omnis Issue ST/WO/2538
        // if (key) this.keyPressed(key);
        // else this.sendDimensions();
        this.clearListElement(this.fieldsListElement)
        this.fieldsArray = [];
        this.clearFieldInputElement();
        this.openCategory("All");
        this.sendDimensions();
    },

    sendDimensions: function() {
        var opener = this;
        jOmnis.sendControlEvent({
            "description"  : "setDimensions",
            "height"    : opener.getHeight(),
            "width"     : opener.getWidth()
        });
    },

    sortFieldsArray: function() {
        this.fieldsArray.sort(function(a, b) {
            if (a.description > b.description) return 1;
            if (b.description > a.description) return -1;
            if (a.category > b.category) return 1;
            if (b.category > a.category) return -1;
            return 0;
        });
    },

    submitChoice: function() {
        jOmnis.sendDoubleClickEvent(this.omnisGetCurrentLine());
    },

    toggleClearButtonVisibility: function(key) {
        var result = "";

        if (key == "Backspace") {
            if (this.fieldInputElement.value) result = this.fieldInputElement.value.slice(0, -1);
        } else result = this.fieldInputElement.value + key;

        if (result) this.clearButton.style.visibility = "visible";
        else this.clearButton.style.visibility = "hidden";
    },

    //====Omnis Methods====//
    omnisGetCurrentLine: function() {
        var selectedId = document.querySelector("#fields-list").value;
        var selectedOmnisLine  = document.querySelector(`option[value='${selectedId}']`);
        if (!selectedOmnisLine) return;

        selectedOmnisLine = Number(selectedOmnisLine.id);
        return selectedOmnisLine;
    },
    omnisOnLoad: function() {
        this.categories         = [];
        this.categoriesElement  = document.getElementById("field-categories");
        this.clearButton        = document.getElementById("clear-btn");
        this.fieldInputElement  = document.getElementById("field-input");
        this.fields             = {};
        this.fieldsArray        = [];
        this.fieldsListElement  = document.getElementById("fields-list");
        this.showAllFields      = true;
    },
    omnisSetData: function(fields) {
        if (fields instanceof omnis_list && fields.getRowCount()>0) {
            this.clearFieldInputElement();
            this.clearListElement(this.categoriesElement);
            this.clearListElement(this.fieldsListElement);
            this.categories     = [];
            this.fields         = {};
            this.fieldsArray    = [];

            for (var i = 0; i < fields.getRowCount(); i++) {
                // Fields are separated into separate lists of categories
                if (!(fields.getData(CATEGORY, i + 1) in this.fields)) {
                    this.fields[fields.getData(CATEGORY, i + 1)] = [];
                }

                this.fields[fields.getData(CATEGORY, i + 1)].push({
                    "id"            : fields.getData(ID, i + 1),
                    "description"   : fields.getData(DESCRIPTION, i + 1),
                    "currentField"  : fields.getData(CURRENT_FIELD, i + 1),
                    "omnisLine"     : i + 1
                });
            }

            this.init();
        }
    }
};