/**
 * @private
 *
 * This class is used only by the grid's HeaderContainer docked child.
 *
 * It adds the ability to shrink the vertical size of the inner container element back if a grouped
 * column header has all its child columns dragged out, and the whole HeaderContainer needs to shrink back down.
 *
 * Also, after every layout, after all headers have attained their 'stretchmax' height, it goes through and calls
 * `setPadding` on the columns so that they lay out correctly.
 */
Ext.define('Ext.grid.ColumnLayout', {
    extend: 'Ext.layout.container.HBox',
    alias: 'layout.gridcolumn',
    type : 'gridcolumn',

    reserveOffset: false,

    firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',
    lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',

    initLayout: function() {
        if (!this.scrollbarWidth) {
            this.self.prototype.scrollbarWidth = Ext.getScrollbarSize().width;
        }
        this.grid = this.owner.up('[scrollerOwner]');
        this.callParent();
    },

    // Collect the height of the table of data upon layout begin
    beginLayout: function (ownerContext) {
        var me = this,
            owner = me.owner,
            grid = me.grid,
            view = grid.view,
            items = me.getVisibleItems(),
            len = items.length,
            firstCls = me.firstHeaderCls, 
            lastCls = me.lastHeaderCls,
            i, item;

        // If we are one side of a locking grid, then if we are on the "normal" side, we have to grab the normal view
        // for use in determining whether to subtract scrollbar width from available width.
        // The locked side does not have scrollbars, so it should not look at the view.
        if (grid.lockable) {
            if (owner.up('tablepanel') === view.normalGrid) {
                view = view.normalGrid.getView();
            } else {
                view = null;
            }
        }

        for (i = 0; i < len; i++) {
            item = items[i];

            // Keep the isLast flag correct so that the column's component layout can know whether or not
            // it needs a right border. See ColumnComponentLayout.beginLayoutCycle
            item.isLast = false;
            item.removeCls([firstCls, lastCls]);
            if (i === 0) {
                item.addCls(firstCls);
            }

            if (i === len - 1) {
                item.addCls(lastCls);
                item.isLast = true;
            }
        }

        me.callParent(arguments);

        // If the owner is the grid's HeaderContainer, and the UI displays old fashioned scrollbars and there is a rendered View with data in it,
        // collect the View context to interrogate it for overflow, and possibly invalidate it if there is overflow
        if (!owner.isColumn && Ext.getScrollbarSize().width && !grid.collapsed && view &&
                view.rendered && (ownerContext.viewTable = view.body.dom)) {
            ownerContext.viewContext = ownerContext.context.getCmp(view);
        }
    },

    roundFlex: function(width) {
        return Math.floor(width);
    },

    calculate: function(ownerContext) {
        this.callParent(arguments);

        // If we have calculated the widths, then if forceFit, and there are no flexes, we cannot tell the
        // TableLayout we are done. We will have to go through the convertWidthsToFlexes stage.
        if (ownerContext.state.parallelDone && (!this.owner.forceFit || ownerContext.flexedItems.length)) {
            // TODO: auto width columns aren't necessarily done here.
            // see view.TableLayout, there is a work around for that there 
            ownerContext.setProp('columnWidthsDone', true);
        }

        // Collect the height of the data table if we need it to determine overflow
        if (ownerContext.viewContext) {
            ownerContext.state.tableHeight = ownerContext.viewTable.offsetHeight;
        }
    },

    completeLayout: function(ownerContext) {
        var me = this,
            owner = me.owner,
            state = ownerContext.state;

        me.callParent(arguments);

        // If we have not been through this already, and the owning Container is configured
        // forceFit, is not a group column and and there is a valid width, then convert
        // widths to flexes, and loop back.
        if (!ownerContext.flexedItems.length && !state.flexesCalculated && owner.forceFit &&

            // Recalculate based upon all columns now being flexed instead of sized.
            // Set flag, so that we do not do this infinitely
            me.convertWidthsToFlexes(ownerContext)) {
            me.cacheFlexes(ownerContext);
            ownerContext.invalidate({
                state: {
                    flexesCalculated: true
                }
            });
        } else {
            ownerContext.setProp('columnWidthsDone', true);
        }
    },

    convertWidthsToFlexes: function(ownerContext) {
        var me = this,
            totalWidth = 0,
            calculated = me.sizeModels.calculated,
            childItems, len, i, childContext, item;

        childItems = ownerContext.childItems;
        len = childItems.length;

        for (i = 0; i < len; i++) {
            childContext = childItems[i];
            item = childContext.target;

            totalWidth += childContext.props.width;

            // Only allow to be flexed if it's a resizable column
            if (!(item.fixed || item.resizable === false)) {

                // For forceFit, just use allocated width as the flex value, and the proportions
                // will end up the same whatever HeaderContainer width they are being forced into.
                item.flex = ownerContext.childItems[i].flex = childContext.props.width;
                item.width = null;
                childContext.widthModel = calculated;
            }
        }

        // Only need to loop back if the total column width is not already an exact fit
        return totalWidth !== ownerContext.props.width;
    },

    /**
     * @private
     * Local getContainerSize implementation accounts for vertical scrollbar in the view.
     */
    getContainerSize: function(ownerContext) {
        var me = this,
            result,
            viewContext = ownerContext.viewContext,
            viewHeight;

        // Column, NOT the main grid's HeaderContainer
        if (me.owner.isColumn) {
            result = me.getColumnContainerSize(ownerContext);
        }

        // This is the maingrid's HeaderContainer
        else {
            result = me.callParent(arguments);

            // If we've collected a viewContext and we're not shrinkwrapping the height
            // then we see if we have to narrow the width slightly to account for scrollbar
            if (viewContext && !viewContext.heightModel.shrinkWrap &&
                    viewContext.target.componentLayout.ownerContext) { // if (its layout is running)
                viewHeight = viewContext.getProp('height');
                if (isNaN(viewHeight)) {
                    me.done = false;
                } else if (ownerContext.state.tableHeight > viewHeight) {
                    result.width -= Ext.getScrollbarSize().width;
                    ownerContext.state.parallelDone = false;
                    viewContext.invalidate();
                }
            }
        }

// TODO - flip the initial assumption to "we have a vscroll" to avoid the invalidate in most
// cases (and the expensive ones to boot)

        return result;
    },

    getColumnContainerSize : function(ownerContext) {
        var padding = ownerContext.paddingContext.getPaddingInfo(),
            got = 0,
            needed = 0,
            gotWidth, gotHeight, width, height;

        // In an shrinkWrap width/height case, we must not ask for any of these dimensions
        // because they will be determined by contentWidth/Height which is calculated by
        // this layout...

        // Fit/Card layouts are able to set just the width of children, allowing child's
        // resulting height to autosize the Container.
        // See examples/tabs/tabs.html for an example of this.

        if (!ownerContext.widthModel.shrinkWrap) {
            ++needed;
            width = ownerContext.getProp('innerWidth');
            gotWidth = (typeof width == 'number');
            if (gotWidth) {
                ++got;
                width -= padding.width;
                if (width < 0) {
                    width = 0;
                }
            }
        }

        if (!ownerContext.heightModel.shrinkWrap) {
            ++needed;
            height = ownerContext.getProp('innerHeight');
            gotHeight = (typeof height == 'number');
            if (gotHeight) {
                ++got;
                height -= padding.height;
                if (height < 0) {
                    height = 0;
                }
            }
        }

        return {
            width: width,
            height: height,
            needed: needed,
            got: got,
            gotAll: got == needed,
            gotWidth: gotWidth,
            gotHeight: gotHeight
        };
    },

    // FIX: when flexing we actually don't have enough space as we would
    // typically because of the scrollOffset on the GridView, must reserve this
    publishInnerCtSize: function(ownerContext) {
        var me = this,
            size = ownerContext.state.boxPlan.targetSize,
            cw = ownerContext.peek('contentWidth'),
            view;

        // Allow the other co-operating objects to know whether the columns overflow the available width.
        me.owner.tooNarrow = ownerContext.state.boxPlan.tooNarrow;

        // InnerCt MUST stretch to accommodate all columns so that left/right scrolling is enabled in the header container.
        if ((cw != null) && !me.owner.isColumn) {
            size.width = cw;

            // innerCt must also encompass any vertical scrollbar width if there may be one
            view = me.owner.ownerCt.view;
            if (view.scrollFlags.y) {
                size.width += Ext.getScrollbarSize().width;
            }
        }

        return me.callParent(arguments);
    }
});