(function(clsPrefix) {

/**
 * @aside guide layouts
 * @aside video layouts
 *
 * The Default Layout is the layout that all other layouts inherit from. The main capability it provides is docking,
 * which means that every other layout can also provide docking support. It's unusual to use Default layout directly,
 * instead it's much more common to use one of the sub classes:
 *
 * * {@link Ext.layout.HBox hbox layout}
 * * {@link Ext.layout.VBox vbox layout}
 * * {@link Ext.layout.Card card layout}
 * * {@link Ext.layout.Fit fit layout}
 *
 * For a full overview of layouts check out the [Layout Guide](#!/guide/layouts).
 *
 * ## Docking
 *
 * Docking enables you to place additional Components at the top, right, bottom or left edges of the parent Container,
 * resizing the other items as necessary. For example, let's say we're using an {@link Ext.layout.HBox hbox layout}
 * with a couple of items and we want to add a banner to the top so that we end up with something like this:
 *
 * {@img ../guides/layouts/docktop.jpg}
 *
 * This is simple to achieve with the `docked: 'top'` configuration below. We can dock as many of the items as we like,
 * to either the top, right, bottom or left edges of the Container:
 *
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'hbox',
 *         items: [
 *             {
 *                 docked: 'top',
 *                 height: 20,
 *                 html: 'This is docked to the top'
 *             },
 *             {
 *                 html: 'message list',
 *                 flex: 1
 *             },
 *             {
 *                 html: 'message preview',
 *                 flex: 2
 *             }
 *         ]
 *     });
 *
 * Similarly, to dock something to the left of a layout (a {@link Ext.layout.VBox vbox} in this case), such as the
 * following:
 *
 * {@img ../guides/layouts/dockleft.jpg}
 *
 * We can simply dock to the left:
 *
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'vbox',
 *         items: [
 *             {
 *                 docked: 'left',
 *                 width: 100,
 *                 html: 'This is docked to the left'
 *             },
 *             {
 *                 html: 'message list',
 *                 flex: 1
 *             },
 *             {
 *                 html: 'message preview',
 *                 flex: 2
 *             }
 *         ]
 *     });
 *
 * We can also dock to the bottom and right and use other layouts than hbox and vbox ({@link Ext.layout.Card card} and
 * {@link Ext.layout.Fit fit} layouts both accept docking too).
 */
Ext.define('Ext.layout.Default', {
    extend: 'Ext.Evented',

    alternateClassName: ['Ext.layout.AutoContainerLayout', 'Ext.layout.ContainerLayout'],

    alias: ['layout.auto', 'layout.default'],

    isLayout: true,

    hasDockedItemsCls: clsPrefix + 'hasdocked',

    centeredItemCls: clsPrefix + 'centered',

    floatingItemCls: clsPrefix + 'floating',

    dockingWrapperCls: clsPrefix + 'docking',

    dockingInnerCls: clsPrefix + 'docking-inner',

    maskCls: clsPrefix + 'mask',

    positionMap: {
        top: 'start',
        left: 'start',
        bottom: 'end',
        right: 'end'
    },

    positionDirectionMap: {
        top: 'vertical',
        bottom: 'vertical',
        left: 'horizontal',
        right: 'horizontal'
    },

    DIRECTION_VERTICAL: 'vertical',

    DIRECTION_HORIZONTAL: 'horizontal',

    POSITION_START: 'start',

    POSITION_END: 'end',

    config: {
        /**
         * @cfg {Ext.fx.layout.Card} animation Layout animation configuration
         * Controls how layout transitions are animated.  Currently only available for
         * Card Layouts
         * @accessor
         */
        animation: null
    },

    constructor: function(container, config) {
        this.container = container;

        this.innerItems = [];

        this.centeringWrappers = {};

        this.initConfig(config);
    },

    reapply: Ext.emptyFn,

    unapply: Ext.emptyFn,

    onItemAdd: function() {
        this.doItemAdd.apply(this, arguments);
    },

    onItemRemove: function() {
        this.doItemRemove.apply(this, arguments);
    },

    onItemMove: function() {
        this.doItemMove.apply(this, arguments);
    },

    onItemCenteredChange: function() {
        this.doItemCenteredChange.apply(this, arguments);
    },

    onItemFloatingChange: function() {
        this.doItemFloatingChange.apply(this, arguments);
    },

    onItemDockedChange: function() {
        this.doItemDockedChange.apply(this, arguments);
    },

    /**
     * @private
     */
    doItemAdd: function(item, index) {
        var docked = item.getDocked();

        if (docked !== null) {
            this.dockItem(item, docked);
        }
        else if (item.isCentered()) {
            this.centerItem(item, index);
        }
        else {
            this.insertItem(item, index);
        }

        if (item.isFloating()) {
            this.onItemFloatingChange(item, true);
        }
    },

    /**
     * @private
     */
    doItemRemove: function(item) {
        if (item.isDocked()) {
            this.undockItem(item);
        }
        else if (item.isCentered()) {
            this.uncenterItem(item);
        }

        if (item.getTranslatable()) {
            item.setTranslatable(false);
        }

        Ext.Array.remove(this.innerItems, item);

        Ext.fly(item.renderElement).destroy();
    },

    /**
     * @private
     */
    doItemMove: function(item, toIndex, fromIndex) {
        if (item.isCentered()) {
            item.setZIndex((toIndex + 1) * 2);
        }
        else {
            if (item.isFloating()) {
                item.setZIndex((toIndex + 1) * 2);
            }
            this.insertItem(item, toIndex);
        }
    },

    /**
     * @private
     */
    doItemCenteredChange: function(item, centered) {
        if (centered) {
            this.centerItem(item);
        }
        else {
            this.uncenterItem(item);
        }
    },

    /**
     * @private
     */
    doItemFloatingChange: function(item, floating) {
        var element = item.element,
            floatingItemCls = this.floatingItemCls;

        if (floating) {
            if (item.getZIndex() === null) {
                item.setZIndex((this.container.indexOf(item) + 1) * 2);
            }
            element.addCls(floatingItemCls);
        }
        else {
            item.setZIndex(null);
            element.removeCls(floatingItemCls);
        }
    },

    /**
     * @private
     */
    doItemDockedChange: function(item, docked, oldDocked) {
        if (oldDocked) {
            this.undockItem(item, oldDocked);
        }

        if (docked) {
            this.dockItem(item, docked);
        }
    },

    centerItem: function(item) {
        this.insertItem(item, 0);

        if (item.getZIndex() === null) {
            item.setZIndex((this.container.indexOf(item) + 1) * 2);
        }

        this.createCenteringWrapper(item);

        // Mainly for styling
        item.element.addCls(this.floatingItemCls);
    },

    uncenterItem: function(item) {
        this.destroyCenteringWrapper(item);
        item.setZIndex(null);
        this.insertItem(item, this.container.indexOf(item));

        // Mainly for styling
        item.element.removeCls(this.floatingItemCls);
    },

    dockItem: function(item, position) {
        var container = this.container,
            itemRenderElement = item.renderElement,
            itemElement = item.element,
            dockingInnerElement = this.dockingInnerElement;

        if (!dockingInnerElement) {
            container.setUseBodyElement(true);
            this.dockingInnerElement = dockingInnerElement = container.bodyElement;
        }

        this.getDockingWrapper(position);

        if (this.positionMap[position] === this.POSITION_START) {
            itemRenderElement.insertBefore(dockingInnerElement);
        }
        else {
            itemRenderElement.insertAfter(dockingInnerElement);
        }

        itemElement.addCls(clsPrefix + 'docked-' + position);
    },

    undockItem: function(item, docked) {
        this.insertItem(item, this.container.indexOf(item));
        item.element.removeCls(clsPrefix + 'docked-' + docked);
    },

    getDockingWrapper: function(position) {
        var currentDockingDirection = this.currentDockingDirection,
            direction = this.positionDirectionMap[position],
            dockingWrapper = this.dockingWrapper;

        if (currentDockingDirection !== direction) {
            this.currentDockingDirection = direction;
            this.dockingWrapper = dockingWrapper = this.createDockingWrapper(direction);
        }

        return dockingWrapper;
    },

    createDockingWrapper: function(direction) {
        return this.dockingInnerElement.wrap({
            classList: [this.dockingWrapperCls + '-' + direction]
        }, true);
    },

    createCenteringWrapper: function(item) {
        var id = item.getId(),
            wrappers = this.centeringWrappers,
            renderElement = item.renderElement,
            wrapper;

        wrappers[id] = wrapper = renderElement.wrap({
            className: this.centeredItemCls
        });

        return wrapper;
    },

    destroyCenteringWrapper: function(item) {
        var id = item.getId(),
            wrappers = this.centeringWrappers,
            renderElement = item.renderElement,
            wrapper = wrappers[id];

        renderElement.unwrap();
        wrapper.destroy();
        delete wrappers[id];

        return this;
    },

    getInnerItemsContainer: function() {
        return this.container.innerElement;
    },

    insertItem: function(item, index) {
       var container = this.container,
           items = container.getItems().items,
           innerItems = this.innerItems,
           containerDom = this.getInnerItemsContainer().dom,
           itemDom = item.renderElement.dom,
           relativeItem, relativeItemDom, domIndex;

       if (container.has(item)) {
           Ext.Array.remove(innerItems, item);
       }

       if (typeof index == 'number') {
           // Retrieve the *logical* relativeItem reference to insertBefore
           relativeItem = items[index];

           // If it is the item itself, get the next sibling
           if (relativeItem === item) {
               relativeItem = items[++index];
           }

           // Continue finding the relativeItem that is neither currently centered nor docked
           while (relativeItem && (relativeItem.isCentered() || relativeItem.isDocked())) {
               relativeItem = items[++index];
           }

           if (relativeItem) {
               // Retrieve the *physical* index of that relativeItem
               domIndex = innerItems.indexOf(relativeItem);

               if (domIndex !== -1) {
                   while (relativeItem && (relativeItem.isCentered() || relativeItem.isDocked())) {
                       relativeItem = innerItems[++domIndex];
                   }

                   if (relativeItem) {
                       innerItems.splice(domIndex, 0, item);

                       relativeItemDom = relativeItem.renderElement.dom;
                       containerDom.insertBefore(itemDom, relativeItemDom);

                       return this;
                   }
               }
           }
       }

       innerItems.push(item);
       containerDom.appendChild(itemDom);

       return this;
   }
});

})(Ext.baseCSSPrefix);