/**
 * A basic title component for a Panel Header
 */
Ext.define('Ext.panel.Title', {
    extend: 'Ext.Component',
    xtype: 'title',
 
    requires: [
        'Ext.Glyph'
    ],
 
    isTitle: true,
    
    // layout system optimization.  Allows autocomponent layout to measure height without
    // having to first know the width.
    noWrap: true,
 
    // 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
    textAlign: 'left',
    iconAlign: 'left',
    rotation: 0,
    text: ' ',
 
    beforeRenderConfig: {
        /**
         * @cfg {'left'/'center'/'right'} [textAlign='left']
         * @inheritdoc Ext.panel.Header#cfg-titleAlign
         * @accessor
         */
        textAlign: null,
 
        /**
         * @cfg {String} 
         * The title's text (can contain html tags/entities)
         * @accessor
         */
        text: null,
 
        /**
         * @cfg {Number/String} glyph
         * @inheritdoc Ext.panel.Header#cfg-glyph
         * @accessor
         */
        glyph: null,
 
        /**
         * @cfg {String} icon
         * @inheritdoc Ext.panel.Header#cfg-icon
         * @accessor
         */
        icon: null,
 
        /**
         * @cfg {'top'/'right'/'bottom'/'left'} [iconAlign='left']
         * alignment of the icon
         * @accessor
         */
        iconAlign: null,
 
        /**
         * @cfg {String} iconCls
         * @inheritdoc Ext.panel.Header#cfg-iconCls
         * @accessor
         */
        iconCls: null,
        
        /**
         * @cfg {'default'/0/1/2} [rotation='default']
         * @inheritdoc Ext.panel.Header#cfg-titleRotation
         * @accessor
         */
        rotation: null
    },
 
    autoEl: {
        role: 'presentation',
        // Required for Opera
        unselectable: 'on'
    },
    
    // In most cases the panel header title is purely presentational
    // and does not have any structural significance wrt Assistive Technologies.
    // The only exception is when the panel participates in Accordion layout;
    // in that case the title component has the role of 'tab' and its textEl
    // should not have any role to expose the text as the tab's accessible name.
    // Header component is aware of this participation and will reset textElRole.
    textElRole: 'presentation',
    
    // By default, panel title is not focusable; this only happens in Accordion layout.
    // This config option is overridable, and it will prime tabIndex to be used
    // without hardcoding it.
    tabIndex: 0,
 
    childEls: [
        'textEl',
        'iconEl',
        'iconWrapEl'
    ],
 
    renderTpl:
        '<tpl if="iconMarkup && iconBeforeTitle">{iconMarkup}</tpl>' +
        // unselectable="on" is required for Opera, other browsers
        // inherit unselectability from the header
        '<div id="{id}-textEl" data-ref="textEl"' +
            ' class="{textCls} {textCls}-{ui} {itemCls}{childElCls}" unselectable="on"' +
            '<tpl if="textElRole"> role="{textElRole}"</tpl>' +
        '>' +
            '{text}' +
        '</div>' +
        '<tpl if="iconMarkup && !iconBeforeTitle">{iconMarkup}</tpl>',
 
    iconTpl:
        '<div id="{id}-iconWrapEl" data-ref="iconWrapEl" role="presentation" ' +
                'class="{iconWrapCls} {iconWrapCls}-{ui} {iconAlignCls} {itemCls}{childElCls}"' +
                '<tpl if="iconWrapStyle"> style="{iconWrapStyle}"</tpl>>' +
            '<div id="{id}-iconEl" data-ref="iconEl" role="presentation" unselectable="on" ' +
                        'class="{baseIconCls} {baseIconCls}-{ui} {iconCls} {glyphCls}" style="' +
                '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>' +
                '<tpl if="glyph && glyphFontFamily">font-family:{glyphFontFamily};</tpl>">' +
                '<tpl if="glyph">{glyph}</tpl>' +
            '</div>' +
        '</div>',
 
    _textAlignClasses: {
        left: Ext.baseCSSPrefix + 'title-align-left',
        center: Ext.baseCSSPrefix + 'title-align-center',
        right: Ext.baseCSSPrefix + 'title-align-right'
    },
 
    _iconAlignClasses: {
        top: Ext.baseCSSPrefix + 'title-icon-top',
        right: Ext.baseCSSPrefix + 'title-icon-right',
        bottom: Ext.baseCSSPrefix + 'title-icon-bottom',
        left: Ext.baseCSSPrefix + 'title-icon-left'
    },
 
    _rotationClasses: {
        0: Ext.baseCSSPrefix + 'title-rotate-none',
        1: Ext.baseCSSPrefix + 'title-rotate-right',
        2: Ext.baseCSSPrefix + 'title-rotate-left'
    },
 
    _rotationAngles: {
        1: 90,
        2: 270
    },
 
    baseCls: Ext.baseCSSPrefix + 'title',
    _titleSuffix: '-title',
    _glyphCls: Ext.baseCSSPrefix + 'title-glyph',
    _iconWrapCls: Ext.baseCSSPrefix + 'title-icon-wrap',
    _baseIconCls: Ext.baseCSSPrefix + 'title-icon',
    _itemCls: Ext.baseCSSPrefix + 'title-item',
    _textCls: Ext.baseCSSPrefix + 'title-text',
 
    afterComponentLayout: function() {
        var me = this,
            rotation = me.getRotation(),
            lastBox, lastX, el;
 
        if (rotation && !Ext.isIE8) {
            // In IE8  we use a BasicImage filter to rotate the title
            // element 90 degrees.  The result is that what was the bottom left
            // corner is positioned exactly where the top left corner was
            // originally.  Since this is the desired result, no additional
            // positioning is needed in IE8.  In browsers that support CSS3 transform,
            // however, we use transform: rotate(90deg) to rotate the element.
            // CSS3 also provides a way to specify the position the rotated element
            // by changing the axis on which it is rotated using the transform-origin
            // property, but the required transform origin varies based on the
            // elements size, and would require some complex math to calculate.
            // To achieve the desired rotated position in modern browsers we use
            // a transform-origin of "0, 0" which means the top left corner of
            // the element is the rotation axis. After rotating 90 degrees we
            // simply move the element to the right by the same number of pixels
            // as its width.
            el = me.el;
            lastBox = me.lastBox;
            lastX = lastBox.x;
            el.setStyle(
                me._getVerticalAdjustDirection(),
                (lastX + ((rotation === 1) ? lastBox.width : -lastBox.height)) + 'px'
            );
        }
        this.callParent();
    },
 
    onRender: function() {
        var me = this,
            rotation = me.getRotation(),
            el = me.el;
        
        me.callParent();
        
        if (rotation) {
            el.setVertical(me._rotationAngles[rotation]);
        }
 
        if (Ext.supports.FixedTableWidthBug) {
            // Workaround for https://bugs.webkit.org/show_bug.cgi?id=130239 and
            // https://code.google.com/p/chromium/issues/detail?id=377190
            // See styleHooks for more details
            el._needsTableWidthFix = true;
        }
    },
 
    applyText: function(text) {
        if (!text) {
            text = '&#160;';
        }
        return text;
    },
    
    beforeRender: function() {
        var me = this;
        
        me.callParent();
        
        me.addCls(me._rotationClasses[me.getRotation()]);
        me.addCls(me._textAlignClasses[me.getTextAlign()]);
    },
 
    getIconMarkup: function() {
        return this.lookupTpl('iconTpl').apply(this.getIconRenderData());
    },
 
    getIconRenderData: function() {
        var me = this,
            icon = me.getIcon(),
            iconCls = me.getIconCls(),
            glyph = me.getGlyph(),
            glyphFontFamily,
            iconAlign = me.getIconAlign();
 
        // Transform Glyph to the useful parts
        if (glyph) {
            glyphFontFamily = glyph.fontFamily;
            glyph = glyph.character;
        }
 
        return {
            id: me.id,
            ui: me.ui,
            itemCls: me._itemCls,
            iconUrl: icon,
            iconCls: iconCls,
            iconWrapCls: me._iconWrapCls,
            baseIconCls: me._baseIconCls,
            iconAlignCls: me._iconAlignClasses[iconAlign],
            glyph: glyph,
            glyphCls: glyph ? me._glyphCls : '',
            glyphFontFamily: glyphFontFamily
        };
    },
 
    initRenderData: function() {
        var me = this,
            iconAlign, renderData;
 
        renderData = Ext.apply({
            text: me.getText(),
            textElRole: me.textElRole,
            id: me.id,
            ui: me.ui,
            itemCls: me._itemCls,
            textCls: me._textCls,
            iconMarkup: null,
            iconBeforeTitle: null
        }, me.callParent());
 
        if (me._hasIcon()) {
            iconAlign = me.getIconAlign();
            renderData.iconMarkup = me.getIconMarkup();
            renderData.iconBeforeTitle = (iconAlign === 'top' || iconAlign === 'left');
        }
        
        return renderData;
    },
    
    onAdded: function(container, pos, instanced) {
        var me = this,
            suffix = me._titleSuffix,
            baseCls = container.baseCls;
 
        me.addCls([
            baseCls + suffix,
            baseCls + suffix + '-' + container.ui
        ]);
        
        me.callParent([container, pos, instanced]);
    },
 
    applyGlyph: function(glyph, oldGlyph) {
        if (glyph) {
            if (!glyph.isGlyph) {
                glyph = new Ext.Glyph(glyph);
            }
            if (glyph.isEqual(oldGlyph)) {
                glyph = undefined;
            }
        }
        return glyph;
    },
 
    updateGlyph: function(glyph, oldGlyph) {
        var me = this,
            glyphCls = me._glyphCls,
            iconEl;
 
        if (me.rendered) {
            me._syncIconVisibility();
            iconEl = me.iconEl;
 
            if (glyph) {
                iconEl.dom.innerHTML = glyph.character;
                iconEl.addCls(glyphCls);
                iconEl.setStyle('font-family', glyph.fontFamily);
            } else if (oldGlyph !== glyph) {
                iconEl.dom.innerHTML = '';
                iconEl.removeCls(glyphCls);
            }
 
            if (me._didIconStateChange(oldGlyph, glyph)) {
                me.updateLayout();
            }
        }
    },
 
    updateIcon: function(icon, oldIcon) {
        icon = icon || '';
        var me = this,
            iconEl;
 
        if (me.rendered && icon !== oldIcon) {
            me._syncIconVisibility();
            iconEl = me.iconEl;
            
            iconEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');
            if (me._didIconStateChange(oldIcon, icon)) {
                me.updateLayout();
            }
        }
    },
 
    updateIconAlign: function(align, oldAlign) {
        var me = this,
            iconWrapEl = me.iconWrapEl,
            el, iconAlignClasses;
 
        if (me.iconWrapEl) {
            el = me.el;
            iconAlignClasses = me._iconAlignClasses;
 
            if (oldAlign) {
                iconWrapEl.removeCls(iconAlignClasses[oldAlign]);
            }
            iconWrapEl.addCls(iconAlignClasses[align]);
 
            // here we move the iconWrap to the correct position in the dom - before the
            // title el for top/left alignments, and after the title el for right/bottom
            if (align === 'top' || align === 'left') {
                el.insertFirst(iconWrapEl);
            } else {
                el.appendChild(iconWrapEl);
            }
            
            me.updateLayout();
        }
    },
 
    updateIconCls: function(cls, oldCls) {
        cls = cls || '';
        var me = this,
            iconEl;
 
        if (me.rendered && oldCls !== cls) {
            me._syncIconVisibility();
            iconEl = me.iconEl;
            
            if (oldCls) {
                iconEl.removeCls(oldCls);
            }
            iconEl.addCls(cls);
            if (me._didIconStateChange(oldCls, cls)) {
                me.updateLayout();
            }
        }
    },
 
    updateRotation: function(rotation, oldRotation) {
        var me = this,
            el, rotationClasses;
       
        if (me.rendered) {
            el = me.el;
            rotationClasses = me._rotationClasses;
            
            me.removeCls(rotationClasses[oldRotation]);
            me.addCls(rotationClasses[rotation]);
 
            el.setHorizontal();
            if (rotation) {
                el.setVertical(me._rotationAngles[rotation]);
            }
    
            // reset styles set by adjustTitlePosition (handles both rtl/ltr), and sizing
            // set by last layout run (this prevents parallel size from becoming perpendicular
            // size after rotation)
            el.setStyle({
                right: '',
                left: '',
                top: '',
                height: '',
                width: ''
            });
    
            me.lastBox = null;
 
            me.updateLayout();
        }
    },
 
    updateText: function(text) {
        if (this.rendered) {
            this.textEl.setHtml(text);
            this.updateLayout();
        }
    },
 
    updateTextAlign: function(align, oldAlign) {
        var me = this,
            textAlignClasses = me._textAlignClasses;
        
        if (me.rendered) {
            if (oldAlign) {
                me.removeCls(textAlignClasses[oldAlign]);
            }
            me.addCls(textAlignClasses[align]);
 
            me.updateLayout();
        }
    },
 
    privates: {
        // rtl hook
        _getVerticalAdjustDirection: function() {
            return 'left';
        },
 
        _didIconStateChange: function(old, current) {
            var currentEmpty = Ext.isEmpty(current);
            return Ext.isEmpty(old) ? !currentEmpty : currentEmpty;
        },
 
        _hasIcon: function() {
            return !!(this.getIcon() || this.getIconCls() || this.getGlyph());
        },
 
        _syncIconVisibility: function() {
            var me = this,
                el = me.el,
                hasIcon = me._hasIcon(),
                iconWrapEl = me.iconWrapEl,
                isBefore, iconAlign;
            
            if (hasIcon && !iconWrapEl) {
                // if an icon was configured, but we have not yet rendered an icon
                // element, we need to render it now.
                iconAlign = me.iconAlign;
                isBefore = (iconAlign === 'left' || iconAlign === 'top');
                
                el.dom.insertAdjacentHTML(
                    isBefore ? 'afterbegin' : 'beforeend',
                    me.getIconMarkup()
                );
            
                iconWrapEl = me.iconWrapEl = el[isBefore ? 'first' : 'last']();
                me.iconEl = iconWrapEl.first();
            }
 
            if (iconWrapEl) {
                iconWrapEl.setDisplayed(hasIcon);
            }
        }
    }
});