/**
 * 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).
        // eslint-disable-next-line vars-on-top, one-var
        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 (= 0; i < len; i ++) {
            target = (child = items[i]).target;
 
            if (!child.orphan) {
                if (% 2 === 0) {
                    // Set min height of column to the max height
                    // So that it can increase on adding placeholder
                    target.el.setMinHeight(child.row.maxHeight);
                }
                else {
                    // Set height for splitter
                    target.el.setHeight(child.row.maxHeight);
                }
            }
            else {
                // since this is an orphan child, set its width to 0
                target.el.setWidth(0);
            }
        }
    },
 
    /**
     * 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; // eslint-disable-line no-unused-vars
 
        // 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; ++i) {
            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);
            }
        }
    },
 
    calculateItemSizeWithContent: function(availableWidth, contentWidth, items) {
        var itemMarginWidth, itemContext,
            splitterItemWidth = 0,
            halfSplitterItemWidth = 0,
            itemWidth, i,
            len = items.length,
            rowindex, rowLen;
 
        availableWidth = (availableWidth < contentWidth) ? 0 : availableWidth;
 
        for (= 0; i < len; i += 2) {
            itemContext = items[i];
            rowLen = itemContext.row.items.length;
            rowindex = itemContext.row.items.indexOf(itemContext);
 
            itemMarginWidth = itemContext.marginInfo.width; // always set by above loop
            itemWidth = itemContext.target.columnWidth;
            itemWidth = Math.floor(itemWidth * availableWidth) - itemMarginWidth;
            
            // Get the width of splitter item. We calculate the half value for width
            // since splitter must form a part of both items between which it lies equally.
            if (splitterItemWidth === 0 && (rowindex + 1 < rowLen)) {
                splitterItemWidth = items[+ 1].getProp('width');
                halfSplitterItemWidth = Math.ceil(splitterItemWidth / 2);
            }
 
            if (halfSplitterItemWidth) {
                // if there exists a splitter to the right and this splitter 
                // is not the last item of this row, reduce the width of the
                // column since splitter will take half width from the item
                if (rowindex + 2 < rowLen) {
                    itemWidth -= halfSplitterItemWidth;
                }
 
                // if there exists a splitter to the left, reduce the width 
                // of the column since splitter will take half width from the item
                if (rowindex > 0) {
                    itemWidth -= halfSplitterItemWidth;
                }
            }
            
            itemWidth = itemContext.setWidth(itemWidth); // constrains to min/maxWidth
            contentWidth += itemWidth + itemMarginWidth;
        }
        
        return contentWidth;
    }
});