/**
 * AbstractBox is a superclass for the two box layouts:
 *
 * * {@link Ext.layout.HBox hbox}
 * * {@link Ext.layout.VBox vbox}
 *
 * FlexBox itself is never used directly, but its subclasses provide flexible arrangement of child components
 * inside a {@link Ext.Container Container}.
 *
 * ## Horizontal Box
 *
 * HBox allows you to easily lay out child components horizontally. It can size items based on a fixed width or a
 * fraction of the total width available, enabling you to achieve flexible layouts that expand or contract to fill the
 * space available.
 *
 * {@img ../guides/layouts/hbox.jpg}
 *
 * See the {@link Ext.layout.HBox HBox layout docs} for more information on using hboxes.
 *
 * ## Vertical Box
 *
 * VBox allows you to easily lay out child components verticaly. It can size items based on a fixed height or a
 * fraction of the total height available, enabling you to achieve flexible layouts that expand or contract to fill the
 * space available.
 *
 * See the {@link Ext.layout.VBox VBox layout docs} for more information on using vboxes.
 */
Ext.define('Ext.layout.FlexBox', {
    extend: 'Ext.layout.Box',
 
    alias: 'layout.box',
 
    config: {
        align: 'stretch'
    },
 
    layoutBaseClass: 'x-layout-box',
 
    itemClass: 'x-layout-box-item',
 
    setContainer: function(container) {
        this.callParent(arguments);
 
        this.monitorSizeFlagsChange();
    },
 
    applyOrient: function(orient) {
        //<debug> 
        if (orient !== 'horizontal' && orient !== 'vertical') {
            Ext.Logger.error("Invalid box orient of: '" + orient + "', must be either 'horizontal' or 'vertical'");
        }
        //</debug> 
 
        return orient;
    },
 
    updateOrient: function(orient, oldOrient) {
        var container = this.container,
            delegation = {
                delegate: '> component'
            };
 
        if (orient === 'horizontal') {
            this.sizePropertyName = 'width';
        }
        else {
            this.sizePropertyName = 'height';
        }
 
        container.innerElement.swapCls('x-' + orient, 'x-' + oldOrient);
 
        if (oldOrient) {
            container.un(oldOrient === 'horizontal' ? 'widthchange' : 'heightchange', 'onItemSizeChange', this, delegation);
            this.redrawContainer();
        }
 
        container.on(orient === 'horizontal' ? 'widthchange' : 'heightchange', 'onItemSizeChange', this, delegation);
    },
 
    onItemInnerStateChange: function(item, isInner) {
        this.callParent(arguments);
 
        var flex, size;
 
        item.toggleCls(this.itemClass, isInner);
 
        if (isInner) {
            flex = item.getFlex();
            size = item.getConfig(this.sizePropertyName);
 
            if (flex) {
                this.doItemFlexChange(item, flex);
            }
            else if (size) {
                this.doItemSizeChange(item, size);
            }
        }
 
        this.refreshItemSizeState(item);
    },
 
    refreshItemSizeState: function(item) {
        var isInner = item.isInnerItem(),
            container = this.container,
            LAYOUT_HEIGHT = container.LAYOUT_HEIGHT,
            LAYOUT_WIDTH = container.LAYOUT_WIDTH,
            dimension = this.sizePropertyName,
            layoutSizeFlags = 0,
            containerSizeFlags = container.getSizeFlags();
 
        if (isInner) {
            layoutSizeFlags |= container.LAYOUT_STRETCHED;
 
            if (this.getAlign() === 'stretch') {
                layoutSizeFlags |= containerSizeFlags & (dimension === 'width' ? LAYOUT_HEIGHT : LAYOUT_WIDTH);
            }
 
            if (item.getFlex()) {
                layoutSizeFlags |= containerSizeFlags & (dimension === 'width' ? LAYOUT_WIDTH : LAYOUT_HEIGHT);
            }
        }
 
        item.setLayoutSizeFlags(layoutSizeFlags);
    },
 
    refreshAllItemSizedStates: function() {
        var innerItems = this.container.innerItems,
            i, ln, item;
 
        for (= 0,ln = innerItems.length; i < ln; i++) {
            item = innerItems[i];
            this.refreshItemSizeState(item);
        }
    },
 
    onContainerSizeFlagsChange: function() {
        this.refreshAllItemSizedStates();
 
        this.callParent(arguments);
    },
 
    onItemSizeChange: function(item, size) {
        if (item.isInnerItem()) {
            this.doItemSizeChange(item, size);
        }
    },
 
    doItemSizeChange: function(item, size) {
        if (size) {
            item.setFlex(null);
            this.redrawContainer();
        }
    },
 
    onItemFlexChange: function(item, flex) {
        if (item.isInnerItem()) {
            this.doItemFlexChange(item, flex);
            this.refreshItemSizeState(item);
        }
    },
 
    doItemFlexChange: function(item, flex) {
        this.setItemFlex(item, flex);
 
        if (flex) {
            item.setConfig(this.sizePropertyName, null);
        }
        else {
            this.redrawContainer();
        }
    },
 
    redrawContainer: function() {
        var container = this.container,
            renderedTo = container.element.dom.parentNode;
 
        if (renderedTo && renderedTo.nodeType !== 11) {
            container.innerElement.redraw();
        }
    },
 
    /**
     * Sets the flex of an item in this box layout.
     * @param {Ext.Component} item The item of this layout which you want to update the flex of.
     * @param {Number} flex The flex to set on this method
     */
    setItemFlex: function(item, flex) {
        var element = item.element,
            style = element.dom.style;
 
        element.toggleCls(Ext.baseCSSPrefix + 'flexed', !!flex);
 
        flex = flex ? String(flex) : '';
 
        if (Ext.browser.is.WebKit) {
            style.setProperty('-webkit-box-flex', flex, null);
        } else if (Ext.browser.is.IE) {
            style.setProperty('-ms-flex', flex + ' 0 0px', null);
        } else {
            style.setProperty('flex', flex + ' 0 0px', null);
        }
    },
 
    convertPosition: function(position) {
        var positionMap = this.positionMap;
 
        if (positionMap.hasOwnProperty(position)) {
            return positionMap[position];
        }
 
        return position;
    },
 
    applyAlign: function(align) {
        return this.convertPosition(align);
    },
 
    updateAlign: function(align, oldAlign) {
        var container = this.container;
 
        container.innerElement.swapCls(align, oldAlign, true, 'x-align');
 
        if (oldAlign !== undefined) {
            this.refreshAllItemSizedStates();
        }
    },
 
    applyPack: function(pack) {
        return this.convertPosition(pack);
    },
 
    updatePack: function(pack, oldPack) {
        this.container.innerElement.swapCls(pack, oldPack, true, 'x-pack');
    }
});