/**
 *
 * This plugin allows the user to modify records behind a pivot cell.
 *
 * The user has to double click that cell to open the range editor window.
 *
 * The following types of range editing are available:
 *
 * - `percentage`: the user fills in a percentage that is applied to each record.
 * - `increment`:  the user fills in a value that is added to each record.
 * - `overwrite`:  the new value filled in by the user overwrites each record.
 * - `uniform`:  replace sum of values with a provided value using uniform distribution
 *
 * More pivot updater types can be defined by extending {@link Ext.pivot.update.Base}.
 *
 * **Note:** Only works when using a {@link Ext.pivot.matrix.Local} matrix on a pivot grid.
 */
Ext.define('Ext.pivot.plugin.RangeEditor', {
    alternateClassName: [
        'Mz.pivot.plugin.RangeEditor'
    ],
 
    alias: [
        'plugin.pivotrangeeditor',
        'plugin.mzrangeeditor'
    ],
 
    extend: 'Ext.plugin.Abstract',
 
    requires: [
        'Ext.pivot.Grid',
        'Ext.pivot.update.Increment',
        'Ext.pivot.update.Overwrite',
        'Ext.pivot.update.Percentage',
        'Ext.pivot.update.Uniform',
        'Ext.window.Window',
        'Ext.form.field.Hidden',
        'Ext.form.field.Text',
        'Ext.form.field.Number',
        'Ext.form.field.ComboBox',
        'Ext.form.field.Display',
        'Ext.form.Panel',
        'Ext.button.Button',
        'Ext.layout.container.Fit',
        'Ext.data.ArrayStore'
    ],
 
    /**
     * Fires on the pivot component before updating all result records.
     *
     * @event pivotbeforeupdate
     * @param {Ext.pivot.update.Base} updater Reference to the updater object
     */
 
    /**
     * Fires on the pivot component after updating all result records.
     *
     * @event pivotupdate
     * @param {Ext.pivot.update.Base} updater Reference to the updater object
     */
 
    /**
     * Fired on the pivot component when the range editor window is visible
     *
     * @event showrangeeditorpanel
     * @param {Ext.window.Window} panel Range editor window
     */
 
    /**
     * Fired on the pivot component when the range editor window is hidden
     *
     * @event hiderangeeditorpanel
     * @param {Ext.window.Window} panel Range editor window
     */
 
    /**
     * @cfg {Number} width Width of the viewer's window.
     */
    width: null,
 
    /**
     * @cfg {Number} height Height of the viewer's window.
     */
    height: null,
 
    /**
     * @cfg {String} textWindowTitle Range editor window title
     */
    textWindowTitle: 'Range editor',
 
    /**
     * @cfg {String} textFieldValue Range editor field Value label
     */
    textFieldValue: 'Value',
 
    /**
     * @cfg {String} textFieldEdit Range editor field Edit label
     */
    textFieldEdit: 'Field',
 
    /**
     * @cfg {String} textFieldType Range editor field Type label
     */
    textFieldType: 'Type',
 
    /**
     * @cfg {String} textButtonOk Range editor window Ok button text
     */
    textButtonOk: 'Ok',
 
    /**
     * @cfg {String} textButtonCancel Range editor window Cancel button text
     */
    textButtonCancel: 'Cancel',
 
    /**
     * @cfg {Function} onBeforeRecordsUpdate
     *
     * Provide a function to handle the records update.
     *
     * This one will be fired before updating the records. Return false if you want to stop
     * the process.
     *
     * The function receives the following arguments: pivot, colDefinition, records, newValue,
     * oldValue.
     */
    onBeforeRecordsUpdate: Ext.emptyFn,
 
    /**
     * @cfg {Function} onAfterRecordsUpdate
     *
     * Provide a function to handle the records update.
     *
     * This one will be fired after all records were updated. "sync" could be called on the store
     * inside this function.
 *
     * The function receives the following arguments: pivot, colDefinition, records, newValue,
     * oldValue.
     */
    onAfterRecordsUpdate: Ext.emptyFn,
 
    /**
     * @cfg {Object} scope
     *
     * Scope to run the functions `onBeforeRecordsUpdate` and `onAfterRecordsUpdate`.
     */
    scope: null,
 
    /**
     * @cfg {Array} updaters
     *
     * Define here the updaters available for the user.
     */
    updaters: [
        ['percentage', 'Percentage'],
        ['increment', 'Increment'],
        ['overwrite', 'Overwrite'],
        ['uniform', 'Uniform']
    ],
 
    /**
     * @cfg {String} defaultUpdater
     *
     * Define which updater is selected by default.
     */
    defaultUpdater: 'uniform',
 
    /**
     *  `"both"` (the default) - The plugin is added to both grids
     *  `"top"` - The plugin is added to the containing Panel
     *  `"locked"` - The plugin is added to the locked (left) grid
     *  `"normal"` - The plugin is added to the normal (right) grid
     *
     * @private
     */
    lockableScope: 'top',
 
    config: {
        /**
         * @private
         */
        grid: null,
 
        /**
         * @private
         */
        view: null
    },
 
    init: function(grid) {
        //<debug>
        // this plugin is available only for the pivot grid
        if (!grid.isPivotGrid) {
            Ext.raise('This plugin is only compatible with Ext.pivot.Grid');
        }
        //</debug>
 
        this.setGrid(grid);
 
        return this.callParent([grid]);
    },
 
    destroy: function() {
        this.setConfig({
            grid: null,
            view: null
        });
        this.cleanUp();
        this.callParent();
    },
 
    updateGrid: function(grid) {
        Ext.destroy(this.gridListeners);
 
        if (grid) {
            this.gridListeners = grid.on({
                pivotitemcelldblclick: 'showPanel',
                pivotgroupcelldblclick: 'showPanel',
                pivottotalcelldblclick: 'showPanel',
                scope: this,
                destroyable: true
            });
        }
    },
 
    updateView: function(view, oldView) {
        Ext.destroy(oldView);
    },
 
    showPanel: function(params, e, eOpts) {
        var me = this,
            grid = me.getGrid(),
            matrix = grid.getMatrix(),
            dataIndex, result, col, view;
 
        // do nothing if the plugin is disabled
        if (me.disabled) {
            return;
        }
 
        result = matrix.results.get(params.leftKey, params.topKey);
 
        if (!result) {
            return;
        }
 
        // create the window that will show the records
        view = me.createPanel();
 
        // to keep compatibility with prior versions of this plugin
        me.currentCol = col = params.column;
        me.currentRecords = result.records || [];
        dataIndex = col.dimension.getId();
        me.currentValue = result.getValue(dataIndex);
 
        view.down('form').getForm().setValues({
            leftKey: params.leftKey,
            topKey: params.topKey,
            dataIndex: col.dimension.dataIndex,
            field: col.dimension.header || col.text || col.dimension.dataIndex,
            value: result.getValue(dataIndex),
            type: me.defaultUpdater
        });
        view.show();
        me.setView(view);
        grid.fireEvent('showrangeeditorpanel', view);
    },
 
    createPanel: function() {
        var me = this;
 
        return new Ext.window.Window({
            title: me.textWindowTitle,
            width: me.width,
            height: me.height,
            layout: 'fit',
            modal: true,
            closeAction: 'hide',
            items: [{
                xtype: 'form',
                padding: 5,
                border: false,
                defaults: {
                    anchor: '100%'
                },
                items: [{
                    fieldLabel: me.textFieldEdit,
                    xtype: 'displayfield',
                    name: 'field'
                }, {
                    fieldLabel: me.textFieldType,
                    xtype: 'combo',
                    name: 'type',
                    queryMode: 'local',
                    valueField: 'id',
                    displayField: 'text',
                    editable: false,
                    store: me.updaters
                }, {
                    fieldLabel: me.textFieldValue,
                    xtype: 'numberfield',
                    name: 'value'
                }, {
                    xtype: 'hidden',
                    name: 'leftKey'
                }, {
                    xtype: 'hidden',
                    name: 'topKey'
                }, {
                    xtype: 'hidden',
                    name: 'dataIndex'
                }]
            }],
            buttons: [{
                text: me.textButtonOk,
                handler: me.updateRecords,
                scope: me
            }, {
                text: me.textButtonCancel,
                handler: me.closePanel,
                scope: me
            }],
 
            listeners: {
                close: 'onClosePanel',
                scope: me
            }
        });
    },
 
    closePanel: function() {
        this.getView().close();
    },
 
    onClosePanel: function() {
        var view = this.getView();
 
        this.getGrid().fireEvent('hiderangeeditorpanel', view);
        this.setView(null);
    },
 
    updateRecords: function() {
        var me = this,
            view = me.getView(),
            values = view.down('form').getValues(),
            fn = Ext.bind(me.cleanUp, me),
            updater;
 
        values.matrix = me.getGrid().getMatrix();
        updater = Ext.Factory.pivotupdate(values);
        updater.on({
            beforeupdate: me.onBeforeUpdate,
            update: me.onUpdate,
            scope: me
        });
        updater.update().then(fn, fn);
    },
 
    cleanUp: function() {
        this.currentCol = this.currentRecords = this.currentValue = null;
    },
 
    onBeforeUpdate: function(updater) {
        var me = this,
            pivot = me.getGrid(),
            ret;
 
        ret = Ext.callback(
            me.onBeforeRecordsUpdate, me.scope || 'self.controller',
            [pivot, me.currentCol, me.currentRecords, updater.getValue(), me.currentValue], 0,
            pivot
        );
 
        if (ret === false) {
            return false;
        }
 
        if (pivot.fireEvent('pivotbeforeupdate', updater) === false) {
            return false;
        }
 
        me.getView().getEl().mask();
    },
 
    onUpdate: function(updater) {
        var me = this,
            pivot = me.getGrid(),
            view = me.getView();
 
        Ext.callback(
            me.onAfterRecordsUpdate, me.scope || 'self.controller',
            [pivot, me.currentCol, me.currentRecords, updater.getValue(), me.currentValue],
            0, pivot
        );
 
        pivot.fireEvent('pivotupdate', updater);
        Ext.destroy(updater);
        view.getEl().unmask();
        view.close();
    }
});