(function(){
    var Tooltip = Class.create({
        tooltips: [],
        
        // Constructor
        initialize: function(element, content, options) {
            this.element = $(element);
            
            this.element.identify();
            
            this.tooltips.push(this);
            
            // Set options
            this.options = Object.extend({
                title:              null,
                width:              'auto',
                height:             'auto',
                offsetTop:          0,
                offsetLeft:         0,
                closeButton:        false,
                closeButtonHover:   false,
                closeOtherTooltipsOnOpen: true,
                target:             false,
                delay:              0.15,
                'class':            'tooltip',
                zIndex:             999,
                effect:             false,
                hook:               {
                    element:            false,
                    mouse:              false
                },
                placement:          {
                    element:            'topRight',
                    tooltip:            'bottomLeft'
                },
                stem:               false,
                beforeOpen:         Prototype.emptyFunction,
                afterOpen:          Prototype.emptyFunction,
                beforeClose:        Prototype.emptyFunction,
                afterClose:         Prototype.emptyFunction,
                beforeUserClose:    Prototype.emptyFunction,
                afterUserClose:     Prototype.emptyFunction
                
            }, options || {});
            
            // Hook to the element by default
            if (!this.options.hook.element) this.options.hook.element = this.element;
            
            // Default effect options
            if (this.options.effect) {
                if (!this.options.effect.type) {
                    this.options.effect = false;
                } else {
                    this.options.effect.options = Object.extend({}, this.options.effect.options || {});
                }
            }
            
            // Create elements
            this.body = new Element('div', {'class': 'body'});
            this.tooltip = new Element('div', {'class': this.options['class']});
            
            // Create title
            if (this.options.title) {
                this.title = new Element('div', {'class': 'title'}).update(this.options.title);
                this.tooltip.insert(this.title);
            }
            
            // Create body
             if (Object.isString(content)) {
                this.body.update(content);
            } else if (content.parentNode) {
                this.body.update($(content).remove());
            } else {
                this.body.update($(content));
            }
            
            this.tooltip.insert(this.body);
            document.body.appendChild(this.tooltip);

            this.tooltip.identify();
            
            this.body.setStyle({
                overflow: 'hidden'
            });
            
            this.tooltip.hide();
            
            if (this.options.closeButton && this.title) {
                this.buttons = new Element('div', {'class': 'buttons'});
                if (this.options.closeButtonHover) {
                    this.buttonClose = new Element('img', {'src': this.options.closeButton}).
                        observe('click', this.closeTooltip.bindAsEventListener(this)).
                        observe('mouseover', function(event) {
                            event.findElement('img').writeAttribute('src', this.options.closeButtonHover);
                        }.bindAsEventListener(this)).
                        observe('mouseout', function(event) {
                            event.findElement('img').writeAttribute('src', this.options.closeButton);
                        }.bindAsEventListener(this));
                    this.buttons.insert(this.buttonClose);
                }
                this.title.insert({top: this.buttons});
            } else {
                //this.element.observe('mouseout', this.hideTooltip.bindAsEventListener(this));
                document.observe('mouseover', this.mouseoverDocument.bindAsEventListener(this));
            }
            
            
            if (this.options.hook.mouse) {
                var followMouse = function(event)
                {
                    if (!this.tooltip.visible()) return;
                
                    this.setPosition(
                        event.pointerY() + this.options.offsetTop,
                        event.pointerX() + this.options.offsetLeft
                    );
                }
                document.observe('mousemove', followMouse.bindAsEventListener(this));
            }
            
            
            if (this.options.maxWidth > 0 && this.tooltip.getWidth() > this.options.maxWidth) {
                this.options.width = this.options.maxWidth;
            }
            
            if (this.options.maxHeight > 0 && this.tooltip.getHeight() > this.options.maxHeight) {
                this.options.height = this.options.maxHeight;
            }
            
            this.delayed = false;
            
            // Set the base tooltip styles
            this.tooltip.setStyle({
                position:       'absolute',
                zIndex:         this.options.zIndex,
                top:            this.getTooltipTop(),
                left:           this.getTooltipLeft(),
                width:          Object.isNumber(this.options.width) ? this.options.width + 'px' : this.options.width
            });
            
            this.body.setStyle({
                height:         Object.isNumber(this.options.height) ? this.options.height + 'px' : this.options.height
            });
            
            this.tooltipDimensions = this.tooltip.getDimensions();
            
            // Create stem
            this.createStem(this.options.stem);
            
            // Register observers
            this.element.observe('mouseover', this.showTooltipDelayed.bindAsEventListener(this));
            this.tooltip.select('.close').invoke('observe', 'click', this.closeTooltip.bindAsEventListener(this));
        },
        
        // Shows the tooltip after a delay
        showTooltipDelayed: function(event)
        {
            if (this.delayed) return;
            if (this.options.delay) {
                this.delayed = this.showTooltip.bind(this).delay(this.options.delay, event);
            } else {
                this.showTooltip(event);
            }
        },
        
        // Shows the tooltip
        showTooltip: function(event)
        {
            if (this.tooltip.visible()) return;
            if (this.options.closeOtherTooltipsOnOpen) {
                this.tooltips.each(function(instance){
                    instance.closeTooltip(event, true);
                });
            }
            try {
                this.options.beforeOpen(this.element, this.tooltip);
                if (!this.options.hook.mouse) {
                    this.setPosition();
                }
                
                if (this.options.effect) {
                    new Effect[this.options.effect.type](this.tooltip, this.options.effect.options);
                } else {
                    this.tooltip.show();
                }
                if (this.delayed) this.delayed = false;
                this.options.afterOpen(this.element, this.tooltip);
            } catch (e) {
                // Ignore
            }
        },
        
        // Mouse over the document
        mouseoverDocument: function(event)
        {
            //alert(event.findElement('#' + this.element.identify()));
            if (undefined == event.findElement('#' + this.element.identify())
                && undefined == event.findElement('#' + this.tooltip.identify())) 
            {
                this.closeTooltip(event, true);
            }
        },
        
        // Hides the tooltip
        closeTooltip: function(event, autoclosed)
        {
            this.clearDelay();
            if (!this.tooltip.visible()) return;
            if (event) event.stop();
            autoclosed = autoclosed || false;
            try {
                this.options.beforeClose(this.element, this.tooltip);
                if (!autoclosed) this.options.beforeUserClose(this.element, this.tooltip);
                this.tooltip.hide();
                if (!autoclosed) this.options.afterUserClose(this.element, this.tooltip);
                this.options.afterClose(this.element, this.tooltip);
            } catch (e) {
                // Ignore
            }
        },
        
        // Clears any delays
        clearDelay: function()
        {
            if (this.delayed) {
                window.clearTimeout(this.delayed);
                this.delayed = false;
            }
            
            return this;
        },
        
        createStem:    function(placement)
        {
            if (!placement) return;
            
            var width, 
                height, 
                top = 0,
                left = 0,
                stem,
                length = 14,
                thickness = 14,
                start = 0,
                stemCellMargin = '0 auto 0 0',
                stemCellWidth,
                color = this.tooltip.getStyle('borderLeftColor');
            
            stem = new Element('div').setStyle({
                background: 'none',
                position:   'absolute'
            });
            
            var dimensions = this.tooltip.getDimensions();
            
            switch (placement) {
                case 'top':
                    stemCellMargin = '0 auto';
                    break;
                    
                case 'bottom':
                    stemCellMargin = '0 auto';
                    break;
                    
                case 'left':
                    stemCellMargin = 'auto 0';
                    break;
                    
                case 'right':
                    stemCellMargin = 'auto 0';
                    break;
                    
                case 'topLeft':
                    width = thickness;
                    height = length;
                    top = -height;
                    left = 2;
                    break;
                
                case 'topRight':
                    width = thickness;
                    height = length;
                    top = -height;
                    left = dimensions.width - width - 2;
                    stemCellMargin = '0 0 0 auto';
                    break;
                
                case 'bottomLeft':
                    width = thickness;
                    height = length;
                    top = dimensions.height;
                    left = 2;
                    start = width;
                    break;
                
                case 'bottomRight':
                    width = thickness;
                    height = length;
                    top = dimensions.height;
                    left = dimensions.width - width - 2;
                    stemCellMargin = '0 0 0 auto';
                    start = width;
                    break;
                
                case 'rightTop':
                    width = length;
                    height = thickness;
                    top = 2;
                    left = dimensions.width;
                    start = width;
                    break;
                
                case 'rightBottom':
                    width = length;
                    height = thickness;
                    top = dimensions.height - height - 2;
                    left = dimensions.width;
                    break;
                
                case 'leftTop':
                    width = length;
                    height = thickness;
                    top = 2;
                    left = -width;
                    stemCellMargin = '0 0 0 auto';
                    start = width;
                    break;
                
                case 'leftBottom':
                    width = length;
                    height = thickness;
                    top = dimensions.height - height - 2;
                    left = -width;
                    stemCellMargin = '0 0 0 auto';
                    break;
            }
            
            stem.setStyle({
                width:      width + 'px',
                height:     height + 'px',
                top:        top + 'px',
                left:       left + 'px'
            });
            
            this.tooltip.insert(stem);
            
            for (var i = 0; i < height; i++) {
                stemCellWidth = start > 0 ? 
                    Math.ceil(start - (i * width / height)):
                    Math.ceil(i * width / height);
                
                stem.insert(new Element('div').setStyle({
                    background: color,
                    width:      stemCellWidth + 'px',
                    height:     '1px',
                    margin:     stemCellMargin
                }));
            }
        },
        
        getTooltipTop: function()
        {
            var offsetTop = this.options.offsetTop ? this.options.offsetTop : 0;
            offsetTop += this.options.hook.element.cumulativeOffset().top;
            
            switch (this.options.placement.element) {
                case 'topLeft':
                case 'topRight':
                    // No change
                    break;
                    
                case 'bottomLeft':
                case 'bottomRight':
                    offsetTop += this.options.hook.element.getHeight();
                    break;
            }
            
            switch (this.options.placement.tooltip) {
                case 'topLeft':
                case 'topRight':
                    // No change
                    break;
                    
                case 'bottomLeft':
                case 'bottomRight':
                    offsetTop -= this.tooltip.getHeight();
            }
            
            return offsetTop;
        },
        
        getTooltipLeft: function()
        {
            var offsetLeft = this.options.offsetLeft ? this.options.offsetLeft : 0;
            offsetLeft += this.options.hook.element.cumulativeOffset().left;
            
            switch (this.options.placement.element) {
                case 'topLeft':
                case 'bottomLeft':
                    // No change
                    break;
                    
                case 'topRight':
                case 'bottomRight':
                    offsetLeft += this.options.hook.element.getWidth();
                    break;
            }
            
            switch (this.options.placement.tooltip) {
                case 'topLeft':
                case 'bottomLeft':
                    // No change
                    break;
                    
                case 'bottomRight':
                case 'topRight':
                    offsetLeft -= this.tooltip.getWidth();
            }
            
            return offsetLeft;
        },
        
        setPosition: function(top, left)
        {
            this.scrollOffsets = document.viewport.getScrollOffsets();
            this.viewportDimensions = document.viewport.getDimensions();
            top = top || this.getTooltipTop();
            left = left || this.getTooltipLeft();
            
            // Calcualte top
            if ((top + this.tooltipDimensions.height) > (this.viewportDimensions.height + this.scrollOffsets.top - 25)) {
                top = this.viewportDimensions.height - this.tooltipDimensions.height + this.scrollOffsets.top - 25;
            }
                
            // Calculate left
            if ((left + this.tooltipDimensions.width) > (this.viewportDimensions.width + this.scrollOffsets.left - 25)) {
                left = this.viewportDimensions.width - this.tooltipDimensions.width + this.scrollOffsets.left - 25;
            }
            
            this.tooltip.setStyle({
                top:        top + 'px',
                left:       left + 'px'
            });
        }
    });
    
    if (!window.RT) window.RT = {};
    window.RT.Tooltip = Tooltip;
})();

document.fire('rt:plugin:tooltip:loaded');