/**
 * Create a tooltip.
 *
 * The `.uiTooltip()` method create a tooltip from each element in the set of matched elements.
 *
 * ### Examples
 * In this example a tooltip is created for the text above. The data-js attribute within a span tag is used to execute the tooltip. By hovering over the text the tooltip text appears and gets not hidden by click, because the interactive option is set to true. The parent div tag is the context element for the show/hide handling.
 *
 * JavaScript:
 *
 *
 *     <div>
 *      some text
 *      <span class="Tooltip ep-js"  data-js="ep.uiTooltip({'interactive' : true, 'context':'parent'});">
 *       tooltip text
 *      </span>
 *     </div>
 *
 *
 * @class jQuery.ui.uiTooltip
 * @extends jQuery.widget
 *
 * @uses ep
 * @uses jQuery.ui.widget
 * @uses ep.fn.sprite
 * @uses ep.fn.contextOrientation
 * @since 6.11.0
 */

/**
 * @event show This event is triggered when the tooltip was displayed.
 * @member jQuery.ui.uiTooltip
 * @since 6.11.0
 */

/**
 * @event hide This event is triggered when the tooltip was hidden.
 * @member jQuery.ui.uiTooltip
 * @since 6.11.2
 */

/**
 * Call up a previously attached tooltip.
 *
 * @param {Function} [callback] A method to execute after the tooltip was displayed (after animated show).
 *
 * @method show
 * @member jQuery.ui.uiTooltip
 *
 * @since 6.11.0
 */

/**
 * Hide a previously displayed tooltip.
 *
 * @param {Function} [callback] A method to execute after the tooltip was hidden (after animated hide).
 *
 * @method hide
 * @member jQuery.ui.uiTooltip
 *
 * @since 6.11.0
 */

/**
 * @cfg {String} event A event handle when show/hide the tooltip. Supported handles: 'click' or 'hover'
 */

/**
 * @cfg {Object,String} context A DOM element, jQuery object or selector to specify a context element for the show/hide handling.
 */

/**
 * @cfg {Boolean} interactive A boolean indication whether to prevent hide when clicked in the tooltip.
 */

/**
 * @cfg {String} orientation Specify the position of the tooltip to the context element. Supported orientations: 'top', 'left', 'right', 'bottom', 'center', 'middle'. If orientation empty the context will be the cursor.
 */

/**
 * @cfg {Array} offsetAdjust An array of numbers for x and y position adjust. The numbers will be intepreted as pixel.
 */

/**
 * @cfg {String} addClass The tooltip wraps an element around the givn element, the addClass option specifies classes to add to the wrap element.
 */

/**
 * @cfg {Number} showDelay A delay duration to show the tooltip.
 */

/**
 * @cfg {Number} hideDelay A delay duration to hide the tooltip.
 */

/**
 * @cfg {Boolean} closeButton A boolean indication whether to add a close button to the tooltip.
 */

/**
 * See `jQuery.ui.uiTooltip` for details.
 *
 * @param {Object} [options] A map of additional options pass to the method.
 * @param {String} event A event handle when show/hide the tooltip. Supported handles: 'click' or 'hover'
 * @param {Object,String} context A DOM element, jQuery object or selector to specify a context element for the show/hide handling.
 * @param {Boolean} interactive A boolean indication whether to prevent hide when clicked in the tooltip.
 * @param {String} orientation Specify the position of the tooltip to the context element. Supported orientations: 'top', 'left', 'right', 'bottom', 'center', 'middle'. If orientation empty the context will be the cursor.
 * @param {Array} offsetAdjust An array of numbers for x and y position adjust. The numbers will be intepreted as pixel.
 * @param {String} addClass The tooltip wraps an element around the givn element, the addClass option specifies classes to add to the wrap element.
 * @param {Number} showDelay A delay duration to show the tooltip.
 * @param {Number} hideDelay A delay duration to hide the tooltip.
 * @param {Boolean} closeButton A boolean indication whether to add a close button to the tooltip.
 *
 * @fires show
 * @fires hide
 *
 * @method uiTooltip
 * @member jQuery
 *
 * @since 6.11.0
 */

/*
 * @copyright       © Copyright 2006-2010, epages GmbH, All Rights Reserved.
 *
 * @module          ep.ui.tooltip
 */

/*jslint nomen: true*/
/*global define*/

define("ep/ui/tooltip", [
    "jquery",
    "ep",

    "jquery/ui/widget",
    "ep/ui/core",
    "ep/fn/sprite",
    "ep/fn/contextorientation"
], function ($, ep) {
    'use strict';

    var EVENTS = {
        'focus': ['focus.uiTooltip', 'blur.uiTooltip'],
        'click': ['click.uiTooltip'],
        'hover': ['mouseenter.uiTooltip', 'mouseleave.uiTooltip']
    };

    $.widget("ui.uiTooltip", /* my.parent.widget,*/ {

        options: {
            event: 'hover',
            context: 'prev',
            interactive: false,
            orientation: 'bottom',
            // can be top,left,right,bottom,center,middle,cursor
            offsetAdjust: [0, 0],
            addClass: '',
            showDelay: 200,
            hideDelay: 400,
            closeButton: false,
            holdFocus: false,
            focusDelay: undefined,
            iconClass: 'ep-info-bubble'
        },

        unity: ((ep.config.unity !== undefined) && (ep.config.unity === true)) ? true : false,

        _create: function () {
            var self = this,
                o = self.options,
                href = '',
                orientations = {
                    bubble: '',
                    tooltip: ''
                },
                additionalClass,
                tooltipOrientation = "";

            href += 'javascript';
            href += ':void(0)';

            self.elem = self.element
                .removeClass('HideElement ToolTip Tooltip') //both classes define display:none
                .removeAttr('style');

            // add context + bind events
            if (typeof o.context === 'string' && o.context.toLowerCase() === 'prev') {
                self.context = self.element.prev();
            } else if (typeof o.context === 'string' && o.context.toLowerCase() === 'next') {
                self.context = self.element.next();
            } else if (typeof o.context === 'string' && o.context.toLowerCase() === 'parent') {
                self.context = self.element.parent();
            } else {
                self.context = $(o.context);
            }

            additionalClass = (o.additionalClass !== undefined) ? o.additionalClass : "";

            if (self.unity) {
                // refactor orientations for CSS orientation classes
                o.orientation.split(' ').forEach(function (element, index, array) {
                    orientations.bubble += " ep-tooltip-" + element;
                    orientations.tooltip += " ep-tooltip-orientation-" + element;
                });
            }

            // tooltip handling for UNITY (CSS based)
            if ((o.oldhandling === undefined) && self.unity) {
                self.unityBubble = $('<span class="ep-info-bubble-wrapper' + orientations.bubble + ' ' + o.addClass + '"><span class="ep-info ep-info-adapt ' + o.iconClass + '"><span class="ep-blend-layer"></span></span><div class="ep-info-bubble-explanation">' +  self.elem.html() + '</div></span>');
                self.context.replaceWith(self.unityBubble);
                self.elem.remove();
                return;
            }

            // add wrapper around tooltip content
            self.wrap = ep('<div>').addClass(additionalClass).addClass('ep-uiTooltip ui-front' + (o.addClass ? ' ' + o.addClass : '') + orientations.tooltip).attr('style', self.elem.attr('style') || '').appendTo('body').append(self.elem).hide();

            if (o.closeButton) {
                /*jslint sloppy: true, browser: true*/
                self.closeButton = ep('<a>').attr({
                    'href': href,
                    'class': 'ep-uiTooltip-closeButton'
                }).text('x').on('click', {}, $.proxy(self, '_hide')).appendTo(self.wrap);
            }
        },

        _init: function () {
            // no additional tooltip handling for UNITY necessary
            if ((this.options.oldhandling === undefined) && (ep.config.unity !== undefined) && (ep.config.unity === true)) {
                return;
            }

            this.wrap.off('.uiTooltip');
            this.context.off('.uiTooltip');

            if(!this.options.holdFocus) {
                this.wrap
                    .on('mouseenter.uiTooltip', $.proxy(this, '_enter'))
                    .on('mouseleave.uiTooltip', $.proxy(this, '_leave'));
            }

            var e = EVENTS[this.options.event];
            if (e[0]) {
                this.context.on(e[0], $.proxy(this, '_show'));
            }
            if (e[1]) {
                this.context.on(e[1], $.proxy(this, '_hide'));
            }
        },

        _moveToTop: ep.ui.createMoveToTop('wrap'),

        _enter: function () {
            if (this.options.interactive) {
                this.cursorInside = true;
                this.wrap.stop(true, true);
            }
        },

        _leave: function () {
            this.cursorInside = false;
            this.hide();
        },

        _show: function (event, callback) {
            var o = this.options,
                context,
                orient,
                add,

                eventTrigger;

            if (!o.disabled) {
                context = this.context;
                orient = o.orientation;
                add = o.offsetAdjust;

                //Move to top, but keep silent
                this._moveToTop({}, true);

                if (orient === 'cursor') {
                    orient = undefined;
                }
                if (event instanceof $.Event && !orient && o.event !== 'focus') {
                    context = event;
                } else if ($.isArray(event)) {
                    context = new $.Event({
                        pageX: event[0],
                        pageY: event[1]
                    });
                }

                // Set position
                this.wrap.contextOrientation(context, orient, add);

                // Hide all tooltips
                $('.ep-uiTooltip').stop(true, true).hide();

                // Start show current tooltip
                this.wrap.delay(o.showDelay).fadeIn('normal', callback);

                this.elem.triggerHandler('show');

                // If tooltip context inside wrapped by a 'ui-front' element trigger focus on context without event bubbling
                if (this.context.parents('.ui-front').length) {
                    eventTrigger = $.Event({
                        type: 'focus'
                    });
                    eventTrigger.stopPropagation();

                    if (typeof o.focusDelay === "undefined") {
                        this.context.trigger(eventTrigger);
                    } else {
                        var _this = this;
                        window.setTimeout(function () {
                          _this.context.trigger(eventTrigger);
                        }, isNaN(o.focusDelay) ? 10 : o.focusDelay);
                    }
                }
            }
        },

        show: function (callback) {
            this._show({}, callback);
        },

        _hide: function (event) {
            if (!this.cursorInside || event.data) {
                this.hide();
            }
        },

        hide: function (callback) {
            var o = this.options;
            if (!o.disabled) {
                this.wrap.stop(true, true).delay(o.interactive ? o.hideDelay + 400 : o.hideDelay).fadeOut('normal', callback);
                this.elem.triggerHandler('hide');

                $('html').off('mouseup', $.proxy(this, '_hide'));
            }
        },

        _setOption: function (key, value) {
            if (key === 'context') {
                this.context.off('.uiTooltip');
                this.context = $(value);
            }
            return this._superApply(arguments);
        },

        destroy: function () {
            this.wrap.off('.uiTooltip');
            this.elem.unwrap();
            this.context.off('.uiTooltip');

            $('html').off('mouseup', $.proxy(this, '_hide'));

            this._superApply(arguments);
        }

    });

    return ep;

});