# Source: core/meta/model-group.js

    var Montage = require("../core").Montage,
        ModelModule = require("./model"),
        ObjectProperty = require("./object-property").ObjectProperty,
        deprecate = require("../deprecate");

    /**
     * @class ModelGroup
     * @classdesc A model group is a singleton that is responsible for
     * loading and dispatching object models and descriptors.
     *
     * @extends Montage
     */
    exports.ModelGroup = Montage.specialize( /** @lends ModelGroup.prototype # */ {

        /**
         * @constructs ModelGroup
         */
        constructor: {
            value: function ModelGroup() {
                this._models = [];
                this._modelTable = {};
            }
        },

        _name: {
            value: null
        },

        /**
         * Name of the ModelGroup.
         * The name is used to define the property on the object.
         * @function
         * @type {string}
         */
        name: {
            get: function () {
                return this._name;
            },
            set: function (value) {
                if(value !== this._name) {
                    this._name = value;
                }
            }
        },

        /**
         * @private
         * @property {Array} value
         */
        _models: {
            value: null
        },

        /**
         * @private
         */
        _modelTable: {
            value: null
        },

        /**
         * Return the list of binder registered on the manager.
         *
         * @readonly
         * @returns {Array.<Binder>}
         */
        models: {
            get: function () {
                return this._models;
            }
        },

        /**
         * Adds an object model to the model group.
         *
         * @function
         * @param {Model} model
         */
        addModel: {
            value: function (model) {
                var index;
                if (model !== null) {
                    if (this._modelTable[model.name]) {
                        this.removeModel(this._modelTable[model.name]);
                    }
                    index = this._models.indexOf(model);
                    if (index >= 0) {
                        this._models.splice(index, 1);
                    }
                    this._models.push(model);
                    this._modelTable[model.name] = model;
                }
            }
        },

        /**
         * @function
         * @param {Model} model
         */
        removeModel: {
            value: function (model) {
                var index;
                if (model !== null) {
                    index = this._models.indexOf(model);
                    if (index >= 0) {
                        this._models.splice(index, 1);
                    }
                    if (this._modelTable[model.name]) {
                        delete this._modelTable[model.name];
                    }
                }
            }
        },

        /**
         * Gets the object model associated with the name.
         * @param {string} name
         */
        modelForName: {
            value: function (name) {
                return this._modelTable[name];
            }
        },

        /**
         * Search through the models for an object descriptor that extends
         * the provided prototype.
         * @function
         * @param {string} prototypeName
         * @param {string} moduleId
         * @returns The requested object descriptor or null if this prototype is not
         * found.
         */
        objectDescriptorForPrototype: {
            value: function (prototypeName, moduleId /* unused */) {
                var objectDescriptor = null, model, index;
                for (index = 0; typeof (model = this.models[index]) !== "undefined" && !objectDescriptor; index++) {
                    objectDescriptor = model.objectDescriptorForName(prototypeName);
                }
                return objectDescriptor;
            }
        },

        /**
         * @private
         */
        _defaultObjectDescriptorObjectProperty: {
            serializable: true,
            value: null
        },

        /**
         * Return the default object descriptor's object property.
         * This is the last resort property declaration object.
         *
         * @readonly
         * @returns {ObjectProperty} default object descriptor object property
         */
        defaultObjectDescriptorObjectProperty: {
            get: function () {
                if (!this._defaultObjectDescriptorObjectProperty) {
                    this._defaultObjectDescriptorObjectProperty = new ObjectProperty().init();
                }
                return this._defaultObjectDescriptorObjectProperty;
            }
        },

        _defaultModel: {
            serializable: true,
            value: null
        },

        /**
         * Return the default model.
         * This is the last resort property declaration object.
         *
         * @readonly
         * @returns {Model} default object descriptor model
         */
        defaultModel: {
            get: function () {
                if (!this._defaultModel) {
                    var _require = global.mr || require; // Handle legacy
                    this._defaultModel = new ModelModule.Model().initWithNameAndRequire("default", _require);
                    this._defaultModel.isDefault = true;
                    this.addModel(this._defaultModel);
                }
                return this._defaultModel;
            }
        },

        /******************************************************************************
         * Deprecated Methods
         */

        /**
         * Return the list of models registered on the group.
         * @deprecated
         * @readonly
         * @returns {Array.<Binder>}
         */
        binders: {
            get: deprecate.deprecateMethod(void 0, function () {
                return this.models;
            }, "binders", "models")
        },

        /**
         * Add a new model
         * @deprecated
         * @function
         * @param {Model} model
         */
        addBinder: {
            value: deprecate.deprecateMethod(void 0, function (binder) {
                this.addModel(binder);
            }, "addBinder", "addModel")
        },

        /**
         * @deprecated
         * @function
         * @param {Binder} binder
         */
        removeBinder: {
            value: deprecate.deprecateMethod(void 0, function (binder) {
                return this.removeModel(binder);
            }, "removeBinder", "removeModel")
        },

        /**
         * @deprecated
         * Gets the model associated with the name.
         * @param {string} name
         */
        binderForName: {
            value: deprecate.deprecateMethod(void 0, function (name) {
                // return this._binderTable[name];
                return this.modelForName(name);
            }, "binderForName", "modelForName")
        },

        /**
         * Search through the binders for an object descriptor that extends that prototype.
         * @deprecated
         * @function
         * @param {string} prototypeName
         * @param {string} moduleId
         * @returns The requested object descriptor or null if this prototype is not
         * managed.
         */
        blueprintForPrototype: {
            value: deprecate.deprecateMethod(void 0, function (prototypeName, moduleId) {
                return this.objectDescriptorForPrototype(prototypeName);
            }, "blueprintForPrototype", "objectDescriptorForPrototype")
        },

        /**
         * Return the default object property.
         * This is the last resort property declaration object.
         * @deprecated
         * @readonly
         * @returns {ObjectProperty} default object property
         */
        defaultBlueprintObjectProperty: {
            get: deprecate.deprecateMethod(void 0, function () {
                return this.defaultObjectDescriptorObjectProperty;
            }, "defaultBlueprintObjectProperty", "defaultObjectDescriptorObjectProperty")
        },

        /**
         * Return the default model.
         *
         * @readonly
         * @returns {Model} default model
         */
        defaultBinder: {
            get: deprecate.deprecateMethod(void 0, function () {
                return this.defaultModel;
            }, "defaultBinder", "defaultModel")
        }

    });