/**
 * This layout extends `Ext.layout.container.Column` and adds splitters between adjacent
 * columns allowing the user to resize them.
 * @private
 */
Ext.define('Ext.layout.container.Dashboard', {
    extend: 'Ext.layout.container.Column',
    alias: 'layout.dashboard',
 
    requires: [
        'Ext.layout.container.ColumnSplitter'
    ],
 
    type: 'dashboard',
 
    firstColumnCls: Ext.baseCSSPrefix + 'dashboard-column-first',
 
    lastColumnCls: Ext.baseCSSPrefix + 'dashboard-column-last',
 
    /*
     * The geometry of a Column layout with splitters between respective items:
     *
     *             0        1      2       3        4
     *      +-----------------------------------------------+
     *      | +-----------+ || +---------+ || +-----------+ | \
     *      | |           | || |         | || |           | |  \
     *      | |           | || |         | || |           | |   \
     *      | |           | || |         | || |           | |    \
     *      | +-----------+ || |         | || |           | |   row[0]
     *      |               || |         | || |           | |    /
     *      |               || |         | || |           | |   /
     *      |               || |         | || +-----------+ |  /
     *      |               || |         | ||               | /
     *      |               || +---------+ ||               |
     *      | +-------------------+ || +------------------+ | \
     *      | |                   | || |                  | |  \
     *      | |                   | || |                  | |   \
     *      | |                   | || |                  | |  row[1]
     *      | |                   | || |                  | |   /
     *      | |                   | || +------------------+ |  /
     *      | +-------------------+ ||                      | /
     *      +-----------------------------------------------+
     *                  6           7            8
     *
     * The splitter between 4 and 6 will be hidden but still present in the items. It is
     * considered part of row[0].
     */
 
    getSplitterConfig: function () {
        return {
           xtype: 'columnsplitter'
        };
    },
 
    /**
     * @private
     * Returns a filtered item list sans splitters
     * @param items
     * @return {Array|*}
     */
    getColumns : function(items) {
        var array = Ext.Array;
        return array.filter(array.from(items), function(item) {
            return item.target && item.target.isSplitter !== true;
        });
    },
 
    beginLayout: function (ownerContext) {
        var me = this;
        me.callParent([ownerContext]);
 
        // We need to reset the heights of the splitters so that they don't influence the 
        // layout (mostly overflow management). 
        var childItems = ownerContext.childItems,
            rows = (ownerContext.rows = []),
            length = childItems.length,
            totalWidth = 2,
            columnTargets = 0,
            lastRow = 0,
            maxColumns = me.owner.getMaxColumns(),
            child, i, prev, row, splitter, target, width;
 
        for (= 0; i < length; ++i) {
            target = (child = childItems[i]).target;
            splitter = target && target.isSplitter;
            columnTargets += (splitter ? 0 : 1);
            width = splitter ? 0 : target.columnWidth || 1;
 
            if (totalWidth + width > 1 || (maxColumns && (columnTargets > maxColumns))) {
                if (prev) {
                    // We have wrapped and we have a previous item which is a splitter by 
                    // definition. We have previously seen that splitter and setHeight(0) 
                    // on it. We now setHeight(0) to effectively hide it. 
                    prev.orphan = 1;
                    prev.el.setHeight(0);
                }
                totalWidth = 0;
                columnTargets = 1;
 
                if (rows.length) {
                    // We have encountered a row break condition 
                    // As this is floating layout, classify the current row 
                    // before proceeding 
                    lastRow = rows.length - 1;
                    me.syncFirstLast(
                        me.getColumns(rows[lastRow].items)
                    );
                }
                rows.push(row = {
                    index: rows.length,
                    items: [],
                    maxHeight: 0
                });
            }
 
            totalWidth += width;
            row.items.push(child);
            child.row = row;
            target.rowIndex = row.index;
 
            if (splitter) {
                child.el.setHeight(1);
            }
 
            prev = child;
        }
 
        if (rows.length ) {
            me.syncFirstLast(
                me.getColumns(rows[rows.length-1].items)
            );
        }
    },
 
    beforeLayoutCycle: function (ownerContext) {
        var me = this,
            items = me.owner.items;
 
        // We need to do this in beforeLayoutCycle because this changes the child items 
        // and hence needs to be considered before recursing. 
        if (me.splitterGen !== items.generation) {
            me.syncSplitters();
 
            // The syncSplitters call will change items.generation so do this last. 
            me.splitterGen = items.generation;
        }
 
        me.callParent(arguments);
    },
 
    finishedLayout: function (ownerContext) {
        var items = ownerContext.childItems,
            len = items.length,
            box, child, i, target, row;
 
        this.callParent([ownerContext]);
 
        for (= 0; i < len; i += 2) {
            target = (child = items[i]).target;
            box = target.lastBox;
            row = child.row;
            row.maxHeight = Math.max(row.maxHeight, box.height);
 
            // Put this on the component so that it gets saved (we use this to fix up 
            // columnWidth on restore) 
            target.width = box.width;
        }
 
        for (= 1; i < len; i += 2) {
            target = (child = items[i]).target;
            if (!child.orphan) {
                target.el.setHeight(child.row.maxHeight);
            }
        }
    },
 
    /**
     * This method synchronizes the splitters so that we have exactly one between each
     * column.
     * @private
     */
    syncSplitters: function () {
        var me = this,
            owner = me.owner,
            items = owner.items.items,
            index = items.length,
            ok = true,
            shouldBeSplitter = false,
            item, splitter;
 
        // Walk backwards over the items so that an insertion index is stable. 
        while (index-- > 0) {
            item = items[index];
 
            if (shouldBeSplitter) {
                if (item.isSplitter) {
                    shouldBeSplitter = false;
                } else {
                    // An item is adjacent to an item, so inject a splitter beyond 
                    // the current item to separate the columns. Keep shouldBeSplitter 
                    // at true since we just encountered an item. 
                    if (ok) {
                        ok = false;
                        owner.suspendLayouts();
                    }
                    splitter = owner.add(index+1, me.getSplitterConfig());
                }
            } else {
                if (item.isSplitter) {
                    // A splitter is adjacent to a splitter so we remove this one. We 
                    // leave shouldBeSplitter at false because the next thing we see 
                    // should still not be a splitter. 
                    if (ok) {
                        ok = false;
                        owner.suspendLayouts();
                    }
                    owner.remove(item);
                } else {
                    shouldBeSplitter = true;
                }
            }
        }
 
        // It is possible to exit the above with a splitter as the first item, but 
        // this is invalid so remove any such splitters. 
        while (items.length && (item = items[0]).isSplitter) {
            if (ok) {
                ok = false;
                owner.suspendLayouts();
            }
            owner.remove(item);
        }
 
        if (!ok) {
            owner.resumeLayouts();
        }
    },
 
    syncFirstLast: function (items) {
        var me = this,
            firstCls = me.firstColumnCls,
            lastCls = me.lastColumnCls,
            len,
            firstAndLast = [firstCls, lastCls],
            i, item, last;
 
        items = Ext.Array.from(items);
        len = items.length;
 
        for (= 0; i < len; ++) {
            item = items[i].target;
            last = (=== len-1);
 
            if (!i) { // if (first) 
                if (last) {
                    item.addCls(firstAndLast);
                } else {
                    item.addCls(firstCls);
                    item.removeCls(lastCls);
                }
            } else if (last) {
                item.addCls(lastCls);
                item.removeCls(firstCls);
            } else {
                item.removeCls(firstAndLast);
            }
        }
    }
});