/**
 * A GridPanel class with live search support.
 */
Ext.define("App.view.CustomSearchPanel", {
    extend: "Ext.grid.Panel",
    requires: ["Ext.toolbar.TextItem", "Ext.form.field.Checkbox", "Ext.form.field.Text", "Ext.ux.statusbar.StatusBar"],

    /**
     * @private
     * search value initialization
     */
    searchValue: null,

    fil: [],

    /**
     * @private
     * The matched positions from the most recent search
     */
    matches: [],

    /**
     * @private
     * The current index matched.
     */
    currentIndex: null,

    /**
     * @private
     * The generated regular expression used for searching.
     */
    searchRegExp: null,

    /**
     * @private
     * Case sensitive mode.
     */
    caseSensitive: false,

    /**
     * @private
     * Regular expression mode.
     */
    regExpMode: false,

    /**
     * @cfg {String} matchCls
     * The matched string css classe.
     */
    matchCls: "x-livesearch-match",

    defaultStatusText: "Search Here",

    // Component initialization override: adds the top and bottom toolbars and setup headers renderer.
    initComponent: function() {
        var me = this;

        me.tbar = [
            "Search",
            {
                xtype: "textfield",
                name: "searchField",
                hideLabel: true,
                width: 200,
                listeners: {
                    change: {
                        fn: me.onTextFieldChange,
                        scope: this,
                        buffer: 600
                    }
                }
            }
        ];

        me.bbar = new Ext.ux.StatusBar({
            defaultText: me.defaultStatusText,
            name: "searchStatusBar"
        });

        me.callParent(arguments);
    },

    // afterRender override: it adds textfield and statusbar reference and start monitoring keydown events in textfield input
    afterRender: function() {
        var me = this;
        me.callParent(arguments);
        me.textField = me.down("textfield[name=searchField]");
        me.statusBar = me.down("statusbar[name=searchStatusBar]");

        me.view.on("cellkeydown", me.focusTextField, me);
    },

    focusTextField: function(view, td, cellIndex, record, tr, rowIndex, e, eOpts) {
        if (e.getKey() === e.S) {
            e.preventDefault();
            this.textField.focus();
        }
    },

    // detects html tag
    tagsRe: /<[^>]*>/gm,

    // DEL ASCII code
    tagsProtect: "\x0f",

    /**
     * In normal mode it returns the value with protected regexp characters.
     * In regular expression mode it returns the raw value except if the regexp is invalid.
     * @return {String} The value to process or null if the textfield value is blank or invalid.
     * @private
     */
    getSearchValue: function() {
        var b = this,
            a = b.textField.getValue();
        if (a === "") {
            return null;
        }
        if (!b.regExpMode) {
            a = Ext.String.escapeRegex(a);
        } else {
            try {
                new RegExp(a);
            } catch (c) {
                b.statusBar.setStatus({ text: c.message, iconCls: "x-status-error" });
                return null;
            }
            if (a === "^" || a === "$") {
                return null;
            }
        }
        return a;
    },

    /**
     * Finds all strings that matches the searched value in each grid cells.
     * @private
     */

    onTextFieldChange: function() {
        var me = this,
            count = 0,
            view = me.view,
            columns = me.visibleColumnManager.getColumns();
        view.refresh();
        // reset the statusbar
        me.statusBar.setStatus({
            text: me.defaultStatusText,
            iconCls: ""
        });
        me.searchValue = me.getSearchValue();
        me.matches = [];
        me.currentIndex = null;

        var store = me.getStore();
        store.clearFilter();

        if (me.features.length !== 0) {
            console.log(me.features);

            var grouping = me.getView().getFeature("grouping");
            if (grouping.ftype == "grouping") {
                grouping.expandAll();
            }
        }

        var filters = [
            new Ext.util.Filter({
                filterFn: function(item) {
                    for (let index = 0; index < me.fil.length; index++) {
                        var filterField = me.fil[index].toLowerCase();
                        var searchString = item.get(filterField);
                        if (searchString !== null) {
                            if (index < me.fil.length - 1) {
                                if (searchString.toLowerCase().includes(me.searchValue)) {
                                    return true;
                                } else {
                                    continue;
                                }
                            } else {
                                if (searchString.toLowerCase().includes(me.searchValue)) {
                                    return true;
                                } else {
                                    false;
                                }
                            }
                        }
                    }
                }
            })
        ];
        if (me.getSearchValue() !== null) {
            store.filter(filters);
        }

        if (me.searchValue !== null) {
            me.searchRegExp = new RegExp(me.getSearchValue(), "g" + (me.caseSensitive ? "" : "i"));

            me.store.each(function(record, idx) {
                var node = view.getNode(record);

                if (node) {
                    Ext.Array.forEach(columns, function(column) {
                        var cell = Ext.fly(node).down(column.getCellInnerSelector(), true),
                            matches,
                            cellHTML,
                            seen;

                        if (cell) {
                            matches = cell.innerHTML.match(me.tagsRe);
                            cellHTML = cell.innerHTML.replace(me.tagsRe, me.tagsProtect);

                            // populate indexes array, set currentIndex, and replace wrap matched string in a span
                            cellHTML = cellHTML.replace(
                                me.searchRegExp,
                                function(m) {
                                    ++count;
                                    if (!seen) {
                                        me.matches.push({
                                            record: record,
                                            column: column
                                        });
                                        seen = true;
                                    }
                                    return '<span class="' + me.matchCls + '">' + m + "</span>";
                                },
                                me
                            );
                            // restore protected tags
                            Ext.each(matches, function(match) {
                                cellHTML = cellHTML.replace(me.tagsProtect, match);
                            });
                            // update cell html
                            cell.innerHTML = cellHTML;
                        }
                    });
                }
            }, me);

            // results found
            if (count) {
                me.currentIndex = 0;
                me.statusBar.setStatus({
                    text: Ext.String.format("{0} match{1} found.", count, count === 1 ? "es" : ""),
                    iconCls: "x-status-valid"
                });
                me.textField.focus();
            }
        }

        // no results found
        if (me.currentIndex === null) {
            me.getSelectionModel().deselectAll();
            me.textField.focus();
        }
    }
});
