/**
 * 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.
 */
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 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: {
            xtype: 'pivotconfigpanel',
            docked: 'right'
        },
        /**
         * @cfg {Object} panelWrapper 
         *
         * Configuration object used to wrap the configurator panel when needed.
         */
        panelWrapper: {
            xtype: 'sheet',
            right: 0,
            stretchY: true,
            layout: 'fit',
            hideOnMaskTap: true,
            enter: 'right',
            exit: 'right',
            style: {
                padding: 0
            }
        },
        /**
         * @cfg {Boolean} panelWrap 
         *
         * Enable or disable the configurator panel wrapper.
         */
        panelWrap: true,
        /**
         * @private
         */
        pivot: null,
        /**
         * Reference to the configurator panel.
         * @private
         */
        view: null
    },
 
    init: function(pivot) {
        this.setPivot(pivot);
    },
 
    /**
     * @private
     * AbstractComponent calls destroy on all its plugins at destroy time.
     */
    destroy: function() {
        this.setConfig({
            pivot: null,
            view: 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();
    },
 
    updateView: function(view, oldView){
        var me = this,
            panel;
 
        Ext.destroy(oldView, me.panelListeners);
 
        if(view){
            panel = view.isXType('pivotconfigpanel') ? view : view.down('pivotconfigpanel');
 
            if(panel) {
                me.panelListeners = panel.on({
                    close: 'hideConfigurator',
                    scope: me
                });
 
                panel.setConfig({
                    pivot: me.getPivot(),
                    fields: me.getFields()
                });
            }
            //<debug> 
            else{
                Ext.raise('Wrong panel configuration! No "pivotconfigpanel" component available.');
            }
            //</debug> 
 
        }
    },
 
    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 pivot = this.getPivot(),
            view;
 
        this.setup();
        view = this.getView();
 
        view.translate(this.getWidth(), 0, {duration: 100});
        view.getTranslatable().on('animationend', function() {
            view.hide(null);
            pivot.fireEvent('hideconfigpanel', view);
        }, this, {single: true});
    },
 
    /**
     * @private
     */
    showConfigurator: function() {
        var me = this,
            view;
 
        me.setup();
        view = me.getView();
 
        view.setWidth(me.getWidth());
        view.show();
        view.translate(0, 0, {duration: 100});
        this.getPivot().fireEvent('showconfigpanel', view);
    },
 
    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;
    },
 
    privates: {
        setup: function () {
            var me = this,
                view, panel;
 
            if (me.doneSetup || !me.isReady) {
                return;
            }
            me.doneSetup = true;
 
            if(me.getPanelWrap()){
                view = me.getPanelWrapper();
                if(!view.items){
                    panel = me.getPanel();
                    panel.docked = null;
                    view.items = [panel];
                }
            }else{
                view = me.getPanel();
            }
 
            me.setView(me.getPivot().add(view));
        }
    }
 
 
});