(function($){
    function handler(e) {
        var el = e.target;
        var data = $(el).data("gcsTooltip");
        while (!data && el) {
            el = el.parentNode;
            data = $(el).data("gcsTooltip");
        }
        if (!data) return;
        if (el != e.target) {
            // TODO hit-test -> mouse over el?
        }
        var $$ = $.Tooltip;
        $$.clearTimeout('show');
        if (e.type == "mouseover") {
            $$.clearTimeout('hide');
            $$.setTimeout(function(){ $$.show(el, data); }, 'show');
        } else if (e.type == "mouseout") {
            $$.setTimeout(function(){ $$.hide(data); }, 'hide');
        }
    }

    $.Tooltip = {
        defaultOptions: {styleClass: 'tooltip', html: false, fadeIn: 0, fadeOut: 0, left: -18, top: 0},
        showDelay: 500,
        hideDelay: 450,
        showTimeout: false,
        hideTimeout: false,
        checkInterval: 0,
        tooltip: '<div class="tooltip"/>',
        textContainer: false,
        insert: function() {
            this.tooltip.appendTo($("body"));
        },
        show: function(el, opts) {
            var options = $.extend({}, this.defaultOptions, opts);
            this.clearTimeout('show');
            if (!$(el).is('*:visible') || $(el).is('.jgcs-notooltip') || $(el).parents('.jgcs-notooltip').length) return;
            if (!this.textContainer) {
                this.tooltip = $(this.tooltip);
                this.tooltip.css({position: 'absolute'});
                if (options.fadeIn > 0) this.tooltip.hide();
                this.insert();
                this.tooltip.data("gcsTooltip", opts);
                this.tooltip.bind('mouseout', handler);
                this.tooltip.bind('mouseover', function(e) {
                    $.Tooltip.clearTimeout('hide');
                    e.stopPropagation();
                });
                this.textContainer = this.tooltip.find(':empty:last');
                if (!this.textContainer.length) this.textContainer = this.tooltip.eq(0);
            }
			this.tooltip.attr('class', options.styleClass);
            this.textContainer[options.html ? 'html' : 'text'](options['text']);
            if (options.html && $.isFunction(this.textContainer.enhance)) this.textContainer.enhance(); // TODO enhance event
            this.position(el, options);
            if (options.fadeIn > 0) {
                this.tooltip.fadeIn(options.fadeIn);
            } else {
                this.tooltip.show();
            }
            if (this.checkInterval) window.clearInterval(this.checkInterval);
            var $$ = this;
            this.checkInterval = window.setInterval(function(){
                if (!$(el).is('*:visible')) $$.hide({});
            }, 100);
        },
        position: function(el, options){
            this.textContainer.css('max-height', '');
            var offset = $(el).offset();
            var left = offset.left + options.left;
            if (left < 5) left = 5;
            var top = offset.top - this.tooltip.height() + options.top;
            if (top < 5) {
                top = offset.top + $(el).outerHeight() + 10;
            }

            this.tooltip.css({left: left, top: top});
        },
        hide: function(options){
            if (this.checkInterval) window.clearInterval(this.checkInterval);
            if (this.textContainer) {
                var fadeOut = options.fadeOut || this.defaultOptions.fadeOut;
                if (fadeOut > 0) {
                    this.tooltip.fadeOut(fadeOut);
                } else {
                    this.tooltip.hide();
                }
            }
            this.clearTimeout('hide');
        },
        clearTimeout: function(s){
            if (this[s + 'Timeout']) {
                window.clearTimeout(this[s + 'Timeout']);
                this[s + 'Timeout'] = false;
            }
        },
        setTimeout: function(vCode, s){
            if (!this[s + 'Timeout']) {
                this[s + 'Timeout'] = window.setTimeout(vCode, this[s + 'Delay']);
            }
        }
    };
    $.fn.tooltip = function(options){
        this.each(function(){
            var $$ = $(this);
            var local = $$.data("gcsTooltip");
            if (local) {
                if (options === false) {
                    $$.attr("title", local.text);
                    $$.removeData("gcsTooltip");
                } else {
                    $.extend(local, options);
                }
            } else if (options !== false) {
                local = $.extend({text: $$.attr('title')}, options);
                $$.data("gcsTooltip", local);
                $$.removeAttr('title');
            }
        });
        return this;
    };
    $(function(){
        $("body").bind("mouseover mouseout", handler);
    });
})(jQuery);

