/**
 * Ext.tab.Bar is used internally by {@link Ext.tab.Panel} to create the bar of tabs that appears
 * at the top of the tab panel. It can also be used as a standalone component to 
 * recreate the look and feel of tabs.
 */
Ext.define('Ext.tab.Bar', {
    extend: 'Ext.Toolbar',
    alternateClassName: 'Ext.TabBar',
    xtype: 'tabbar',
    isTabBar: true,
 
    requires: [
        'Ext.layout.HBox',
        'Ext.tab.Tab'
    ],
 
    config: {
        /**
         * @cfg {String} defaultTabUI
         * A default {@link Ext.Component#ui ui} to use for {@link Ext.tab.Tab Tab} items.
         */
        defaultTabUI: null,
 
        /**
         * @cfg {Boolean} animateIndicator
         * Determines if the active indicator below the tab should animate or snap
         */
        animateIndicator: false,
 
        /**
         * @cfg {String} tabRotation
         * Specifies tab rotation. Possible values are 'default', 'left',
         * 'none', 'right'.
         * @accessor
         */
        tabRotation: "default"
    },
 
    /**
     * @cfg defaultType
     * @inheritdoc
     */
    defaultType: 'tab',
 
    /**
     * @cfg layout
     * @inheritdoc
     */
    layout: {
        type: 'hbox',
        align: 'stretch'
    },
 
    eventedConfig: {
        /**
         * @cfg {Number/String/Ext.Component} activeTab
         * The initially activated tab. Can be specified as numeric index, itemId,
         * component ID, or as the component instance itself.
         * @accessor
         * @evented
         */
        activeTab: null
    },
 
    /**
     * @property baseCls
     * @inheritdoc
     */
    baseCls: Ext.baseCSSPrefix + 'tabbar',
 
    /**
     * Speed in which the Indicator will move per Tab in milliseconds
     */
    indicatorAnimationSpeed: 150,
 
    /**
     * @event tabchange
     * Fired when active tab changes.
     * @param {Ext.tab.Bar} this 
     * @param {Ext.tab.Tab} newTab The new Tab
     * @param {Ext.tab.Tab} oldTab The old Tab
     */
 
    initialize: function() {
        var me = this;
 
        me.callParent();
 
        me.on({
            tap: 'onTabTap',
 
            delegate: '> tab',
            scope: me
        });
    },
 
    getTemplate: function() {
        var template = this.callParent();
 
        template.push({
            reference: 'stripElement',
            cls: Ext.baseCSSPrefix + 'strip-el'
        });
 
        return template;
    },
 
    /**
     * @private
     */
    onTabTap: function(tab) {
        this.setActiveTab(tab);
    },
 
    /**
     * @private
     */
    applyActiveTab: function(newActiveTab, oldActiveTab) {
        var newTabInstance = this.parseActiveTab(newActiveTab);
 
        if (!newActiveTab && newActiveTab !== 0) {
            return;
        }
 
        if (!newTabInstance) {
            //<debug>
            if (oldActiveTab) {
                Ext.Logger.warn('Trying to set a non-existent activeTab');
            }
 
            //</debug>
            return;
        }
 
        return newTabInstance;
    },
 
    /**
     * @private
     */
    updateTabRotation: function(rotation) {
        var tabs = this.getTabs(),
            i;
 
        for (= 0; i < tabs.length; i++) {
            tabs[i].setRotation(rotation);
        }
    },
 
    /**
     * @private
     * Default pack to center when docked to the bottom, otherwise default pack to left
     */
    updateDocked: function(newDocked) {
        var me = this,
            layout = me.getLayout(),
            initialConfig = me.getInitialConfig(),
            i, vertical,
            pack, tabs;
 
        if (!initialConfig.layout || !initialConfig.layout.pack) {
            pack = (newDocked === 'bottom') ? 'center' : 'left';
 
            // layout isn't guaranteed to be instantiated so must test
            if (layout.isLayout) {
                layout.setPack(pack);
            }
            else {
                layout.pack = (layout && layout.pack) ? layout.pack : pack;
            }
        }
 
        vertical = (newDocked === 'right' || newDocked === 'left');
 
        if (layout.getVertical() !== vertical) {
            layout.setVertical(vertical);
        }
 
        // Let the tab buttons know the new tab bar position.
        tabs = this.getTabs();
 
        for (= 0; i < tabs.length; i++) {
            tabs[i].setTabPosition(newDocked);
        }
 
        this.callParent(arguments);
    },
 
    /**
     * @private
     * Sets the active tab
     */
    updateActiveTab: function(newTab, oldTab) {
        var me = this,
            animateIndicator = this.getAnimateIndicator();
 
        if (animateIndicator && newTab && oldTab && oldTab.parent) {
            me.animateTabIndicator(newTab, oldTab);
        }
        else {
 
            if (newTab) {
                newTab.setActive(true);
            }
 
            // Check if the parent is present, if not it is destroyed
            if (oldTab && oldTab.parent) {
                oldTab.setActive(false);
                this.previousTab = oldTab;
            }
 
        }
    },
 
    updateAnimateIndicator: function() {
        var me = this;
 
        if (me.$animateIndicatorElement) {
            me.$animateIndicatorElement.destroy();
        }
 
        if (me.$indicatorAnimationListeners) {
            me.$indicatorAnimationListeners.destroy();
        }
 
        me.$indicatorAnimationListeners = me.$animateIndicatorElement = null;
    },
 
    animateTabIndicator: function(newTab, oldTab) {
        var me = this,
            newTabElement = newTab.element,
            oldTabElement = oldTab.element,
            oldIndicator = oldTab.activeIndicatorElement,
            newIndicator = newTab.activeIndicatorElement,
            oldIndicatorProps, newIndicatorProps, animateIndicatorElement,
            vertical, heightOrWidth, calcIndicatorProps,
            tabBarPosition = this.getDocked();
 
        vertical = ((tabBarPosition === 'left') || (tabBarPosition === 'right'));
 
        calcIndicatorProps = function(tabElement, indicator) {
            // Construct an object that looks like this. If the
            // orientation is vertical, start and stop at the tab's y.
            // If it's horizontal, start and stop at the tab's x.
            // {
            //     width: 32,
            //     height: 4,
            //     x: 100,
            //     y: 300,
            //     'background-color': #fafafa
            // }
 
            var slideAnimObj = {
                width: indicator.getWidth(),
                height: indicator.getHeight(),
                x: indicator.getX(),
                y: tabElement.getY(),
                'background-color': indicator.getStyle('background-color')
            };
 
            if (!vertical) {
                slideAnimObj.x = tabElement.getX();
                slideAnimObj.y = indicator.getY();
            }
 
            return slideAnimObj;
        };
 
        oldIndicatorProps = calcIndicatorProps(oldTabElement, oldIndicator);
        newIndicatorProps = calcIndicatorProps(newTabElement, oldIndicator);
 
        newIndicator.hide();
 
        newTab.setActive(true);
        oldTab.setActive(false);
 
        // If the indicator has a height (if top or bottom) or width (if left or right)
        // then have it do its thing.
        heightOrWidth = vertical ? 'width' : 'height';
 
        if (oldIndicatorProps[heightOrWidth] || newIndicatorProps[heightOrWidth]) {
 
            animateIndicatorElement = me.$animateIndicatorElement;
 
            animateIndicatorElement = me.$animateIndicatorElement = me.element.insertFirst({
                cls: Ext.baseCSSPrefix + 'active-indicator-el'
            });
 
            if (me.$indicatorAnimationListeners) {
                me.$indicatorAnimationListeners.destroy();
                me.$indicatorAnimationListeners = null;
            }
 
            me.$indicatorAnimation = animateIndicatorElement.animate({
                duration: me.indicatorAnimationSpeed,
                from: oldIndicatorProps,
                to: newIndicatorProps
            });
 
            me.$indicatorAnimationListeners = me.$indicatorAnimation.on({
                destroyable: true,
                animationend: {
                    fn: function() {
                        newIndicator.show();
                        animateIndicatorElement.hide();
                        animateIndicatorElement.destroy();
                        me.$indicatorAnimationListeners.destroy();
                        me.$indicatorAnimation = me.$indicatorAnimationListeners = null;
                    },
                    single: true
                }
            });
        }
    },
 
    /**
     * Returns the tabs within the tab panel
     * @return {Ext.tab.Tab[]} 
     */
    getTabs: function() {
        return this.query('> tab');
    },
 
    /**
     * @private
     * Parses the active tab, which can be a number or string
     */
    parseActiveTab: function(tab) {
        // we need to call getItems to initialize the items, otherwise they will not exist yet.
        if (typeof tab === 'number') {
            return this.getTabs()[tab];
        }
        else if (typeof tab === 'string') {
            tab = this.getComponent(tab) || Ext.getCmp(tab);
        }
 
        return tab;
    },
 
    onItemAdd: function(item, index) {
        var me = this,
            defaultTabUI = me.getDefaultTabUI();
 
        if (item.isTab) {
            item.setRotation(me.getTabRotation());
            item.setTabPosition(me.getDocked());
 
            if (defaultTabUI && (item.getUi() == null)) {
                item.setUi(defaultTabUI);
            }
        }
 
        this.callParent([item, index]);
    },
 
    privates: {
        /**
         * @private
         * Determines the next tab to activate when one tab is closed.
         * @param {Ext.tab.Tab} tabToClose 
         */
        findNextActivatableTab: function(tabToClose) {
            var me = this,
                previousTab = me.previousTab,
                nextTab;
 
            if (tabToClose.getActive() && me.getItems().getCount() > 1) {
                if (previousTab && previousTab !== tabToClose && !previousTab.getDisabled()) {
                    nextTab = previousTab;
                }
                else {
                    nextTab = tabToClose.next('tab:not([disabled=true])') ||
                        tabToClose.prev('tab:not([disabled=true])');
                }
            }
 
            // If we couldn't find the next tab to activate, fall back
            // to the currently active one. We need to have a focused tab
            // at all times.
            return nextTab || me.getActiveTab();
        },
 
        /**
         * @private
         * @param {Ext.tab.Tab} tab 
         */
        closeTab: function(tab) {
            var me = this,
                nextActivatableTab = me.findNextActivatableTab(tab),
                parent = me.parent;
 
            if (parent && parent.isTabPanel) {
 
                if (tab.card.fireEvent('beforeclose', tab.card) === false) {
                    return;
                }
 
                // setting the active card on a tab panel also sets the active tab in the tab bar
                if (nextActivatableTab) {
                    parent.setActiveItem(nextActivatableTab.card);
                }
 
                // removing card from tab panel also removes the tab from the tab bar
                parent.remove(tab.card);
            }
            else {
                if (nextActivatableTab) {
                    me.setActiveTab(nextActivatableTab);
                }
 
                me.remove(tab);
            }
        }
    }
});