Show:
/* copyright © 2015 David Valdman */

define(function(require, exports, module) {
    var Transform = require('../../core/Transform');

    var DEFAULT = {
        OPACITY : 1,
        TRANSFORM : Transform.identity,
        ORIGIN : null,
        ALIGN : null
    };

    /**
     * Defines the rules for composing layout specs: transform, align, origin and opacity.
     *  Transform is multiplied by the parent's transform (matrix multiplication).
     *  Align is a proportional offset relative to the parent size.
     *  Origin is a proportional offset relative to the current size.
     *  Opacity is multiplied by the parent's opacity.
     *
     * @method compose
     * @private
     * @param spec {object}           Object layout spec
     * @param parentSpec {object}     Parent layout spec
     * @param size {Array}            Object size
     * @return {object}               The composed layout spec
     */

    function compose(spec, parentSpec, size){
        var parentOpacity = (parentSpec.opacity !== undefined) ? parentSpec.opacity : DEFAULT.OPACITY;
        var parentTransform = parentSpec.transform || DEFAULT.TRANSFORM;

        var origin = spec.origin || DEFAULT.ORIGIN;
        var align = spec.align || DEFAULT.ALIGN;

        var opacity = (spec.opacity !== undefined)
            ? parentOpacity * spec.opacity
            : parentOpacity;

        var transform = (spec.transform)
            ? Transform.compose(parentTransform, spec.transform)
            : parentTransform;

        var nextSizeTransform = (spec.origin)
            ? parentTransform
            : parentSpec.nextSizeTransform || parentTransform;

        if (spec.size)
            nextSizeTransform = parentTransform;

        if (origin && (origin[0] || origin[1])){
            //TODO: allow origin to propogate when size is non-numeric
            var tx =  (typeof size[0] === 'number') ? -origin[0] * size[0] : 0;
            var ty =  (typeof size[1] === 'number') ? -origin[1] * size[1] : 0;
            transform = Transform.moveThen([tx, ty, 0], transform);
            origin = null;
        }

        if (size && align && (align[0] || align[1])) {
            var shift = _vecInContext([align[0] * size[0], align[1] * size[1], 0], nextSizeTransform);
            transform = Transform.thenMove(transform, shift);
            align = null;
        }

        return {
            transform : transform,
            opacity : opacity,
            origin : origin,
            align : align,
            nextSizeTransform : nextSizeTransform
        };
    }

    function _vecInContext(v, m) {
        return [
            v[0] * m[0] + v[1] * m[4] + v[2] * m[8],
            v[0] * m[1] + v[1] * m[5] + v[2] * m[9],
            v[0] * m[2] + v[1] * m[6] + v[2] * m[10]
        ];
    }

    module.exports = compose;
});