define(function(require, exports, module) {
    main.consumes = ["Plugin", "ui"];
    main.provides = ["tooltip"];
    return main;

    function main(options, imports, register) {
        var Plugin = imports.Plugin;
        var ui = imports.ui;
        
        /***** Initialization *****/
        
        var plugin = new Plugin("Ajax.org", main.consumes);
        
        /***** Methods *****/
        
        function create(options, oHtml) {
            var div;
    
            if (!options.tooltip) {
                div = document.body.appendChild(document.createElement("div"));
                div.className = "menu-bk downward menu-bkFocus c9-tooltip";
                if (options.width)
                    div.style.width = options.width;
                div.style.position = "absolute";
                div.innerHTML = "<div></div>";
    
                var arrow = div.appendChild(document.createElement("div"));
                arrow.className = "arrow revisionsInfoArrow";
    
                options.tooltip = div;
            }
            else {
                div = options.tooltip;
            }
    
            div.addEventListener("mouseover", ttmouseover);
            div.addEventListener("mouseout", ttmouseout);
    
            div.companion = oHtml;
        }
    
        function add(oHtml, options, plugin) {
            if (oHtml.nodeFunc)
                oHtml = oHtml.$ext;
    
            oHtml.addEventListener("mouseover", mouseover);
            oHtml.addEventListener("mouseout", mouseout);
            oHtml.addEventListener("mousedown", mousedown);
    
            if (options.timeout === undefined)
                options.timeout = 500;
    
            oHtml.c9tooltipOptions = options;
            
            plugin.addOther(function(){
                destroy(oHtml);
            });
            
            return oHtml;
        }
    
        function destroy(oHtml) {
            var options = oHtml.c9tooltipOptions;
            var tooltip = options.tooltip;
    
            if (!tooltip)
                return;
            
            if (tooltip.parentNode)
                tooltip.parentNode.removeChild(tooltip);
            if (tooltip.htmlNodes) {
                tooltip.htmlNodes.forEach(function(oHtml) {
                    oHtml.removeEventListener("mouseover", mouseover);
                    oHtml.removeEventListener("mouseout", mouseout);
                    oHtml.removeEventListener("mousedown", mousedown);
                }, this);
            }
        }
    
        function ttmouseover(){
            var oHtml = this.companion;
            var options = oHtml.c9tooltipOptions;
    
            clearTimeout(options.timer);
            if (options.control)
                options.control.stop();
    
            apf.setOpacity(this, 1);
        }
    
        function ttmouseout(e) {
            //if (apf.isChildOf(this, e.target, true))
            //  return;
    
            mouseout.call(this.companion);
        }
    
        function mouseover(e) {
            var options = this.c9tooltipOptions;
    
            clearTimeout(options.timer);
            if (options.tooltip)
                clearTimeout(options.tooltip.timer);
    
            if (options.isAvailable && options.isAvailable() === false)
                return;
    
            var _self = this;
            options.timer = setTimeout(function(){
                if (options.control)
                    options.control.stop();
    
                create(options, _self);
    
                options.tooltip.style.display = "block";
    
                var pos;
                if (options.getPosition)
                    pos = options.getPosition();
                else {
                    var p = ui.getAbsolutePosition(_self);
                    pos = [(p[0] - ((options.tooltip.offsetWidth - _self.offsetWidth)/2)),
                           (p[1])];
                }
                options.tooltip.style.left = pos[0] + "px";
                options.tooltip.style.top = pos[1] + "px";
    
                if (options.message)
                    (options.tooltip.firstElementChild || options.tooltip).innerHTML = options.message;
    
                if (options.animate !== false) {
                    apf.tween.single(options.tooltip,
                        {type: "fade", from: 0, to : 1, steps: 10, interval: 0,
                         control: options.control = {}});
                }
                else {
                    apf.setOpacity(options.tooltip, 1);
                }
            }, options.timeout);
        }
    
        function mouseout(e) {
            var options = this.c9tooltipOptions;
    
            clearTimeout(options.timer);
    
            if (!options.tooltip || options.tooltip.style.display != "block")
                return;
    
            options.timer = options.tooltip.timer = setTimeout(function(){
                if (options.control)
                    options.control.stop();
    
    //            if (options.animate !== false) {
                    apf.tween.single(options.tooltip, {
                        type: "fade", 
                        from: 1, 
                        to: 0, 
                        steps: 10, 
                        interval: 0,
                        control: options.control = {},
                        onfinish: function(){ 
                            options.tooltip.style.display = "none";
                        }
                    });
    //            }
    //            else {
    //                options.tooltip.style.display = "none";
    //            }
            }, 200);
        }
    
        function mousedown(e) {
            var options = this.c9tooltipOptions;
    
            clearTimeout(options.timer);
            if (options.tooltip && options.hideonclick) {
                if (options.control)
                    options.control.stop();
    
                options.tooltip.style.display = "none";
            }
        }
        
        /***** Register and define API *****/
        
        /**
         * Adds HTML/CSS stylable tooltips to HTMLElements. 
         * 
         * You can find these tooltips throughout Cloud9. For instance the 
         * {@link findreplace} and {@link findinfiles} plugins use it to display
         * help messages when hovering over the options buttons. The 
         * {@link autosave} plugin uses a tooltip to 
         * explain to the user why the save button no longer works.
         * 
         * This example shows how to add a tooltip to a div that shows
         * after 1 second:
         * 
         *     tooltip.add(someDiv, {
         *         width       : "100px",
         *         timeout     : 1000,
         *         hideonclick : true,
         *         message     : "Use <span style='color:red'>HTML</span> in "
         *             + "this message."
         *     });
         * 
         * This example shows how to add your own HTMLElement as a tooltip to
         * another div that is shown immediately when a user hovers over it. The
         * placement of the tooltip is calculated in the getPosition method.
         * 
         *     tooltip.add(someDiv, {
         *         message : "Some Message",
         *         width   : "auto",
         *         timeout : 0,
         *         tooltip : myTooltipDiv,
         *         animate : false,
         *         getPosition : function(){
         *             var left = 100;
         *             var top = 100;
         *             return [left, top];
         *         }
         *     });
         * 
         * @singleton
         */
        plugin.freezePublicAPI({
            /**
             * Adds a tooltip to an HTMLElement.
             * @param {HTMLElement}  htmlElement              The HTMLElement that a user can hover over to see the tooltip.
             * @param {Object}       options
             * @param {Function}     [options.isAvailable]    Called to determine whether the tooltip should be displayed.
             * @param {Number}       [options.width="200px"]  Specify a CSS string that sets the width. Set to "auto" to automatically size the tooltip.
             * @param {Number}       [options.timeout]        The time in milliseconds the user should hover the `htmlElement` until the tooltip displays.
             * @param {Boolean}      [options.hideonclick]    Specifies whether the tooltip should dissapear when the user clicks on it.
             * @param {String}       [options.message]        The message displayed in the tooltip. This can be any valid HTML string.
             * @param {HTMLElement}  [options.tooltip]        An HTMLElement that is used to display the message. If this option is not specified, an HTMLElement is created automatically.
             * @param {Boolean}      [options.animate=true]   Set to false to turn off the fading animation of the tooltip.
             * @param {Function}     [options.getPosition]    Override the default positioning of the tooltip by returning an array with the left and top position as integers in an Array (e.g [100, 200]).
             * @param {Plugin}       plugin                   The plugin responsible for creating this tooltip. This is necessary for cleanup during the unload phase of the plugin.
             * @return {HTMLElement}
             */
            add: add,
            
            /**
             * Remove a previously created tooltip
             * @param {HTMLElement} htmlElement  The `htmlElement` returned by {@link tooltip#add}.
             */
            destroy: destroy
        });
        
        register(null, {
            tooltip: plugin
        });
    }
});