/**
 * A simple grid-like layout for proportionally dividing container space and allocating it
 * to each item. All items in this layout are given one or more percentage sizes and CSS
 * `float:left` is used to provide the wrapping.
 *
 * To select which of the percentage sizes an item uses, this layout adds a viewport
 * {@link #states size-dependent} class name to the container. The style sheet must
 * provide the rules to select the desired size using the {@link #responsivecolumn-item}
 * mixin.
 *
 * For example, a panel in a responsive column layout might add the following styles:
 *
 *      .my-panel {
 *          // consume 50% of the available space inside the container by default
 *          @include responsivecolumn-item(50%);
 *
 *          .x-responsivecolumn-small & {
 *              // consume 100% of available space in "small" mode
 *              // (viewport width < 1000 by default)
 *              @include responsivecolumn-item(100%);
 *          }
 *      }
 *
 * Alternatively, instead of targeting specific panels in CSS, you can create reusable
 * classes:
 *
 *      .big-50 {
 *          // consume 50% of the available space inside the container by default
 *          @include responsivecolumn-item(50%);
 *      }
 *
 *      .x-responsivecolumn-small {
 *          > .small-100 {
 *              @include responsivecolumn-item(100%);
 *          }
 *      }
 *
 * These can be added to components in the layout using the `responsiveCls` config:
 *
 *      items: [{
 *          xtype: 'my-panel',
 *
 *          // Use 50% of space when viewport is "big" and 100% when viewport
 *          // is "small":
 *          responsiveCls: 'big-50 small-100'
 *      }]
 *
 * The `responsiveCls` config is provided by this layout to avoid overwriting classes
 * specified using `cls` or other standard configs.
 *
 * Internally, this layout simply uses `float:left` and CSS `calc()` (except on IE8) to
 * "flex" each item. The calculation is always based on a percentage with a spacing taken
 * into account to separate the items from each other.
 */
Ext.define('Ext.ux.layout.ResponsiveColumn', {
    extend: 'Ext.layout.container.Auto',
    alias: 'layout.responsivecolumn',

    /**
     * @cfg {Object} states
     *
     * A set of layout state names corresponding to viewport size thresholds. One of the
     * states will be used to assign the responsive column CSS class to the container to
     * trigger appropriate item sizing.
     *
     * For example:
     *
     *      layout: {
     *          type: 'responsivecolumn',
     *          states: {
     *              small: 800,
     *              medium: 1200,
     *              large: 0
     *          }
     *      }
     *
     * Given the above set of responsive states, one of the following CSS classes will be
     * added to the container:
     *
     *   - `x-responsivecolumn-small` - If the viewport is <= 800px
     *   - `x-responsivecolumn-medium` - If the viewport is > 800px and <= 1200px
     *   - `x-responsivecolumn-large` - If the viewport is > 1200px
     *
     * For sake of efficiency these classes are based on the size of the browser viewport
     * (the browser window) and not on the container size. As the size of the viewport
     * changes, this layout will maintain the appropriate CSS class on the container which
     * will then activate the appropriate CSS rules to size the child items.
     */
    states: {
        small: 1000,
        large: 0
    },

    _responsiveCls: Ext.baseCSSPrefix + 'responsivecolumn',

    initLayout: function() {
        this.innerCtCls += ' ' + this._responsiveCls;
        this.callParent();
    },

    beginLayout: function(ownerContext) {
        var me = this,
            viewportWidth = Ext.Element.getViewportWidth(),
            states = me.states,
            activeThreshold = Infinity,
            innerCt = me.innerCt,
            currentState = me._currentState,
            name, threshold, newState;

        for (name in states) {
            threshold = states[name] || Infinity;

            if (viewportWidth <= threshold && threshold <= activeThreshold) {
                activeThreshold = threshold;
                newState = name;
            }
        }

        if (newState !== currentState) {
            innerCt.replaceCls(currentState, newState, me._responsiveCls);
            me._currentState = newState;
        }

        me.callParent(arguments);
    },

    onAdd: function(item) {
        var responsiveCls;

        this.callParent([item]);

        responsiveCls = item.responsiveCls;

        if (responsiveCls) {
            item.addCls(responsiveCls);
        }
    }
}, function(Responsive) {
    //--------------------------------------------------------------------------------------
    // IE8 does not support CSS calc expressions, so we have to fallback to more traditional
    // for of layout. This is very similar but much simpler than Column layout.
    //
    if (Ext.isIE8) {
        Responsive.override({
            responsiveSizePolicy: {
                readsWidth: 0,
                readsHeight: 0,
                setsWidth: 1,
                setsHeight: 0
            },

            setsItemSize: true,

            calculateItems: function(ownerContext, containerSize) {
                var me = this,
                    targetContext = ownerContext.targetContext,
                    items = ownerContext.childItems,
                    len = items.length,
                    gotWidth = containerSize.gotWidth,
                    contentWidth = containerSize.width,
                    i, itemContext, itemMarginWidth, itemWidth;

                // No parallel measurement, cannot lay out boxes.
                if (gotWidth === false) {
                    targetContext.domBlock(me, 'width');

                    return false;
                }

                if (!gotWidth) {
                    // gotWidth is undefined, which means we must be width shrink wrap.
                    // Cannot calculate item widths if we're shrink wrapping.
                    return true;
                }

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

                    // The mixin encodes these in background-position syles since it is
                    // unlikely a component will have a background-image.
                    itemWidth = parseInt(itemContext.el.getStyle('background-position-x'), 10);
                    itemMarginWidth =
                        parseInt(itemContext.el.getStyle('background-position-y'), 10);

                    itemContext.setWidth(
                        (itemWidth / 100 * (contentWidth - itemMarginWidth)) - itemMarginWidth
                    );
                }

                ownerContext.setContentWidth(contentWidth +
                    ownerContext.paddingContext.getPaddingInfo().width);

                return true;
            },

            getItemSizePolicy: function() {
                return this.responsiveSizePolicy;
            }
        });
    }
});
