/**
 * Simple header class which is used for on {@link Ext.panel.Panel} and {@link Ext.window.Window}.
 */
Ext.define('Ext.panel.Header', {
    extend: 'Ext.panel.Bar',
    requires: [
        'Ext.panel.Title',
        'Ext.panel.Tool'
    ],
    xtype: 'header',
 
    /**
     * @property {Boolean} isHeader 
     * `true` in this class to identify an object as an instantiated Header, or subclass thereof.
     */
    isHeader: true,
 
    defaultType: 'tool',
    indicateDrag: false,
    weight: -1,
    shrinkWrap: 3,
 
    // For performance reasons we give the following configs their default values on 
    // the class body.  This prevents the updaters from running on initialization in the 
    // default configuration scenario 
    iconAlign: 'left',
    titleAlign: 'left',
    titlePosition: 0,
    titleRotation: 'default',
 
    beforeRenderConfig: {
        /**
         * @cfg {Number/String} glyph
         * A numeric unicode character code to use as the icon for the panel header. The
         * default font-family for glyphs can be set globally using
         * {@link Ext#setGlyphFontFamily Ext.setGlyphFontFamily()}. Alternatively, this
         * config option accepts a string with the charCode and font-family separated by the
         * `@` symbol. For example '65@My Font Family'.
         */
        glyph: null,
 
        /**
         * @cfg {String} icon 
         * Path to image for an icon.
         *
         * There are no default icons that come with Ext JS.
         */
        icon: null,
 
        /**
         * @cfg {String} iconCls 
         * CSS class for an icon.
         *
         * There are no default icon classes that come with Ext JS.
         */
        iconCls: null,
 
        /**
         * @cfg {'top'/'right'/'bottom'/'left'} [iconAlign='left']
         * The side of the title to render the icon.
         */
        iconAlign: null,
 
        /**
         * @cfg {String/Ext.panel.Title}
         * The title text or config object for the {@link Ext.panel.Title Title} component.
         */
        title: {
            $value: {
                ariaRole: 'presentation',
                xtype: 'title',
                flex: 1
            },
            merge: function(newValue, oldValue) {
                if (typeof newValue === 'string') {
                    newValue = {
                        text: newValue
                    };
                }
 
                return Ext.merge(oldValue ? Ext.Object.chain(oldValue) : {}, newValue);
            }
        },
 
        /**
         * @cfg {String} [titleAlign='left']
         * The alignment of the title text.
         */
        titleAlign: null,
 
        /**
         * @cfg {Number} [titlePosition=0]
         * The ordinal position among the header items (tools and other components specified using the {@link #cfg-items} config)
         * at which the title component is inserted. See {@link Ext.panel.Panel#cfg-header Panel's header config}.
         *
         * If not specified, the title is inserted after any {@link #cfg-items}, but *before* any {@link Ext.panel.Panel#tools}.
         *
         * Note that if an {@link #icon} or {@link #iconCls} has been configured, then the icon component will be the
         * first item before all specified tools or {@link #cfg-items}. This configuration does not include the icon.
         */
        titlePosition: null,
        
        /**
         * @cfg {'default'/0/1/2} [titleRotation='default']
         * The rotation of the header's title text.  Can be one of the following values:
         *
         * - `'default'` - use the default rotation, depending on the dock position of the header
         * - `0` - no rotation
         * - `1` - rotate 90deg clockwise
         * - `2` - rotate 90deg counter-clockwise
         *
         * The default behavior of this config depends on the dock position of the header:
         *
         * - `'top'` or `'bottom'` - `0`
         * - `'right'` - `1`
         * - `'left'` - `1`
         */
        titleRotation: null
    },
 
    // a class for styling that is shared between panel and window headers 
    headerCls: Ext.baseCSSPrefix + 'header',
 
    /**
     * @event click
     * Fires when the header is clicked. This event will not be fired
     * if the click was on a {@link Ext.panel.Tool}
     * @param {Ext.panel.Header} this
     * @param {Ext.event.Event} e
     */
 
    /**
     * @event dblclick
     * Fires when the header is double clicked. This event will not
     * be fired if the click was on a {@link Ext.panel.Tool}
     * @param {Ext.panel.Header} this
     * @param {Ext.event.Event} e
     */
 
    /**
     * @cfg {Number} [itemPosition]
     * The index at which the any {@link #cfg-items} will be inserted into the Header's
     * items collection.  By default this will effectively be the `1` position
     * placing the items following the panel {@link Ext.panel.Panel#title title}.
     *
     * Set to `0` to have the items {@link #insert inserted} before the panel title.
     *
     *     Ext.create('Ext.panel.Panel', {
     *         title: 'Hello',
     *         width: 200,
     *         html: '<p>World!</p>',
     *         renderTo: Ext.getBody(),
     *         tools: [{
     *             type: 'pin'
     *         }],
     *         header: {
     *             //itemPosition: 0, // before panel title
     *             //itemPosition: 1, // after panel title
     *             //itemPosition: 2, // after pin tool
     *             items: [{
     *                 xtype: 'button',
     *                 text: 'Header Button'
     *             }]
     *         }
     *     });
     */
 
    initComponent: function() {
        var me = this,
            items = me.items,
            itemPosition = me.itemPosition,
            cls = [me.headerCls];
 
        me.tools = me.tools || [];
        me.items = items = (items ? items.slice() : []);
 
        if (itemPosition !== undefined) {
            me._userItems = items.slice();
            me.items = items = [];
        }
 
        me.indicateDragCls = me.headerCls + '-draggable';
        if (me.indicateDrag) {
            cls.push(me.indicateDragCls);
        }
 
        me.addCls(cls);
 
        me.syncNoBorderCls();
 
        // Add Tools 
        Ext.Array.push(items, me.tools);
        // Clear the tools so we can have only the instances. Intentional mutation of passed in array 
        // Owning code in Panel uses this array as its public tools property. 
        me.tools.length = 0;
        me.callParent();
 
        me.on({
            dblclick: me.onDblClick,
            click: me.onClick,
            element: 'el',
            scope: me
        });
    },
 
    /**
     * Add a tool to the header
     * @param {Object} tool 
     */
    addTool: function(tool) {
        // Even though the defaultType is tool, it may be changed, 
        // so let's be safe and forcibly specify tool 
        this.add(Ext.ComponentManager.create(tool, 'tool'));
    },
 
    afterLayout: function() {
        var me = this,
            frameBR, frameTR, frameTL, xPos;
 
        if (me.vertical) {
            frameTR = me.frameTR;
            if (frameTR) {
                // The corners sprite currently requires knowledge of the vertical header's 
                // width to correctly set the background position of the bottom right corner. 
                // TODO: rearrange the sprite so that this can be done with pure css. 
                frameBR = me.frameBR;
                frameTL = me.frameTL;
                xPos = (me.getWidth() - frameTR.getPadding('r') -
                    ((frameTL) ? frameTL.getPadding('l') : me.el.getBorderWidth('l'))) + 'px';
                frameBR.setStyle('background-position-x', xPos);
                frameTR.setStyle('background-position-x', xPos);
            }
        }
        this.callParent();
    },
 
    applyTitle: function(title, oldTitle) {
        var me = this,
            isString, configHasRotation;
 
        title = title || '';
 
        isString = typeof title === 'string';
        if (isString) {
            title = {
                text: title
            };
        }
 
        if (oldTitle) {
            // several title configs can trigger layouts, so suspend before setting 
            // configs in bulk 
            Ext.suspendLayouts();
            oldTitle.setConfig(title);
            Ext.resumeLayouts(true);
            title = oldTitle;
        } else {
            if (isString) {
                title.xtype = 'title';
            }
            title.ui = me.ui;
            title.headerRole = me.headerRole;
            configHasRotation = ('rotation' in title);
 
            title = Ext.create(title);
            
            // avoid calling the title's rotation updater on initial startup in the default scenario 
            if (!configHasRotation && me.vertical && me.titleRotation === 'default') {
                title.rotation = 1;
            }
        }
 
        return title;
    },
 
    applyTitlePosition: function(position) {
        var max = this.items.getCount();
 
        if (this._titleInItems) {
            --max;
        }
        return Math.max(Math.min(position, max), 0);
    },
 
    beforeLayout: function () {
        this.callParent();
        this.syncBeforeAfterTitleClasses();
    },
 
    beforeRender: function() {
        var me = this,
            itemPosition = me.itemPosition;
 
        me.protoEl.unselectable();
        me.callParent();
 
        if (itemPosition !== undefined) {
            me.insert(itemPosition, me._userItems);
        }
    },
 
    /**
     * Gets the tools for this header.
     * @return {Ext.panel.Tool[]} The tools
     */
    getTools: function(){
        return this.tools.slice();
    },
 
    onAdd: function(component, index) {
        var tools = this.tools;
        this.callParent([component, index]);
        if (component.isTool) {
            tools.push(component);
            tools[component.type] = component;
        }
    },
 
    onAdded: function(container, pos, instanced) {
        this.syncNoBorderCls();
        this.callParent([container, pos, instanced]);
    },
 
    onRemoved: function(container, pos, instanced) {
        this.syncNoBorderCls();
        this.callParent([container, pos, instanced]);
    },
 
    setDock: function(dock) {
        var me = this,
            title = me.getTitle(),
            rotation = me.getTitleRotation(),
            titleRotation = title.getRotation();
 
        Ext.suspendLayouts();
 
        me.callParent([dock]);
 
        if (rotation === 'default') {
            rotation = (me.vertical ? 1 : 0);
 
            if (rotation !== titleRotation) {
                title.setRotation(rotation);
            }
            
            if (me.rendered) {
                // remove margins set on items by box layout last time around. 
                // TODO: this will no longer be needed when EXTJS-13359 is fixed 
                me.resetItemMargins();
            }
        }
 
        Ext.resumeLayouts(true);
    },
 
    updateGlyph: function(glyph) {
        this.getTitle().setGlyph(glyph);
    },
 
    updateIcon: function(icon) {
        this.getTitle().setIcon(icon);
    },
 
    updateIconAlign: function(align, oldAlign) {
        this.getTitle().setIconAlign(align);
    },
 
    updateIconCls: function(cls) {
        this.getTitle().setIconCls(cls);
    },
 
    updateTitle: function(title, oldTitle) {
        if (!oldTitle) {
            this.insert(this.getTitlePosition(), title);
            this._titleInItems = true;
        }
        // for backward compat with 4.x, set titleCmp property 
        this.titleCmp = title;
    },
 
    updateTitleAlign: function(align, oldAlign) {
        this.getTitle().setTextAlign(align);
    },
 
    updateTitlePosition: function(position) {
        this.insert(position, this.getTitle());
    },
 
    updateTitleRotation: function(rotation) {
        if (rotation === 'default') {
            rotation = (this.vertical ? 1 : 0);
        }
        this.getTitle().setRotation(rotation);
    },
 
    privates: {
        fireClickEvent: function(type, e){
            var toolCls = '.' + Ext.panel.Tool.prototype.baseCls;
            if (!e.getTarget(toolCls)) {
                this.fireEvent(type, this, e);
            }
        },
 
        getFocusEl: function() {
            return this.el;
        },
 
        getFramingInfoCls: function(){
            var me = this,
                cls = me.callParent(),
                owner = me.ownerCt;
 
            if (!me.expanding && owner && (owner.collapsed || me.isCollapsedExpander)) {
                cls += '-' + owner.collapsedCls;
            }
            return cls + '-' + me.dock;
        },
 
        onClick: function(e) {
            this.fireClickEvent('click', e);
        },
 
        onDblClick: function(e){
            this.fireClickEvent('dblclick', e);
        },
 
        syncBeforeAfterTitleClasses: function(force) {
            var me = this,
                items = me.items,
                childItems = items.items,
                titlePosition = me.getTitlePosition(),
                itemCount = childItems.length,
                itemGeneration = items.generation,
                syncGen = me.syncBeforeAfterGen,
                afterCls, beforeCls, i, item;
 
            if (!force && (syncGen === itemGeneration)) {
                return;
            }
            me.syncBeforeAfterGen = itemGeneration;
 
            for (= 0; i < itemCount; ++i) {
                item = childItems[i];
 
                afterCls  = item.afterTitleCls  || (item.afterTitleCls  = item.baseCls + '-after-title');
                beforeCls = item.beforeTitleCls || (item.beforeTitleCls = item.baseCls + '-before-title');
 
                if (!me.title || i < titlePosition) {
                    if (syncGen) {
                        item.removeCls(afterCls);
                    } // else first time we won't need to remove anything... 
                    item.addCls(beforeCls);
                } else if (> titlePosition) {
                    if (syncGen) {
                        item.removeCls(beforeCls);
                    }
                    item.addCls(afterCls);
                }
            }
        },
 
        syncNoBorderCls: function() {
            var me = this,
                ownerCt = this.ownerCt,
                noBorderCls = me.headerCls + '-noborder';
 
            // test for border === false is needed because undefined is the same as true 
            if (ownerCt ? (ownerCt.border === false && !ownerCt.frame) : me.border === false) {
                me.addCls(noBorderCls);
            } else {
                me.removeCls(noBorderCls);
            }
        }
    } // private 
});