/**
 * This plugin allows the end user to configure the pivot component.
 *
 * It adds the following methods to the pivot grid:
 * - showConfigurator: which when called will show the configurator panel
 * - hideConfigurator: which when called will hide the configurator panel
 *
 * The configurator panel will be shown when the end-user does a `longpress` event
 * on the column headers.
 *
 * On phones a field can be moved between fields areas after a `longpress` event
 * is triggered on the field.
 */
Ext.define('Ext.pivot.plugin.Configurator', {
    extend: 'Ext.plugin.Abstract',
 
    requires: [
        'Ext.util.Collection',
        'Ext.pivot.plugin.configurator.Panel'
    ],
 
    alias: 'plugin.pivotconfigurator',
 
    /**
     * Fired on the pivot component before a configurator field is moved.
     *
     * Return false if you don't want to move that field.
     *
     * @event beforemoveconfigfield
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.Container} config.fromContainer Source container to move from
     * @param {Ext.pivot.plugin.configurator.Container} config.toContainer Destination container to move to
     * @param {Ext.pivot.plugin.configurator.Field} config.field Field configuration
     */
 
    /**
     * Fired on the pivot component before the pivot settings container is shown.
     *
     * Return false if you don't want to show the container.
     *
     * @event beforeshowpivotsettings
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.window.Settings} config.container Form panel container where you can inject
     * additional fields
     * @param {Object} config.settings Settings that will be loaded into the form
     */
 
    /**
     * Fired on the pivot component after the configurator pivot settings container is shown.
     *
     * @event showpivotsettings
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.window.Settings} config.container Form panel container where you can inject
     * additional fields
     * @param {Object} config.settings Settings that were loaded into the form
     */
 
    /**
     * Fired on the pivot component before settings are applied to the pivot matrix.
     *
     * Return false if you don't want to apply the settings to the pivot matrix.
     *
     * @event beforeapplypivotsettings
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.Settings} config.container Form panel container that contains all
     * pivot matrix settings.
     * @param {Object} config.settings Settings that will be loaded into the form
     */
 
    /**
     * Fired on the pivot component after settings are applied to the pivot matrix.
     *
     * @event applypivotsettings
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.Settings} config.container Form panel container where you can inject
     * additional fields
     * @param {Object} config.settings Settings that were loaded into the form
     */
 
    /**
     * Fired on the pivot component before the field settings container is shown.
     *
     * Return false if you don't want to show the field settings container.
     *
     * @event beforeshowconfigfieldsettings
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.Form} config.container Form panel container where you can inject
     * additional fields
     * @param {Object} config.settings Settings that will be loaded into the form
     */
 
    /**
     * Fired on the pivot component after the configurator field settings container is shown.
     *
     * @event showconfigfieldsettings
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.Form} config.container Form panel container where you can inject
     * additional fields
     * @param {Object} config.settings Settings that were loaded into the form
     */
 
    /**
     * Fired on the pivot component before settings are applied to the configurator field.
     *
     * Return false if you don't want to apply the settings to the field.
     *
     * @event beforeapplyconfigfieldsettings
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.Form} config.container Form panel container that contains all field settings
     * @param {Object} config.settings Settings that will be loaded into the form
     */
 
    /**
     * Fired on the pivot component after settings are applied to the configurator field.
     *
     * @event applyconfigfieldsettings
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config 
     * @param {Ext.pivot.plugin.configurator.Form} config.container Form panel container that contains all field settings
     * @param {Object} config.settings Settings that were loaded into the form
     */
 
    /**
     * Fired on the pivot component before the new configuration is applied.
     *
     * Return false if you don't want to apply the new configuration to the pivot grid.
     *
     * @event beforeconfigchange
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config Config object used to reconfigure the pivot
     */
 
    /**
     * Fired on the pivot component when the configuration changes.
     *
     * @event configchange
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     * @param {Object} config Config object used to reconfigure the pivot
     */
 
    /**
     * Fired on the pivot component when the configurator panel is visible
     *
     * @event showconfigpanel
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     */
 
    /**
     * Fired on the pivot component when the configurator panel is disabled
     *
     * @event hideconfigpanel
     * @param {Ext.pivot.plugin.configurator.Panel} panel Configurator panel
     */
 
 
    config: {
        /**
         * @cfg {Ext.pivot.plugin.configurator.Field[]} fields
         *
         * This is the array of fields you want to be used in the configurator.
         *
         * If no fields are defined then all fields are fetched from the store model if
         * a {@link Ext.pivot.matrix.Local Local} matrix is used.
         *
         * The fields are indexed by the dataIndex supplied to them which means that you can't have two fields
         * sharing the same dataIndex. If you want to define two fields that share the same dataIndex then
         * it's best to use a unique dataIndex for the 2nd field and define a grouperFn on it.
         *
         * The dimensions that are configured on the pivot component but do not exist in this fields collection
         * will be added here with a set of default settings.
         */
        fields: [],
        /**
         * @cfg {Number} width
         *
         * The width of the configurator panel.
         */
        width: 400,
        /**
         * @cfg {Object} panel
         *
         * Configuration object used to instantiate the configurator panel.
         */
        panel: {
            lazy: true,
            $value: {
                xtype: 'pivotconfigpanel',
                hidden: true,
                floated: true,
                modal: true,
                hideOnMaskTap: true,
                right: 0,
                height: '100%'
            }
        },
        /**
         * @private
         */
        pivot: null
    },
 
    init: function (pivot) {
        this.setPivot(pivot);
    },
 
    /**
     * @private
     * AbstractComponent calls destroy on all its plugins at destroy time.
     */
    destroy: function () {
        this.setConfig({
            pivot: null,
            panel: null
        });
        this.callParent();
    },
 
    /**
     * Enable the plugin to show the configurator panel.
     */
    enable: function () {
        this.disabled = false;
        this.showConfigurator();
    },
 
    /**
     * Disable the plugin to hide the configurator panel.
     */
    disable: function () {
        this.disabled = true;
        this.hideConfigurator();
    },
 
    applyPanel: function (panel, oldPanel) {
        if (panel) {
            panel = panel.isInstance ? panel : Ext.create(panel);
        }
 
        return panel;
    },
 
    updatePanel: function (panel, oldPanel) {
        var me = this,
            pivot = this.getPivot();
 
        Ext.destroy(oldPanel, me.panelListeners);
 
        if (panel) {
            me.panelListeners = panel.on({
                hiddenchange: 'onPanelHiddenChange',
                close: 'hideConfigurator',
                scope: me,
                destroyable: true
            });
 
            panel.setConfig({
                pivot: pivot,
                fields: me.getFields()
            });
 
            pivot.add(panel);
        }
    },
 
    onPanelHiddenChange: function (panel, hidden) {
        this.getPivot().fireEvent(hidden ? 'hideconfigpanel' : 'showconfigpanel', panel);
    },
 
    updatePivot: function (pivot, oldPivot) {
        var me = this;
 
        Ext.destroy(me.pivotListeners);
 
        if (oldPivot) {
            oldPivot.showConfigurator = oldPivot.hideConfigurator = null;
        }
 
        if (pivot) {
            //<debug>
            // this plugin is only available for the pivot components
            if (!pivot.isPivotComponent) {
                Ext.raise('This plugin is only compatible with pivot components');
            }
            //</debug>
            pivot.showConfigurator = Ext.bind(me.showConfigurator, me);
            pivot.hideConfigurator = Ext.bind(me.hideConfigurator, me);
 
            if (pivot.initialized) {
                me.onPivotInitialized();
            } else {
                pivot.on({
                    initialize: 'onPivotInitialized',
                    single: true,
                    scope: me
                });
            }
        }
    },
 
    getWidth: function () {
        var pivot = this.getPivot(),
            viewport = Ext.Viewport,
            maxWidth = 100;
 
        if (pivot && pivot.element) {
            maxWidth = pivot.element.getWidth();
        }
        if (viewport) {
            maxWidth = Math.min(maxWidth, viewport.element.getHeight(), viewport.element.getWidth());
        }
 
        return Ext.Number.constrain(this._width, 100, maxWidth);
    },
 
    /**
     * @private
     */
    onPivotInitialized: function () {
        var me = this,
            pivot = me.getPivot(),
            fields = me.getFields(),
            matrix = pivot.getMatrix(),
            header = pivot.getHeaderContainer && pivot.getHeaderContainer(),
            fieldsToUpdate = [],
            duplicates = {},
            noFields = false,
            store, newFields, field, name, length, i, dim, config;
 
        if (fields.length === 0 && matrix instanceof Ext.pivot.matrix.Local) {
            // if no fields were provided then try to extract them from the matrix store
            noFields = true;
            store = matrix.store;
            newFields = store ? store.model.getFields() : [];
            length = newFields.length;
 
            for (= 0; i < length; i++) {
                name = newFields[i].getName();
 
                if (!fields.byDataIndex.get(name)) {
                    fields.add({
                        header: Ext.String.capitalize(name),
                        dataIndex: name
                    });
                }
            }
        }
 
        // extract fields from the existing pivot configuration
        newFields = Ext.Array.merge(matrix.leftAxis.dimensions.getRange(), matrix.topAxis.dimensions.getRange(), matrix.aggregate.getRange());
        length = newFields.length;
        for (= 0; i < length; i++) {
            dim = newFields[i].getConfig();
            delete(dim.matrix);
            delete(dim.values);
            delete(dim.id);
            field = fields.byDataIndex.get(dim.dataIndex);
            if (!field) {
                fields.add(dim);
            } else if (noFields) {
                if (!duplicates[dim.dataIndex]) {
                    duplicates[dim.dataIndex] = 0;
                }
                delete(dim.header);
                duplicates[dim.dataIndex]++;
                fieldsToUpdate.push(dim);
            }
        }
 
        // Some fields defined on the pivot axis already exist in the configurator fields
        // so we need to update the configurator fields for later usage.
        // This is important because the dimensions may have labelRenderer/renderer/formatter defined
        // This happens only when no fields were defined on the Configurator plugin.
        length = fieldsToUpdate.length;
        for (= 0; i < length; i++) {
            dim = fieldsToUpdate[i];
            if (duplicates[dim.dataIndex] === 1) {
                field = fields.byDataIndex.get(dim.dataIndex);
                if (field) {
                    config = field.getConfig();
                    Ext.merge(config, dim);
                    field.setConfig(config);
                }
            }
        }
 
        me.isReady = true;
        me.doneSetup = false;
 
        if (header) {
            me.pivotListeners = header.renderElement.on({
                longpress: 'showConfigurator',
                scope: this
            });
        }
    },
 
    /**
     * @private
     */
    hideConfigurator: function () {
        var panel = this.getPanel();
 
        if (panel) {
            panel.hide();
        }
    },
 
    /**
     * @private
     */
    showConfigurator: function () {
        var panel = this.getPanel();
 
        if (panel) {
            panel.setWidth(this.getWidth());
            panel.show();
        }
    },
 
    getFields: function () {
        var ret = this._fields;
 
        if (!ret) {
            ret = new Ext.util.Collection({
                extraKeys: {
                    byDataIndex: 'dataIndex'
                },
                decoder: function (field) {
                    return (field && field.isField) ? field : new Ext.pivot.plugin.configurator.Field(field || {});
                }
            });
            this.setFields(ret);
        }
 
        return ret;
    },
 
    applyFields: function (fields, fieldsCollection) {
        if (fields == null || (fields && fields.isCollection)) {
            return fields;
        }
 
        if (fields) {
            if (!fieldsCollection) {
                fieldsCollection = this.getFields();
            }
 
            fieldsCollection.splice(0, fieldsCollection.length, fields);
        }
 
        return fieldsCollection;
    },
 
    deprecated: {
        '6.5': {
            configs: {
                panelWrapper: null,
                panelWrap: null
            }
        }
    }
 
});