/*
 *  Copyright 2018 Anyware Services
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * This class handle the ribbon tooltip in two ways.
 * First you can create such a tooltip by getting the required configuration using the static method #create this way:
 * 
 *      tooltip: {
 *          title: 'My Ametys Tooltip', 
 *          image: 'resources/img/ametys.gif', 
 *          // glyphIcon: 'ametysicon-computer12',
 *          // iconDecorator: null
 *          text: 'Click on this button to get access to some features', 
 *          help: 'http://help.for.this.element', 
 *          inribbon: true
 *      }
 * 
 * Secondly, this class automatically initialize the tooltip manager by using this class to handle correctly the "inribbon" tooltip position 
 */
Ext.define("Ametys.ui.fluent.tip.Tooltip", {
    extend: "Ext.tip.QuickTip",
    alias: 'widget.ametys.quicktip',

    /** 
     * @private
     * @property {Number} quickShowInterval The time between last hidden time, to display automatically. See Ext.tip.ToolTipView#quickShowInterval.
     */
    quickShowInterval: 0,
    
    statics: {
        
        /**
         * @property {String} tipCls The CSS classname for tooltips.
         * @readonly
         * @private
         */
        tipCls: 'x-fluent-tooltip',
        
        /**
         * @property {String} helpText The generic help text at the bottom of tips with "help"
         * @readonly
         * @private
         */
        helpText: "{{i18n PLUGINS_CORE_UI_MSG_TOOLTIP_FOOTER_TEXT}}",
        
        /**
         * @property {Number} tooltipWidth The width of the tooltip if the tooltip doesn't have an image
         * @readonly
         * @private
         */
        tooltipWidth: 210,
        /**
         * @property {Number} imagedTooltipWidth The width of the tooltip if the tooltip has an image
         * @readonly
         * @private
         */
        imagedTooltipWidth: 318,
        
        /**
         * @property {Ext.Template} tipTemplate The template for tooltips
         * @readonly
         * @private
         */
        tipTemplate: Ext.create('Ext.XTemplate',
            '<tpl if="glyphIcon">',
            '<div class="{tipCls}-wrapper {tipCls}-withglyph">',
            '<tpl elseif="image">',
            '<div class="{tipCls}-wrapper {tipCls}-withimage">',
            '<tpl else>',
            '<div class="{tipCls}-wrapper">',
            '</tpl>',
                '<div class="{tipCls}-text">',
                    '<tpl if="image">',
                        '<span class="{tipCls}-img" style="height: {imageHeight + 2}px; width: {imageWidth + 2}px; background-image: url(\'{image}\'); display: inline-block;"></span>',
                    '</tpl>',
                    '<tpl if="glyphIcon">',
                        '<span class="{tipCls}-glyph {glyphIcon} size-{imageHeight} {iconDecorator} ametys-decoratortype-{iconDecoratorType} " style="font-size: {imageHeight}px; line-height: {imageHeight}px; height: {imageHeight + 2}px; width: {imageWidth + 2}px; display: inline-block;"></span>',
                    '</tpl>',
                    '{text}',
                '</div>',
                '<tpl if="help">',
                    '<div class="{tipCls}-footer ametysicon-question13"><a href="#{help}" onclick="Ametys.tool.ToolsManager.openTool(\'uitool-help\', {help: \'{help}\'}); return false;">{helpText}</a></div>',
                '</tpl>',
            '</div>',
            { compiled: true }
        ),
        
        /**
         * Create a config object for tooltip looking ribbon style.
         * This method is automatically called if the config of a registered tip has an image, a glyph icon, an help url or has inribbon specified
         * @param {Object} config A config object
         * @param {String} config.title The title in the tooltip
         * @param {Number} [config.width] An optional width in pixel to override the defaults.
         * @param {String} [config.image] An optional image path to display a main image in the tooltip.
         * @param {String} [config.imageHeight=48] Height, in pixels, of the image above.
         * @param {String} [config.imageWidth=48] Width, in pixels, of the image above.
         * @param {String} [config.glyphIcon] The CSS class for glyph to use as the icon. This is an alternative to the set of icons.
         * @param {String} [config.iconDecorator] The CSS class to use as decorator above the main icon.
         * @param {String} [config.iconDecoratorType=action-default] The type of the decorator. See #var-$ametys-tooltip-glyph-decorator-background-color 
         * @param {String} config.text The main text of the tooltip. Can contains html tags
         * @param {String} [config.help] The optionnal help url that is linked to an url to be displayed in the help too.
         * @param {Number} [config.dismissDelay=20000] The time before the tooltip automatically disapear is the mouse stay over the element
         * @param {HTMLElement/Ext.dom.Element/String} [config.target] The target element or string id to monitor for mouseover events to trigger showing this ToolTip.
         * @param {Boolean} [config.inribbon=true] Is the tooltip applying for a component of the ribbon? Default to true. It does matter to vertically align the tooltip to the ribbon.
         * @param {String} [config.anchor] If the tooltip is not "inribbon", you can specify an anchor to select its position relatively to the target. When null, the tooltip is following the mouse. Otherwise must be a string like "tl-bl" to specify the tooltip top-left will be aligned with the target bottom-left (and will not fly with the mouse anymore). Valid positions for each element are "tl, t, tr, l, c, r, bl, b, br".
         * @return {Object} A configuration for tooltip
         */
        create: function(config)
        {
            if (config)
            {
                config.cls = Ext.Array.from(config.cls);
                config.cls.push(this.tipCls);
                
                Ext.applyIf(config, {
                    imageHeight: 48,
                    imageWidth: 48,
                    text: '',
                    iconDecoratorType: Ametys.ribbon.element.ui.CommonController.ICON_DECORATOR_TYPE_DEFAULT_VALUE,
                    tipCls: this.tipCls,
                    helpText: this.helpText
                })
                
                return {
                    cls: config.cls,
                    title: config.title,
                    text: this.tipTemplate.apply(config),
                        
                    target: config.target,
                    showDelay: 900,
                    dismissDelay: config.dismissDelay ? config.dismissDelay : 20000,
                    help: config.help, // can be undefined
                    
                    containsAction: config.help || Ametys.ui.fluent.tip.Tooltip._containsAction(config.text),
                    
                    width: config.width ? config.width : (config.image || config.glyphIcon ? this.imagedTooltipWidth : this.tooltipWidth),
                            
                    ribbon: config.inribbon == false ? false : true,
                    anchor: config.anchor
                };
            }
            else
            {
                return null;
            }
        },
        
        /**
         * @private
         * If the tip text contains an action to click
         */
        _containsAction: function(text)
        {
            return text.includes("<a ") || text.includes("<button ");
        }
    },
    
    constructor: function()
    {
        this.callParent(arguments);
        
        this.on('mouseover', this._onMouseOver, this, { element: 'el'});
    },
    
    /**
     * @private
     * The mouseover listener to stop autohide process for inribbon tips
     * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
     * @param {HTMLElement} t The target of the event.
     */
    _onMouseOver: function(e, t)
    {
        if (this.lastActiveTarget && this.lastActiveTarget.containsAction)
        {
            this.clearTimer('hide');
            this.clearTimer('dismiss');    
        }
    },
    
    /**
     * @private
     * When leaving the tooltip target
     * @param {Ext.event.Event} e The event
     */
    onTargetOut: function(e)
    {
        if (!Ametys.isSuspended())
        {
            if (this.activeTarget)
            {
                this.lastActiveTarget = this.activeTarget;
            }
            
            this._currentEvent = e;
            this.callParent(arguments);
            delete this._currentEvent;
        }
    },
    
    /**
     * @private
     * When over the tooltip target
     * @param {Ext.event.Event} e The event
     */
    onTargetOver:function(e) 
    {
        if (!Ametys.isSuspended())
        {
            this._currentEvent = e;
            this.callParent(arguments);
            delete this._currentEvent;
        }
    },

    /**
     * @private
     * Display the quicktip
     * See Ext.tip.QuickTip#activateTarget
     */
    activateTarget: function() {
        var me = this,
            activeTarget = me.activeTarget,
            currentTarget = me.currentTarget,
            delay = activeTarget.showDelay,
            hideAction = activeTarget.hideAction;

        // If moved from target to target rapidly, the hide delay will not
        // have fired, so just update content and alignment.
        if (me.isVisible()) {
            /*me.updateContent();
            me.realignToTarget();*/
            
            Ext.tip.Tip.prototype.hide.apply(me); // OVERRIDING THE DEFAULT BEHAVIOR TO HIDE TOOLTIPS WHEN CHANGING BUTTONS
        }
        /*else*/ {
            if (activeTarget.showDelay) {
                delay = me.showDelay;
                me.showDelay = parseInt(activeTarget.showDelay, 10);
            }

            me.delayShow();

            if (activeTarget.showDelay) {
                me.showDelay = delay;
            }
            
            activeTarget.containsAction = Ametys.ui.fluent.tip.Tooltip._containsAction(activeTarget.text);
            if (activeTarget.containsAction)
            {
                activeTarget.fluent = true;
            }

            if (!(hideAction = activeTarget.hideAction)) {
                delete me.hideAction;
            }
            else {
                me.hideAction = hideAction;
            }
        }
    },        
    // ----
    
    /**
     * @private
     * Call when the mouse is out of the target
     */
    handleTargetOut: function() 
    {
        if (!this.activeTarget || !this.activeTarget.containsAction || !this.getEl() || !this.getEl().dom || !this.getEl().dom.contains(this._currentEvent.relatedTarget))
        {
            this.callParent(arguments);
        }
    },
    
    hide: function()
    {
        delete this.lastActiveTarget;
        this.callParent(arguments);
    },
    
    register: function(config)
    {
        if (config.fluent || config.help || config.image || config.inribbon || config.glyphIcon || Ametys.ui.fluent.tip.Tooltip._containsAction(config.text))
        {
            Ext.apply(config, this.self.create(config));
        }
        
        this.callParent([config]);
    },

    onPosition: function(x, y) 
    {
        this.callParent(arguments);

        var tip = this;
        
        
        var target = Ext.get(tip.activeTarget.target);
        if (target && target.component && target.component.menu 
                && (!target.component.hasCls("x-menu-item") || target.component.hasCls("x-menu-item-active")) // Do not displau an open sub-menu tooltip
                && (!target.component.hasCls("x-btn") || target.component.hasCls("x-btn-menu-active")) // Do not display an open button/menu tooltip, in ribbon it will overlap
            )
        {
            tip.hide();
            return;
        }
        
        if (tip.activeTarget.ribbon)
        {
            var newX, newY;
            
            var parent = target.parent("body > *[id^=viewport] > *[id^=viewport] > *");
            if (!parent)
            {
                parent = target.parent("body > *");
            }
            
            newX = target.getLeft() - 5;
            newY = parent.getBottom();
            
            newX = Math.max(newX, 3);
            newX = Math.min(newX, Ext.getBody().getRight() - this.getWidth());
            
            newY = Math.min(newY, Ext.getBody().getBottom() - this.getHeight());
            
            var currentXY = tip.getPosition();
            if (currentXY[0] != newX || currentXY[1] != newY)
            {
                tip.setPagePosition(newX, newY);
            }
        }
        else
        {
            var newX, newY, tparent;
            
            var anchor = tip.activeTarget.anchor;

            // required position
            var parent, parentMenu;
            if (target && (parent = target.parent(".x-menu")))
            {
                parentMenu = parent;
                
                if (parentMenu.getRight() + 2 + this.getWidth() > Ext.getBody().getRight())
                {
                    anchor = "tr-tl";
                }
                else
                {
                    anchor = "tl-tr";
                }
            }
            else if (target && target.is(".x-tab"))
            {
                if (target.is(".x-tab-top"))
                {
                    anchor = "tl-bl"
                }
                else if (target.is(".x-tab-left"))
                {
                    anchor = "tl-tr"
                }
                else if (target.is(".x-tab-right"))
                {
                    anchor = "tr-tl"
                }
                else if (target.is(".x-tab-bottom"))
                {
                    anchor = "bl-tl"
                }
            }
            else if (target && (tparent = target.parent()) && tparent.is(".ametys-description"))
            {
                anchor = "tl-bl";
            }
            
            var maxLoop = 2;
            while (anchor && maxLoop > 0)
            {
                var targetCorner = anchor.substring(anchor.indexOf("-") + 1);
                if (targetCorner.indexOf("l") != -1)
                {
                        newX = (parentMenu || target).getLeft() - 1;
                }
                else if (targetCorner.indexOf("r") != -1)
                {
                        newX = (parentMenu || target).getRight() + 1;
                }
                else
                {
                        newX = ((parentMenu || target).getLeft() + (parentMenu || target).getRight()) / 2;
                }

                if (targetCorner.indexOf("t") != -1)
                {
                        newY = target.getTop();
                }
                else if (targetCorner.indexOf("b") != -1)
                {
                        newY = target.getBottom();
                }
                else
                {
                        newY = (target.getTop() + target.getBottom()) / 2;
                }
                
                var tooltipCorner = anchor.substring(0, anchor.indexOf("-"));
                if (tooltipCorner.indexOf("l") != -1)
                {
                        newX -= 0;
                }
                else if (tooltipCorner.indexOf("r") != -1)
                {
                        newX -= this.getWidth();
                }
                else
                {
                        newX -= this.getWidth() / 2;
                }

                if (tooltipCorner.indexOf("t") != -1)
                {
                        newY -= 0;
                }
                else if (tooltipCorner.indexOf("b") != -1)
                {
                        newY -= this.getHeight();
                }
                else
                {
                        newY -= this.getHeight() / 2;
                }
                
                var constrainedNewXY = tip.calculateConstrainedPosition(null, [newX, newY]) || [newX, newY]; // Let's avoid an infinite loop
                
                if (constrainedNewXY[1] != newY && anchor == "tl-bl")
                {
                    // We tried to put tooltip under the element, but it is not fitting, let's loop and try above
                    maxLoop--;
                    anchor = "bl-tl";
                    continue;
                }
                
                var currentXY = tip.getPosition();
                if (currentXY[0] != constrainedNewXY[0] || currentXY[1] != constrainedNewXY[1])
                {
                    tip.setPagePosition(constrainedNewXY[0], constrainedNewXY[1]);
                }

                break;
            }
        }
    }
});

Ext.tip.QuickTipManager.init(null, {xtype: 'ametys.quicktip'});