(function () {
    var Ext = window.Ext4 || window.Ext;
    /**
     * @private
     * A dialog that provides an easy way to bulk edit artifacts.
     *
     *      @example
     *      Ext.create('Rally.ui.dialog.BulkEditDialog', {
     *
     *      });
     */
    Ext.define('Rally.ui.dialog.BulkEditDialog', {
        extend: 'Rally.ui.dialog.Dialog',
        alias: 'widget.rallybulkeditdialog',

        requires: [
            'Rally.ui.Button',
            'Rally.ui.MilestoneTargetProjectPermissionsHelper',
            'Rally.ui.combobox.ComboBox',
            'Rally.ui.grid.FieldColumnFactory'
        ],

        statics: {
            BLACKLISTED_FIELDS_ACROSS_MODELS: ['State', 'ScheduleState']
        },

        autoShow: true,
        cls: 'bulk-edit-dialog',
        closable: true,
        draggable: true,
        title: 'Bulk Edit...',
        width: 375,

        clientMetrics: [{
            event: 'beforeshow',
            description: 'dialog invoked'
        }, {
            method: '_onFieldSelect',
            description: 'edit field selected'
        }, {
            event: 'edit',
            description: 'edit performed'
        }],

        config: {
            /**
             * @cfg {[Rally.data.Model]} records (required)
             * The records to bulk edit
             */
            records: null
        },

        initComponent: function() {
            this.callParent(arguments);

            this.addEvents(
                /**
                 * @param Rally.ui.dialog.BulkEditDialog the dialog
                 * @param Rally.data.wsapi.Field field the field being edited
                 * @param {String|Number} the new value
                 */
                'edit'
            );

            this.add(
                {
                    xtype: 'component',
                    cls: 'directions rui-info-label',
                    renderTpl: Ext.create('Ext.XTemplate',
                        'For the <tpl><b>{[values.recordCount]}</b></tpl> checked ',
                        '<tpl if="recordCount === 1">item<tpl else>items</tpl> apply the following values:'
                    ),
                    renderData: {
                        recordCount: this.records.length
                    }
                },
                {
                    xtype: 'container',
                    itemId: 'form-container',
                    cls: 'form-container',
                    items: [
                        {
                            xtype: 'rallycombobox',
                            autoExpand: true,
                            itemId: 'editField',
                            store: Ext.create('Ext.data.Store', {
                                fields: ['name', 'displayName'],
                                data: this._getValidFields()
                            }),
                            defaultSelectionPosition: null,
                            displayField: 'displayName',
                            emptyText: 'Choose Field...',
                            valueField: 'name',
                            listeners: {
                                select: this._onFieldSelect,
                                scope: this
                            }
                        },
                        {
                            xtype: 'component',
                            cls: 'text',
                            html: ' to '
                        },
                        {
                            xtype: 'rallycombobox',
                            disabled: true,
                            itemId: 'editValue',
                            store: Ext.create('Ext.data.Store', {
                                fields: []
                            }),
                            emptyText: 'Choose Value...'
                        }
                    ]
                }
            );

            this.addDocked({
                xtype: 'toolbar',
                dock: 'bottom',
                padding: '0 0 10 0',
                layout: {
                    type: 'hbox',
                    pack: 'center'
                },
                ui: 'footer',
                items: [
                    {
                        xtype: 'rallybutton',
                        itemId: 'applyButton',
                        text: 'Apply',
                        cls: 'primary rly-small',
                        disabled: true,
                        handler:  this._onApplyClicked,
                        scope: this
                    },
                    {
                        xtype: 'rallybutton',
                        text: 'Cancel',
                        cls: 'secondary rly-small',
                        handler: function() {
                            this.close();
                        },
                        scope: this
                    }
                ]
            });
        },

        afterRender: function() {
            this.callParent(arguments);
            this.down('#editField').focus(false, 150);
        },

        _onApplyClicked: function() {
            var valueField = this.down('#editValue');
            var args = {
                field: this.records[0].self.getField(this.down('#editField').getValue()),
                value: valueField.getValue()
            };
            if(Ext.isFunction(valueField.getRecord)) {
                var record = valueField.getRecord();
                args.displayValue = (record) ? record.get(valueField.displayField) : null;
            } else if(Ext.isDate(valueField.getValue())) {
               args.displayValue = valueField.getRawValue();
            }
            this.fireEvent('edit', this, args);
            this.close();
        },

        _fieldIsBulkEditable: function(field) {
            var fieldDefAttr = field.attributeDefinition;
            return field.editor
                && !field.readOnly
                && !field.hidden
                && !field.isMappedFromArtifact
                && ((fieldDefAttr.AttributeType === 'OBJECT' && fieldDefAttr.Constrained) || !_.contains(['TEXT', 'COLLECTION'], fieldDefAttr.AttributeType))
                && this._fieldCanBeEditedForAllRecords(field);
        },

        _fieldCanBeEditedForAllRecords: function(field) {
            if (field.modelType === 'milestone' && field.name === 'TargetProject') {
                return _.every(this.records, function(record) {
                    return Rally.ui.MilestoneTargetProjectPermissionsHelper.canEdit(record);
                });
            }
            return true;
        },

        _isFieldOnAllModels: function(models, field) {
            return _.every(models, function(model) {
                return model.hasField(field.name) &&
                    model.getField(field.name).attributeDefinition.AttributeType === field.attributeDefinition.AttributeType;
            });
        },

        _isFieldBlacklistedForEditingOnAnyModel: function(models, field) {
            return _.any(models, function(model) {
                return Rally.ui.grid.FieldColumnFactory.isFieldBlacklistedForEditingOnGrids(field, model);
            });
        },

        _isFieldBlacklistedForEditingAcrossModels: function(models, field) {
            return models.length > 1  && _.contains(this.self.BLACKLISTED_FIELDS_ACROSS_MODELS, field.name);
        },

        _getValidFields: function() {
            var models = _(this.records).pluck('self').unique().value();

            return _(models[0].getFields())
                .filter(this._fieldIsBulkEditable, this)
                .filter(_.curry(this._isFieldOnAllModels)(models))
                .reject(_.curry(this._isFieldBlacklistedForEditingAcrossModels.bind(this))(models))
                .reject(_.curry(this._isFieldBlacklistedForEditingOnAnyModel)(models))
                .sortBy('displayName')
                .value();
        },

        _isDropdownField: function(fieldDef) {
            return _.contains(fieldDef.editor.xtype, 'combobox') || _.contains(fieldDef.editor.xtype, 'rallyrecordcontexteditor');
        },

        _prepareDropdownEditorConfig: function(editorConfig) {
            var fetch = this._getFetchForConfig(editorConfig);
            var config = Ext.merge(editorConfig, {
                defaultSelectionPosition: null,
                editable: true,
                emptyText: 'Choose Value...',
                listeners: {
                    select: function(cmp) {
                        this.down('#applyButton').setDisabled(false);
                    },
                    scope: this
                },
                value: null
            });

            if (config.name === 'Project') {
                config.xtype = 'rallyprojectcombobox';
            } else {
                config = Ext.merge(config, {
                    storeConfig: {
                        fetch: fetch,
                        autoLoad: true,
                        context: this.records[0].self.context,
                        requester: this
                    }
                });
            }
            return config;
        },

        _getFetchForConfig: function(editorConfig) {
            var clazz = Ext.ClassManager.getByAlias('widget.' + editorConfig.xtype);
            if (clazz.prototype.config.storeConfig && clazz.prototype.config.storeConfig.fetch) {
                return clazz.prototype.config.storeConfig.fetch;
            } else {
                return [clazz.prototype.valueField, clazz.prototype.displayField];
            }
        },

        _prepareTextEditorConfig: function(editorConfig) {
            return Ext.merge(editorConfig, {
                emptyText: 'Enter Value...',
                listeners: {
                    validitychange: function (cmp, isValid) {
                        this.down('#applyButton').setDisabled(!isValid);
                    },
                    scope: this
                }
            });
        },

        _prepareEditor: function(fieldDef) {
            var editorConfig = fieldDef.editor;

            if (editorConfig.field && editorConfig.field.xtype) {
                editorConfig = editorConfig.field;
            }

            editorConfig = Ext.apply({
                itemId: 'editValue',
                width: 150,
                enableKeyEvents: true,
                listeners: {
                    keyup: function(cmp, e) {
                        if (!this.down('#applyButton').isDisabled() && e.getKey() === Ext.EventObject.ENTER) {
                            this._onApplyClicked();
                        }
                    }
                }
            }, editorConfig);

            if (this._isDropdownField(fieldDef)) {
                this.down('#applyButton').setDisabled(true);
                editorConfig = this._prepareDropdownEditorConfig(editorConfig);
            } else {
                this.down('#applyButton').setDisabled(fieldDef.required);
                editorConfig = this._prepareTextEditorConfig(editorConfig);
            }

            var field = this.down('#form-container').add(editorConfig);

            if (Ext.isFunction(field.setProject)) {
                field.setProject(this.records[0].self.context.project || Rally.environment.getContext().getProjectRef());
            }
        },

        _onFieldSelect: function(combo, selected) {
            var valueField = this.down('#editValue'),
                fieldDef = this.records[0].self.getField(selected[0].get(combo.valueField));

            if (valueField) {
                valueField.destroy();
            }

            this._prepareEditor(fieldDef);
        }
    });
})();