# Source: core/meta/model.js

    var Montage = require("../core").Montage,
        ModelGroup = require("./model-group").ModelGroup,
        ObjectDescriptorModule = require("./object-descriptor"),
        deprecate = require("../deprecate"),
        application = require("../application").application;
        var _group = null;

    /**
     * @class Model
     * @classdesc A Model represents a logical collection
     *
     * @extends Montage
     */
    var Model = exports.Model = Montage.specialize( /** @lends Model.prototype # */ {

        /**
         * @constructs Model
         */
        constructor: {
            value: function Model() {
                this._name = null;
                this.modelModuleId = null;
                this.isDefault = false;
                this._objectDescriptorForPrototypeTable = {};
                return this;
            }
        },

        /**
         * @function
         * @param {string} name
         * @returns itself
         */
        initWithNameAndRequire: {
            value: function (name, _require) {
                if (!name) {
                    throw new Error("name is required");
                } else if (!_require) {
                    throw new Error("require is required");
                }

                this._name = name;
                this._require = _require;
                Model.group.addModel(this);
                return this;
            }
        },

        serializeSelf: {
            value: function (serializer) {

                if (this.version > 1) {
                    serializer.setProperty("version", this.version);
                }

                serializer.setProperty("name", this.name);
                if (this.objectDescriptors.length > 0) {
                    serializer.setProperty("objectDescriptors", this.objectDescriptors);
                }
                serializer.setProperty("objectModelModuleId", this.modelInstanceModuleId);
            }
        },

        deserializeSelf: {
            value: function (deserializer) {
                var value = deserializer.getProperty("version");
                if (value !== void 0) {
                    this.version = value;
                }

                value = deserializer.getProperty("name");
                if (value !== void 0) {
                    this._name = value;
                }
                //copy contents into the objectDescriptors array
                value = deserializer.getProperty("objectDescriptors") || deserializer.getProperty("blueprints");
                if (value) {
                    this._objectDescriptors = value;
                }
                value = deserializer.getProperty("objectModelModuleId") || deserializer.getProperty("binderModuleId");
                if (value !== void 0) {
                    this.modelInstanceModuleId = value;
                }
            }
        },

        _name: {
            value: null
        },

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

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

        /**
         * Require for the binder.
         * All objectDescriptors added must be in this require's package, or in a direct
         * dependency.
         * @readonly
         * @returns {function} a package's `require` function
         */
        require: {
            get: function () {
                return this._require;
            }
        },

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

        /**
         * The identifier is the name of the binder and is used to make the
         * serialization of binders more readable.
         * @returns {string}
         */
        identifier: {
            get: function () {
                return [
                    "objectModel",
                    this.name.toLowerCase()
                ].join("_");
            }
        },

        /**
         * This is used for references only so that we can reload referenced
         * binders.
         */
        modelInstanceModuleId: {
            serializable:false,
            value: null
        },

        /**
         * Identify the default binder. Do not set.
         * @readonly
         * @type {boolean}
         */
        isDefault: {
            serializable: false,
            value: false
        },

        _objectDescriptors: {
            value: null
        },

        /**
         * The list of objectDescriptors in this binder.
         * @readonly
         * @returns {Array.<ObjectDescriptor>}
         */
        objectDescriptors: {
            get: function () {
                return this._objectDescriptors || (this._objectDescriptors = []);
            }
        },

        /**
         * @function
         * @param {?ObjectDescriptor} objectDescriptor
         * @returns objectDescriptor
         */
        addObjectDescriptor: {
            value: function (objectDescriptor) {
                if (objectDescriptor !== null) {
                    var index = this.objectDescriptors.indexOf(objectDescriptor);
                    if (index < 0) {
                        if ((objectDescriptor.model !== null) && (objectDescriptor.model !== this)) {
                            objectDescriptor.model.removeObjectDescriptor(objectDescriptor);
                        }
                        this.objectDescriptors.push(objectDescriptor);
                        objectDescriptor.model = this;
                    }
                }
                return objectDescriptor;
            }
        },

        /**
         * @function
         * @param {ObjectDescriptor} objectDescriptor
         * @returns objectDescriptor
         */
        removeObjectDescriptor: {
            value: function (objectDescriptor) {
                if (objectDescriptor !== null) {
                    var index = this.objectDescriptors.indexOf(objectDescriptor);
                    if (index >= 0) {
                        this.objectDescriptors.splice(index, 1);
                        objectDescriptor.model = null;
                    }
                }
                return objectDescriptor;
            }
        },

        /**
         * @function
         * @param {string} name
         * @param {string} moduleID
         * @returns {ObjectDescriptor} The new objectDescriptor
         */
        addObjectDescriptorNamed: {
            value: function (name) {
                return this.addObjectDescriptor(new ObjectDescriptorModule.ObjectDescriptor().initWithName(name));
            }
        },

        /**
         * Return the object descriptor associated with this prototype.
         * @function
         * @param {string} prototypeName
         * @param {string} moduleId
         * @returns {?ObjectDescriptor} objectDescriptor
         */
        objectDescriptorForPrototype: {
            value: deprecate.deprecateMethod(void 0, function (prototypeName) {
                return this.objectDescriptorForName(prototypeName);
            }, "objectDescriptorForPrototype", "objectDescriptorForName")
        },

        /**
         *
         * @param {string} name
         * @returns {?ObjectDescriptor} if this model has an object descriptor
         * with the provided name.  Otherwise, returns null.
         */
        objectDescriptorForName: {
            value: function (name) {
                var objectDescriptors = this.objectDescriptors,
                    objectDescriptor = null,
                    length = objectDescriptors.length;
                for (var i = 0; i < length && !objectDescriptor; i++) {
                    if (objectDescriptors[i].name === name) {
                        objectDescriptor = objectDescriptors[i];
                    }
                }
                return objectDescriptor;
            }
        },

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

        /**
         * Return the object descriptor object property for this model.
         * This will return the default if none is declared.
         * @type {ObjectProperty}
         */
        ObjectProperty: {
            get: function () {
                if (!this._objectDescriptorObjectProperty) {
                    this._objectDescriptorObjectProperty = Model.group.defaultObjectDescriptorObjectProperty;
                }
                return this._objectDescriptorObjectProperty;
            }
        },

        objectDescriptorModuleId: require("../core")._objectDescriptorModuleIdDescriptor,
        objectDescriptor: require("../core")._objectDescriptorDescriptor,

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

        /**
         * The list of object descriptors in this model.
         * @deprecated
         * @readonly
         * @returns {Array.<ObjectDescriptor>}
         */
        blueprints: {
            get: deprecate.deprecateMethod(void 0, function () {
                return this.objectDescriptors;
            }, "blueprints", "objectDescriptors")
        },

        /**
         * @deprecated
         * @function
         * @param {?ObjectDescriptor} objectDescriptor
         * @returns objectDescriptor
         */
        addBlueprint: {
            value: deprecate.deprecateMethod(void 0, function (blueprint) {
                return this.addObjectDescriptor(blueprint);
            }, "addBlueprint", "addObjectDescriptor")
        },

        /**
         * @deprecated
         * @function
         * @param {ObjectDescriptor} objectDescriptor
         * @returns objectDescriptor
         */
        removeBlueprint: {
            value: deprecate.deprecateMethod(void 0, function (blueprint) {
                return this.removeObjectDescriptor(blueprint);
            }, "removeBlueprint", "removeObjectDescriptor")
        },

        /**
         * @deprecated
         * @function
         * @param {string} name
         * @param {string} moduleID
         * @returns {ObjectDescriptor} The new objectDescriptor
         */
        addBlueprintNamed: {
            value: deprecate.deprecateMethod(void 0, function (name) {
                return this.addObjectDescriptorNamed(name);
            }, "addBlueprintNamed", "addObjectDescriptorNamed")
        },

        /**
         * @deprecated
         * Return the objectDescriptor associated with this prototype.
         * @function
         * @param {string} prototypeName
         * @param {string} moduleId
         * @returns {?ObjectDescriptor} objectDescriptor
         */
        blueprintForPrototype: {
            value: deprecate.deprecateMethod(void 0, function (prototypeName) {
                return this.blueprintForName(prototypeName);
            }, "blueprintForPrototype", "blueprintForName")
        },

        /**
         * @deprecated
         * @param {string} name
         * @returns {?ObjectDescriptor}
         */
        blueprintForName: {
            value: deprecate.deprecateMethod(void 0, function (name) {
                return this.objectDescriptorForName(name);
            }, "blueprintForName", "objectDescriptorForName")
        },

        blueprintModuleId: require("../core")._objectDescriptorModuleIdDescriptor,
        blueprint: require("../core")._objectDescriptorDescriptor

    }, {

        /**
         * Returns the model group.
         * @returns {ModelGroup}
         */
        group: {
            get: function () {
                if (_group === null) {
                    _group = new ModelGroup();
                    _group.name = application ? application.name : "";
                }
                return _group;
            }
        },

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

        /**
         * @deprecated
         * Returns the model group.
         * @returns {ModelGroup}
         */
        manager: {
            get: deprecate.deprecateMethod(void 0, function () {
                return exports.Model.group;
            }, "Binder.manager", "Model.group")
        }

    });