YUI.add('aui-layout-row', function (A, NAME) {

/**
 * The Layout Row Component
 *
 * @module aui-layout-row
 */

var ALLOWED_SIZE = 12,
    SELECTOR_ROW = '.layout-row',
    TPL_ROW = '<div class="layout-row-container-row"><div class="layout-row row"></div></div>';

/**
 * A base class for Layout Row.
 *
 * @class A.LayoutRow
 * @extends Base
 * @param {Object} config Object literal specifying layout configuration
 *     properties.
 * @constructor
 */
A.LayoutRow = A.Base.create('layout-row', A.Base, [], {
    /**
     * Construction logic executed during `A.LayoutRow` instantiation. Lifecycle.
     *
     * @method initializer
     * @protected
     */
    initializer: function() {
        var cols = this.get('cols');

        A.Array.invoke(cols, 'addTarget', this);

        this.after({
            colsChange: this._afterColsChange
        });

        this._uiSetCols(cols);
    },

    /**
     * Adds a col to this row.
     *
     * @method addCol
     * @param {Number} index Index to add the the col.
     * @param {A.LayoutCol} col Col to be added.
     */
    addCol: function(index, col) {
        var cols = this.get('cols').concat(),
            insertIndex = index;

        if (A.Lang.isUndefined(index)) {
            insertIndex = cols.length;
        }
        else if (A.instanceOf(index, A.LayoutCol)) {
            insertIndex = cols.length;
            col = index;
        }

        if (!col) {
            col = new A.LayoutCol();
        }

        if (insertIndex > cols.length) {
            insertIndex = cols.length;
        }

        cols.splice(insertIndex, 0, col);

        cols = this._resizeColsAfterAdding(cols, insertIndex);

        this.set('cols', cols);
    },

    /**
     * Moves the content of the given column to a different position.
     *
     * @method moveRow
     * @param {A.LayoutCol} col Column to move the content to the new position.
     * @param {Number} position The new position of the column's content.
     **/
    moveColContent: function(position, col) {
        var cols = this.get('cols').concat(),
            targetCol = cols[position];

        targetCol.set('value', col.get('value'));
        col.set('value', null);
    },

    /**
     * Removes a col from this row.
     *
     * @method removeCol
     * @param {Number | A.LayoutCol} col Column index or column to be removed from this row
     */
    removeCol: function(col) {
        if (A.Lang.isNumber(col)) {
            this._removeColByIndex(col);
        }
        else if (A.instanceOf(col, A.LayoutCol)) {
            this._removeColByReference(col);
        }
    },

    /**
     * Fired after the `cols` attribute is set.
     *
     * @method _afterColsChange
     * @param {EventFacade} event
     * @protected
     */
    _afterColsChange: function(event) {
        A.Array.invoke(event.prevVal, 'removeTarget', this);
        A.Array.invoke(event.newVal, 'addTarget', this);

        this._uiSetCols(this.get('cols'));
    },

    /**
     * Resize cols width passing a size.
     *
     * @method _expandNeighborColsWidth
     * @param {Array} cols Columns to be resized
     * @param {Number} size Size to shared between the columns
     * will be increase or decrease
     * @protected
     */
    _expandNeighborColsWidth: function(cols, size) {
        var col1 = cols[0],
            col2 = cols[1];

        col1.set('size', col1.get('size') + Math.ceil(size / cols.length));

        if (col2) {
            col2.set('size', col2.get('size') + Math.floor(size / cols.length));
        }
    },

    /**
     * Find a next and previous column.
     *
     * @method _findNeighbors
     * @param {Array} cols Columns to search into.
     * @param {Number} index Column index reference.
     * @return {Array} Columns neighbors.
     * @protected
     */
    _findNeighbors: function(cols, index) {
        var nextCol = cols[index + 1],
            previousCol = cols[index - 1],
            neighbor = [];

        if (previousCol) {
            neighbor.push(previousCol);
        }

        if (nextCol) {
            neighbor.push(nextCol);
        }

        return neighbor;
    },

    /**
     * Returns the sum of all this row's column sizes.
     *
     * @method getSize
     * @return {Number}
     */
    _getSize: function(cols) {
        var size;

        return A.Array.reduce(cols, 0, function(prev, current) {
            if (!A.instanceOf(current, A.LayoutCol)) {
                size = current.size || 0;
            }
            else {
                size = current.get('size');
            }
            prev += size;
            return prev;
        });
    },

    /**
     * Resize cols width passing a size.
     *
     * @method _reduceNeighborColsWidth
     * @param {Array} cols Columns to be resized
     * @param {Number} size Size to shared between the columns
     * @protected
     */
    _reduceNeighborColsWidth: function(cols, size) {
        var majorCol = cols[0],
            minorCol,
            remove = 0;

        if (cols[1] && cols[1].get('size') > cols[0].get('size')) {
            minorCol = cols[0];
            majorCol = cols[1];
        }

        if (minorCol) {
            remove = Math.min(Math.floor(size / cols.length), minorCol.get('size') - 1);
            minorCol.set('size', minorCol.get('size') - remove);
        }

        majorCol.set('size', majorCol.get('size') - (size - remove));
    },

    /**
     * Removes a col from this row by it's index.
     *
     * @method _removeColByIndex
     * @param {Number} index Column index to be removed from this row
     * @protected
     */
    _removeColByIndex: function(index) {
        var cols = this.get('cols').concat();

        cols.splice(index, 1);
        cols = this._resizeColsAfterRemoving(cols, index);
        this.set('cols', cols);
    },

    /**
     * Removes a col from this row by it's reference.
     *
     * @method _removeColByReference
     * @param {A.LayoutCol} col Column to be removed from this row
     * @protected
     */
    _removeColByReference: function(col) {
        var cols = this.get('cols').concat(),
            index = A.Array.indexOf(cols, col);

        if (index >= 0) {
            this._removeColByIndex(index);
        }
    },

    /**
     * Resizes cols.
     *
     * @method _resizeColsAfterAdding
     * @param {Array} cols Columns to be resized.
     * @param {Number} index Column index reference.
     * @return {Array} Columns resized.
     * @protected
     */
    _resizeColsAfterAdding: function(cols, index) {
        var colSize = cols[index].get('size'),
            previousColSize = 0,
            nextColSize = 0,
            maxWidth = 0,
            neighbor = this._findNeighbors(cols, index);

        previousColSize = neighbor[0].get('size');

        if (neighbor[1]) {
            nextColSize = neighbor[1].get('size');
        }

        maxWidth = (previousColSize + nextColSize) - neighbor.length;

        if (colSize > maxWidth) {
            colSize = maxWidth;
        }

        this._reduceNeighborColsWidth(neighbor, colSize);

        cols[index].set('size', colSize);

        return cols;
    },

    /**
     * Expand nearby cols after deletting another one.
     *
     * @method _resizeColsAfterRemoving
     * @param {Array} cols Columns to be resized.
     * @param {Number} index Column index reference.
     * @return {Array} Columns resized.
     * @protected
     */
    _resizeColsAfterRemoving: function(cols, index) {
        var removedColSize = ALLOWED_SIZE - this._getSize(cols),
            neighbor;

        neighbor = [];

        if (cols[index]) {
            neighbor.push(cols[index]);
        }

        if (cols[index - 1]) {
            neighbor.push(cols[index - 1]);
        }

        this._expandNeighborColsWidth(neighbor, removedColSize);

        return cols;
    },

    /**
     * Sets the `cols` attribute.
     *
     * @method _setCols
     * @param {Array} cols
     * @return {Array}
     * @protected
     */
    _setCols: function(cols) {
        var col,
            i,
            newCols = [],
            size;

        for (i = 0; i < cols.length; i++) {
            col = cols[i];
            if (!A.instanceOf(col, A.LayoutCol)) {
                col = new A.LayoutCol(col);
            }

            newCols.push(col);
        }

        cols = newCols;

        size = this._getSize(cols);

        if (size < ALLOWED_SIZE) {
            cols.push(new A.LayoutCol({ size: ALLOWED_SIZE - size }));
        }

        return cols;
    },

    /**
     * Update the UI according to the value of the `cols` attribute.
     *
     * @method _uiSetCols
     * @param {Array} cols
     * @protected
     */
    _uiSetCols: function(cols) {
        var nodeContainer = this.get('node'),
            node = nodeContainer.one(SELECTOR_ROW);

        nodeContainer.setStyle('height', nodeContainer.get('offsetHeight'));

        node.empty();
        A.each(cols, function(col) {
            node.append(col.get('node'));
        });

        nodeContainer.setStyle('height', '');
    },

    /**
     * Validates the `cols` attribute.
     *
     * @method _validateCols
     * @param  {Array} cols
     * @return {Boolean}
     * @protected
     */
    _validateCols: function(cols) {
        var maximumCols = this.get('maximumCols'),
            size = this._getSize(cols);

        if (size > ALLOWED_SIZE || size < 0 || !A.Lang.isArray(cols) || cols.length > maximumCols) {
            return false;
        }

        return true;
    }
}, {

    /**
     * Static property used to define the default attribute
     * configuration for the Layout Row.
     *
     * @property ATTRS
     * @type Object
     * @static
     */
    ATTRS: {

        /**
         * Array containing `A.LayoutCol` objects
         *
         * @attribute cols
         * @default []
         * @type {Array}
         */
        cols: {
            setter: '_setCols',
            validator: '_validateCols',
            value: []
        },

        /**
         * Determines if cols should have the same height.
         *
         * @attribute equalHeight
         * @default true
         * @type {Boolean}
         */
        equalHeight: {
            validator: A.Lang.isBoolean,
            value: true
        },

        /**
         * Number to determine maximum cols on a row.
         *
         * @attribute maximumCols
         * @default 12
         * @type {Number}
         */
        maximumCols: {
            validator: function(val) {
                return val > 0 && val <= 12;
            },
            value: 12
        },

        /**
         * Determine if the row can move.
         *
         * @attribute movable
         * @default true
         * @type {Boolean}
         */
        movable: {
            validator: A.Lang.isBoolean,
            value: true
        },

        /**
         * The node where this column will be rendered.
         *
         * @attribute node
         * @type Node
         */
        node: {
            setter: function(val) {
                val.one(SELECTOR_ROW).setData('layout-row', this);
                return val;
            },
            validator: A.Lang.isNode,
            valueFn: function() {
                return A.Node.create(TPL_ROW);
            },
            writeOnce: 'initOnly'
        },

        /**
         * Determine if the row can be removed.
         *
         * @attribute removable
         * @default true
         * @type {Boolean}
         */
        removable: {
            validator: A.Lang.isBoolean,
            value: true
        }
    }
});


}, '3.1.0-deprecated.110', {"requires": ["array-invoke", "aui-node-base", "base-build"], "skinnable": true});
