(function () {
    var Ext = window.Ext4 || window.Ext;

    /**
     * A picker that lets you select a project from any workspace.
     *
     *      @example
     *      Ext.create('Ext.Container', {
     *          items: [{
     *              xtype: 'rallyprojectpicker'
     *          }],
     *          renderTo: Ext.getBody().dom
     *      });
     *
     */
    Ext.define('Rally.ui.picker.project.ProjectPicker', {
        extend: 'Ext.form.field.Picker',
        alias: 'widget.rallyprojectpicker',
        requires: [
            'Rally.data.wsapi.Model'
        ],

        componentCls: 'rui-triggerfield rui-project-picker',

        placeholderCls: 'rui-multi-object-placeholder',

        editable: false,
        pickerOffset: [0, -1],
        matchFieldWidth: false,

        config: {
            /**
             * @cfg {String}
             * Text shown during initial load
             */
            loadingText: 'Loading...',

            /**
             * @cfg {Boolean}
             * If true, displays the projects the current user has most recently viewed.
             */
            showMostRecentlyUsedProjects: true,

            /**
             * @cfg {Boolean}
             * If true, displays the project scope up and down checkboxes below the project tree.
             * Also, the field's value becomes a map, e.g.,
             * {
             *      project: projectRecord,
             *      projectScopeUp: false,
             *      projectScopeDown: true
             * }
             */
            showProjectScopeUpAndDown: false,

            /**
             * @cfg {String}
             * If workspace _ref is supplied, we overwrite topLevelModel and topLevelStoreConfig
             * so that we only show projects under that workspace. We also do not show the top level workspace.
             */
            workspace: undefined
        },

        constructor: function (config) {
            this.mergeConfig(config);
            this.callParent([this.config]);
        },

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

            this.addEvents(
                /**
                 * @event
                 * Fired when the scope has changed.
                 */
                'change',
                /**
                 * @event
                 * Fired if an invalid project is set on this field.
                 */
                'invalidvalue'
            );

            this.tree = this._createProjectTree();
        },

        /**
         * @private
         * Required to override when extending Ext.form.field.Picker
         */
        createPicker: function () {

            var items = [];

            if (this.getShowMostRecentlyUsedProjects()) {
                this.recents = this._createMostRecentlyUsedProjects();
                items.push(this.recents);
            }

            items.push(this.tree);

            if (this.getShowProjectScopeUpAndDown()) {
                this.projectScopeUpDownField = this._createProjectScopeUpDownField();
                items.push(this.projectScopeUpDownField);
            }

            this.picker = Ext.widget('container', {
                cls: 'rui-project-picker-container',
                itemId: 'projectPickerContainer',
                floating: true,
                shadow: false,
                hidden: true,
                minWidth: 250,
                items: items
            });

            return this.picker;
        },

        _createProjectTree: function () {
            return Ext.create('Rally.ui.tree.ProjectTree', {
                workspace: this.getWorkspace(),
                autoLoadTopLevel: false,
                listeners: {
                    scope: this,
                    itemselected: this._treeItemSelected
                }
            });
        },

        _createMostRecentlyUsedProjects: function () {
            return Ext.create('Rally.ui.picker.project.MostRecentlyUsedProjects', {
                workspaceObjectID: Rally.environment.getContext().getWorkspace().ObjectID,
                listeners: {
                    select: this._mostRecentlyUsedProjectSelected,
                    scope: this
                }
            });
        },

        _createProjectScopeUpDownField: function () {
            return Ext.create('Rally.ui.picker.project.ProjectScopeUpDownField', {
                value: {
                    projectScopeUp: this.projectScopeUp,
                    projectScopeDown: this.projectScopeDown
                },
                listeners: {
                    change: function (field, value) {
                        this.projectScopeUp = value.projectScopeUp;
                        this.projectScopeDown = value.projectScopeDown;
                        this.fireEvent('change', this, this.getValue());
                    },
                    scope: this
                }
            });
        },
        _mostRecentlyUsedProjectSelected: function (project) {
            this.setSelectedRecord(project);
            this.fireEvent('select');
            this.collapse();
        },
        _treeItemSelected: function (treeItem) {
            var record = treeItem.getRecord();
            this.setSelectedRecord(record);
            this.fireEvent('select');
            this.collapse();
        },

        applyShowProjectScopeUpAndDown: function (value, oldValue) {
            return value && Rally.environment.getContext().getSubscription().ProjectHierarchyEnabled;
        },

        /**
         * Set the value for the picker.
         * @param value {Object/Rally.data.wsapi.Model/String} A scope object (with project ref, scope up and down), a project ref String, or a project record.
         */
        setValue: function (value) {

            if (value instanceof Rally.data.wsapi.Model) {
                this.setSelectedRecord(value);
                return;
            }

            if (Ext.isString(value)) {

                this.setValueForProjectRef(value);

            } else if (Ext.isObject(value)) {

                if (this.getShowProjectScopeUpAndDown()) {
                    this.projectScopeUp = value.projectScopeUp;
                    this.projectScopeDown = value.projectScopeDown;

                    if (this.projectScopeUpDownField) {
                        this.projectScopeUpDownField.setValue({
                            projectScopeUp: value.projectScopeUp,
                            projectScopeDown: value.projectScopeDown
                        });
                    }
                }

                this.setValueForProjectRef(value.project);
            }

        },

        /**
         * Overridden to return a scope object if #showProjectScopeUpAndDown is enabled, or the project ref if not.
         * Call #getSelectedRecord if you need the fully hydrated project record.
         */
        getValue: function () {
            var ref = this.getSelectedRecord() ? this.getSelectedRecord().get('_ref') : undefined;
            if (this.getShowProjectScopeUpAndDown()) {
                return {
                    project: ref,
                    projectScopeUp: this.projectScopeUp,
                    projectScopeDown: this.projectScopeDown
                };
            } else {
                return ref;
            }
        },

        /**
         * Given a ref string, loads the record and configures the project picker with it as the value.
         * @param ref ref of a project
         */
        setValueForProjectRef: function (ref) {

            Rally.data.ModelFactory.getModel({
                type: 'Project',
                success: function (model) {

                    var oid = Rally.util.Ref.getOidFromRef(ref);

                    model.load(oid, {
                        success: function (record) {
                            this.setValue(record);
                        },
                        failure: function () {
                            //if the project doesn't exist or we don't have access to it.
                            //let our owner handle the error situation.
                            this.fireEvent('invalidvalue');
                        },
                        fetch: ['Name', 'Workspace'],
                        scope: this
                    });

                },
                scope: this
            });

        },

        /**
         * Set the text displayed in the picker text input.
         * Does not set the value of the field, only what's displayed.
         * @param value
         */
        setDisplayValue: function (value) {
            if (this.inputEl && this.inputEl.dom) {
                this.inputEl.dom.value = value;
            }
        },

        /**
         * Sets the project show, however does not auto expand to the project.
         * @param {Rally.data.wsapi.Model} record
         */
        setSelectedRecord: function (record) {
            this.projectRecord = record;
            var displayValue = record && record.get('Name') || '';
            this.setDisplayValue(displayValue);
            this.fireEvent('change', this, this.getValue());
        },

        /**
         * Get the selected project
         * @return {Rally.data.wsapi.Model}
         */
        getSelectedRecord: function () {
            return this.projectRecord;
        },

        /**
         * @override
         * Overridden to wait for tree load before showing
         */
        expand: function () {
            if (this.isLoaded) {
                this.removePlaceholderText();
                this.callParent(arguments);
            } else {
                this.setPlaceholderText(this.getLoadingText());
                this.tree.loadTopLevel().then({
                    success: function () {
                        this.isLoaded = true;
                        this.expand();
                    },
                    scope: this
                });
            }
        },
        setPlaceholderText: function (text) {
            var inputEl = this.inputEl;
            inputEl.addCls(this.placeholderCls);
            this.prePlaceholderText = inputEl.dom.value;
            inputEl.dom.value = text;
        },

        removePlaceholderText: function () {
            var inputEl = this.inputEl;
            if (inputEl.hasCls(this.placeholderCls)) {
                inputEl.removeCls(this.placeholderCls);
                inputEl.dom.value = this.prePlaceholderText;
            }
        },

        getSubmitData: function () {
            var data;
            if (!this.disabled && this.submitValue) {
                data = {};
                data[this.getName()] = this.getValue();
            }
            return data;
        },

        destroy: function () {
            if (this.tree) {
                this.tree.destroy();
            }
            if (this.recents) {
                this.recents.destroy();
            }
            if (this.projectScopeUpDownField) {
                this.projectScopeUpDownField.destroy();
            }
            this.callParent(arguments);
        }
    });
})();