/**
 * This class describes a size determination strategy or algorithm used by the layout
 * system. There are special instances of this class stored as static properties to
 * avoid needless object instantiation. These instances should be treated as readonly.
 * 
 *  * `calculated`
 *  * `configured`
 *  * `constrainedMax`
 *  * `constrainedMin`
 *  * `natural`
 *  * `shrinkWrap`
 *  * `calculatedFromConfigured`
 *  * `calculatedFromNatural`
 *  * `calculatedFromShrinkWrap`
 *
 * Using one of these instances is simply:
 *
 *       var calculated = Ext.layout.SizeModel.calculated;
 *
 * @private
 */
Ext.define('Ext.layout.SizeModel', {
    constructor: function (config) {
        var me = this,
            SizeModel = me.self,
            sizeModelsArray = SizeModel.sizeModelsArray,
            name;

        Ext.apply(me, config);

        me[name = me.name] = true; // set the one special flag that matches our name

        me.fixed = !(me.auto = me.natural || me.shrinkWrap);

        /**
         * @property {Number} ordinal
         * The 0-based ordinal for this `SizeModel` instance.
         * @readonly
         */
        sizeModelsArray[me.ordinal = sizeModelsArray.length] =
            SizeModel[name] =
            SizeModel.sizeModels[name] = me;
    },

    statics: {
        /**
         * An array of all SizeModel instances.
         * @private
         */
        sizeModelsArray: [],

        /**
         * An object containing all SizeModel instances keyed by `name`.
         * @private
         */
        sizeModels: {}
    },

    /**
     * @property {String} name
     * The name of this size model (e.g., "calculated").
     * @readonly
     */

    /**
     * @property {Boolean} auto
     * True if the size is either `natural` or `shrinkWrap`, otherwise false.
     * @readonly
     */

    /**
     * @property {Boolean} calculated
     * True if the size is calculated by the `ownerLayout`.
     * @readonly
     */
    calculated: false,

    /**
     * @property {Boolean} configured
     * True if the size is configured (e.g., by a `width` or `minWidth`). The names of
     * configuration properties can be found in the {@link #names} property.
     * @readonly
     */
    configured: false,

    /**
     * @property {Boolean} constrainedMax
     * True if the size is constrained by a `maxWidth` or `maxHeight` configuration. This
     * is a flavor of `configured` (since `maxWidth` and `maxHeight` are config options).
     * If true, the {@link #names} property will be defined as well.
     * @readonly
     */
    constrainedMax: false,

    /**
     * @property {Boolean} constrainedMin
     * True if the size is constrained by a `minWidth` or `minHeight` configuration. This
     * is a flavor of `configured` (since `minWidth` and `minHeight` are config options).
     * If true, the {@link #names} property will be defined as well.
     * @readonly
     */
    constrainedMin: false,

    /**
     * @property {Boolean} fixed
     * True if the size is either `calculated` or `configured`, otherwise false.
     * @readonly
     */

    /**
     * @property {Boolean} natural
     * True if the size is determined by CSS and not by content. Such sizes are assumed to
     * be dependent on the container box and measurement occurs on the outer-most element.
     * @readonly
     */
    natural: false,

    /**
     * @property {Boolean} shrinkWrap
     * True if the size is determined by content irrespective of the container box.
     * @readonly
     */
    shrinkWrap: false,

    /**
     * @property {Boolean} calculatedFromConfigured
     * True if the size is calculated by the `ownerLayout` based on a configured size.
     * @readonly
     */
    calculatedFromConfigured: false,

    /**
     * @property {Boolean} calculatedFromNatural
     * True if the size is calculated by the `ownerLayout` based on `natural` size model
     * results.
     * @readonly
     */
    calculatedFromNatural: false,

    /**
     * @property {Boolean} calculatedFromShrinkWrap
     * True if the size is calculated by the `ownerLayout` based on `shrinkWrap` size model
     * results.
     * @readonly
     */
    calculatedFromShrinkWrap: false,

    /**
     * @property {Object} names An object with the config property names that determine the
     * size.
     * @property {String} names.width The width property name (e.g., 'width').
     * @property {String} names.height The height property name (e.g., 'minHeight').
     * @readonly
     */
    names: null
},
function () {
    var SizeModel = this,
        sizeModelsArray = SizeModel.sizeModelsArray,
        i, j, n, pairs, sizeModel;

    //-------------------------------------------------------------------------------
    // These are the 4 fundamental size models.

    new SizeModel({ // jshint ignore:line
        name: 'calculated'
    });

    new SizeModel({ // jshint ignore:line
        name: 'configured',
        names: { width: 'width', height: 'height' }
    });

    new SizeModel({ // jshint ignore:line
        name: 'natural'
    });

    new SizeModel({ // jshint ignore:line
        name: 'shrinkWrap'
    });

    //-------------------------------------------------------------------------------
    // These are the size models are flavors of the above but with some extra detail
    // about their dynamic use.

    new SizeModel({ // jshint ignore:line
        name: 'calculatedFromConfigured',
        configured: true,
        calculatedFrom: true,
        names: { width: 'width', height: 'height' }
    });

    new SizeModel({ // jshint ignore:line
        name: 'calculatedFromNatural',
        natural: true,
        calculatedFrom: true
    });

    new SizeModel({ // jshint ignore:line
        name: 'calculatedFromShrinkWrap',
        shrinkWrap: true,
        calculatedFrom: true
    });

    new SizeModel({ // jshint ignore:line
        name: 'constrainedMax',
        configured: true,
        constrained: true,
        names: { width: 'maxWidth', height: 'maxHeight' }
    });

    new SizeModel({ // jshint ignore:line
        name: 'constrainedMin',
        configured: true,
        constrained: true,
        names: { width: 'minWidth', height: 'minHeight' }
    });

    new SizeModel({ // jshint ignore:line
        name: 'constrainedDock',
        configured: true,
        constrained: true,
        constrainedByMin: true,
        names: { width: 'dockConstrainedWidth', height: 'dockConstrainedHeight' }
    });

    for (i = 0, n = sizeModelsArray.length; i < n; ++i) {
        sizeModel = sizeModelsArray[i];

        /**
         * An array of objects indexed by the {@link #ordinal} of a height `SizeModel` on
         * a width `SizeModel` to yield an object describing both height and width size
         * models.
         * 
         * Used like this:
         *
         *      widthModel.pairsByHeightOrdinal[heightModel.ordinal]
         *
         * This provides a reusable object equivalent to the following:
         * 
         *      {
         *          width: widthModel,
         *          height: heightModel
         *      }
         *
         * @property {Object[]} pairsByHeightOrdinal
         * @property {Ext.layout.SizeModel} pairsByHeightOrdinal.width The `SizeModel` for
         * the width.
         * @property {Ext.layout.SizeModel} pairsByHeightOrdinal.height The `SizeModel` for
         * the height.
         */
        sizeModel.pairsByHeightOrdinal = pairs = [];

        for (j = 0; j < n; ++j) {
            pairs.push({
                width: sizeModel,
                height: sizeModelsArray[j]
            });
        }
    }
});
