/*eslint no-eval:1*/ // turn eval usage into a warning when linting
/* Copyright (c) 2015-present terrestris GmbH & Co. KG
 * Copyright (c) 2015-present David French
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * RowExpanderWithComponents plugin
 *
 * This an ux originally created for ExtJS 4.2.2 by David French under MIT
 * License.
 *
 * Modified by terrestris to fit the needs of ExtJS 6.
 * https://github.com/davidffrench/Ext.ux.RowExpanderWithComponents
 *
 * @class BasiGX.ux.RowExpanderWithComponents
 */
Ext.define('BasiGX.ux.RowExpanderWithComponents', {
    extend: 'Ext.grid.plugin.RowExpander',
    alias: 'plugin.rowexpanderwithcomponents',
    pluginId: 'rowexpanderwithcomponents',

    /**
     * @cfg {XTemplate} rowBodyTpl
     * This needs to default to the below for ExtJS components to render to the
     * correct row (defaults to <tt><div id="display-row-{id}"> </div></tt>).
     */
    rowBodyTpl: new Ext.XTemplate(
        '<div id="display-row-{id}"> </div>'
    ),

    /**
     * @cfg {Object} rowBodyCompTemplate
     * This template will be used for every record. It can contain general
     * Ext JS Components. Text in `{{ }}` will be executed as JavaScript.
     * Sample below
     *
     *     rowBodyCompTemplate: {
     *       xtype: 'container',
     *       items: [{
     *         xtype: 'image',
     *         src: '{{record.getOlLayer().get("legendUrl")}}',
     *         height: '{{record.getOlLayer().get("legendHeight")}}',
     *         alt: '{{record.getOlLayer().get("legendUrl")}}'
     *       }]
     *     }
     *
     * Defaults to <tt>null</tt>
     */
    rowBodyCompTemplate: null,

    /**
     * @cfg {Boolean} expandOnClick
     * <tt>true</tt> to toggle a row between expanded/collapsed when single
     * clicked (defaults to <tt>true</tt>).
     */
    expandOnClick: false,

    /**
     * @cfg {Boolean} hideExpandColumn
     * <tt>true</tt> to hide the column that contains the expand/collapse icons
     * (defaults to <tt>true</tt>).
     */
    hideExpandColumn: false,

    /**
     * @cfg {Boolean} enableTextSelection
     * <tt>true</tt> to enable text selection within the grid
     * (defaults to <tt>true</tt>).
     */
    enableTextSelection: true,

    preventRecursionArray: [],

    init: function(grid) {
        var me = this;
        var view;

        me.callParent(arguments);

        //get the grids view
        view = me.view = grid.getView();

        //this css does not highlight the row expander body
        grid.addCls('rowexpanderwithcomponents');

        // set the rowexpander column to hidden if hideExpandColumn config is
        // true
        if (me.hideExpandColumn) {
            grid.headerCt.query('gridcolumn')[0].hidden = true;
        }
        // enable text selection if the config is true
        if (me.enableTextSelection) {
            view.enableTextSelection = true;
        }

        view.on('expandbody', function(rowNode, record) {
            var recId = record.getId();
            if (!recId) {
                Ext.Error.raise('Error: Records must have an id to use the' +
                    'rowExpanderWithComponents plugin. ' +
                    'Use http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.data.' +
                    'Model-cfg-idProperty or http://docs.sencha.com/extjs/' +
                    '4.2.2/#!/api/Ext.data.Model-cfg-idgen');
            }

            var row = 'display-row-' + recId;
            var clonedRowTemplate = Ext.clone(me.rowBodyCompTemplate);

            // TODO The rowbody behaviour seems to be not that smooth. We
            // should have a look at this
            // if the row got children dont add it again
            if (Ext.get(row).dom.children.length === 0) {
                var parentCont = Ext.create(Ext.container.Container, {
                    height: '100%',
                    width: '100%',
                    itemId: grid.getId() + '-parentRowExpCont-' + recId,
                    items: [
                        me.replaceObjValues(clonedRowTemplate, record)
                    ]
                });
                //render the ExtJS component to the div
                parentCont.render(row);

                //Stop all events in the row body from bubbling up
                var rowEl = parentCont.getEl().parent('.x-grid-rowbody');
                rowEl.swallowEvent(['mouseenter', 'click', 'mouseover',
                    'mousedown', 'dblclick', 'cellclick', 'itemmouseenter',
                    'itemmouseleave', 'onRowFocus', 'mouseleave']);

                // adding the dynamic css to component
                var rowToStyle = parentCont.getEl().parent(
                    '.x-grid-rowbody-tr'
                );
                rowToStyle.addCls(grid.getCssForRow(record));
            }

        });
        //assign the helper functions to the gridview and grid
        view.getRowComponent = me.getRowComponent;
        grid.getRowComponent = me.getRowComponent;

        grid.addToRowComponent = me.addToRowComponent;
        grid.addToRowComponent = me.addToRowComponent;
    },

    /**
     * Gets the parent ExtJS container in the rowexpander body from the rows
     * record id.
     *
     * @param {Number} recId The row record id.
     * @return {Ext.container.Container} The parent ExtJS container in the
     *     rowexpander body
     */
    getRowComponent: function(recId) {
        return Ext.ComponentQuery.query(
            '#' + this.up('treepanel').getId() + '-parentRowExpCont-' + recId
        )[0];
    },

    /**
     * Removes all ExtJS items from the parent row component.
     *
     * @param {Number} recId The row record id.
     */
    removeAllFromRowComponent: function(recId) {
        var rowCont = this.getRowComponent(recId);

        rowCont.removeAll();
    },

    /**
     * Adds items to the parent ExtJS container in the rowexpander body
     * @param {integer} recId The row record id
     * @param {Array} items ExtJS components
     */
    addToRowComponent: function(recId, items) {
        var rowCont = this.getRowComponent(recId);

        rowCont.add(items);
    },

    /**
     * Allow single click to expand grid
     *
     * @param {Ext.view.Table} view The grid view.
     * @private
     */
    bindView: function(view) {
        if (this.expandOnClick) {
            view.on('itemclick', this.onItemClick, this);
        }
        this.callParent(arguments);
    },

    /**
     * Allow single click to expand grid.
     *
     * @param {Ext.view.Table} view The grid view.
     * @param {Ext.data.Model} record The record that belongs to the item.
     * @param {HTMLElement} row The item's element.
     * @param {Number} rowIdx The item's index.
     * @private
     */
    onItemClick: function(view, record, row, rowIdx) {
        this.toggleRow(rowIdx, record);
    },

    /**
     * Overrides the key bindings to be consistent with standard ext trees.
     * Also see https://www.w3.org/TR/wai-aria-practices/#treegrid
     *
     * @param {Object} view the view
     * @param {Object} record the record
     * @param {Object} row the row
     * @param {Number} rowIdx the row index
     * @param {KeyEvent} e the key event
     */
    onKeyDown: function(view, record, row, rowIdx, e) {
        var me = this;
        var key = e.getKey();
        var pos = view.getNavigationModel().getPosition();
        var isCollapsed;

        if (pos) {
            row = Ext.fly(row);
            isCollapsed = row.hasCls(me.rowCollapsedCls);

            if (e.ctrlKey && key === 39 && isCollapsed) {
                me.toggleRow(rowIdx, record);
            }
            if (e.ctrlKey && key === 37 && !isCollapsed) {
                me.toggleRow(rowIdx, record);
            }
        }
    },

    /**
     * Converts all string values with {{}} to code
     * Example: '{{record.get('test'}}' converts to record.get('test')
     *
     * @param {Object} obj The object in which code in values might be
     *     evaluated. Will be checked recursively.
     * @param {Ext.data.Model} record The record used for replacing.
     * @return {Object} The passed object in which replacements might have
     *     occured.
     * @private
     */
    replaceObjValues: function(obj, record) {

        for (var all in obj) {
            if (typeof obj[all] === 'string' && obj[all].match(/{{(.*)}}/)) {
                /* eslint no-eval:0 */
                obj[all] = eval(obj[all].match(/{{(.*)}}/)[1]);
            }
            if (typeof obj[all] === 'object' && obj[all] !== null) {
                if (Ext.Array.contains(this.preventRecursionArray, obj[all])) {
                    return obj;
                } else {
                    this.preventRecursionArray.push(obj[all]);
                    this.replaceObjValues(obj[all], record);
                }
            }
            if (obj.xtype) {
                obj.layerRec = record;
                // if we do not have a cluster layer, we remove the "double
                // symbology / legend"
                if (record.getOlLayer() && record.getOlLayer().get('type') &&
                    record.getOlLayer().get('type') !== 'WFSCluster' &&
                    Ext.isArray(obj.items) && obj.items.length > 1) {
                    var lastItem = obj.items[obj.items.length - 1];
                    if (lastItem.xtype === 'image') {
                        obj.items.pop();
                    }
                }
            }
        }

        return obj;
    }
});