var SA = window.SA || {};

function findPosX(obj)
{
  var curleft = 0;
  if(obj.offsetParent)
      while(1)
      {
        curleft += obj.offsetLeft;
        if(!obj.offsetParent)
          break;
        obj = obj.offsetParent;
      }
  else if(obj.x)
      curleft += obj.x;
  return curleft;
}

function findPosY(obj)
{
  var curtop = 0;
  if(obj.offsetParent)
      while(1)
      {
        curtop += obj.offsetTop;
        if(!obj.offsetParent)
          break;
        obj = obj.offsetParent;
      }
  else if(obj.y)
      curtop += obj.y;
  return curtop;
}

function isArray(obj) {
   if (obj.constructor.toString().indexOf("Array") == -1)
      return false;
   else
      return true;
}

SA.Tooltip = {

    // temp variable for work around
    temp : null,

    attached_tooltips : new Array(),

    dynamic_id_base : 0,

    current_z_index : 50,

    // default delays for tooltips
    DEFAULT_SHOW_DELAY : 500,
    DEFAULT_HIDE_OVER_DELAY : null,
    DEFAULT_HIDE_OUT_DELAY : 500,


    /* Turns any element into a tooltip */
    makeTooltipFromId : function(attach, tooltip_id, options) {
        var tip = new SA.Tooltip.HoverTooltip(attach, tooltip_id, options);

        SA.Tooltip.attached_tooltips[SA.Tooltip.attached_tooltips.length] = tip;

        return tip;
    },


    /* Turns a piece of text into a tooltip */
    createTooltip : function(attach, content, class_name, options) {
        // define the ttip id
        var tooltip_id = "sa_ttip_"+SA.Tooltip.dynamic_id_base;
        SA.Tooltip.dynamic_id_base++;

        // create the div block
        var tooltip_element = document.createElement('DIV');
        tooltip_element.setAttribute("id", tooltip_id);
        tooltip_element.innerHTML = content;
        if(class_name) tooltip_element.className = class_name;
        tooltip_element.style.visibility = 'hidden';
        document.body.appendChild(tooltip_element);

        // make it a tooltip
        return SA.Tooltip.makeTooltipFromId(attach, tooltip_id, options);
    },


    // This constructor creates an object that turns a basic
    // DOM element into a hover style tool tip
    HoverTooltip : function(attach, tip_id, options) {
        // create an internal reference for functions to use
        var instance = this;

        this.tip_id = tip_id;

        // fetch the objects
        var attach_el = null;
        if(typeof (attach) == 'string') attach_el = document.getElementById(attach);
        else attach_el = attach;

        var tip_el = document.getElementById(tip_id);
        this.attach_el = attach_el;
        this.tip_el = tip_el;

        // set the defaults
        this.show_delay = SA.Tooltip.DEFAULT_SHOW_DELAY;
        this.hide_over_delay = SA.Tooltip.DEFAULT_HIDE_OVER_DELAY;
        this.hide_out_delay = SA.Tooltip.DEFAULT_HIDE_OUT_DELAY;

        if(options && isArray(options) && options['type'] == 'menu') {
            this.show_delay = 1;
            this.hide_out_delay = 1;
        }

        // instance for timer
        this.timer = null;
        this.mouse_pos = null;

        // flag to stop other states running
        this.in_process = false;

        // update the tip element so that it is ready to play
        if(this.tip_el) {
            this.tip_el.className = (this.tip_el.className) ? 'hover_tip '+ this.tip_el.className : 'hover_tip';
            this.tip_el.style.visibility = "hidden";
        }

        this.update_pos = update_pos_func;
        function update_pos_func(e) {
            instance.mouse_pos = mousePos(e);
        }

        this.hide_tip = hide_tip_func;
        function hide_tip_func() {
            // hide the tip
            if(tip_el) {
                tip_el.style.visibility = "hidden";
                tip_el.style.left = "0px";
                tip_el.style.top = "0px";

                SA.Tooltip.current_z_index--;
            }
            // hide the shim
            instance.hide_shim();
            instance.sync_shim(tip_el);
        }

        this.show_tip = show_tip_func;
        function show_tip_func() {
            if( !tip_el ) return;

            // don't update if we're already visible
            if( tip_el.style.visibility == 'visible' ) return;

            // set position and zindex
            tip_el.style.position = 'absolute';
            tip_el.style.zIndex = SA.Tooltip.current_z_index;
            SA.Tooltip.current_z_index++;

            /* 1: determine the correct x and y (in the page, not the window)
               2: (if needed:) fix the width and attempt to determine the height
               3: determine the window height and width
               4: adjust x and y so it appears correctly on the screen
             */

             var offset_x = 10;
             var offset_y = 15;
             var ie_scrollbars = 21;

             // determine the starting pos of the tip
             var tl_x = instance.mouse_pos.x + offset_x;
             var tl_y = instance.mouse_pos.y + offset_y;
             var br_x = tl_x + tip_el.offsetWidth;
             var br_y = tl_y + tip_el.offsetHeight;

             // set fixed position for menu dropdown
             if(options && isArray(options) && options['type'] == 'menu') {
                 tl_x = findPosX(attach_el);
                 tl_y = findPosY(attach_el) + 38;
             }

             // determine the pos and size of the window
             var window_tl_x = SA_body.scrollLeft;
             var window_tl_y = SA_body.scrollTop;
             var window_br_x = window_tl_x + (SA_body.clientWidth ? SA_body.clientWidth : window.innerWidth) +
                               ( window.pageXOffset ? window.pageXOffset : (SA_body.scrollLeft ? SA_body.scrollLeft : 0) );
             var window_br_y = window_tl_x + (SA_body.clientHeight ? SA_body.clientHeight : window.innerHeight) +
                               ( window.pageYOffset ? window.pageYOffset : (SA_body.scrollTop ? SA_body.scrollTop : 0) );

             // if the br corner is outside - move it back
             if(br_x > window_br_x) { tl_x = tl_x + window_br_x - br_x; }
             if(br_y > window_br_y) { tl_y = tl_y + window_br_y - br_y; }
             // if the tl corner is outside - move it back
             if(window_tl_x > tl_x) { tl_x = window_tl_x; }
             if(window_tl_y > tl_y) { tl_y = window_tl_y; }

             // move the y position back for menu
             if(options && isArray(options) && options['type'] == 'menu') {
                 tl_y = findPosY(attach_el) + 38;
             }

             /*alert("mouse: x = "+instance.mouse_pos.x+" y = "+instance.mouse_pos.y+"<br/>\n"+
                   "box tl: x = "+tl_x+" y = "+tl_y+"<br/>\n"+
                   "box br: x = "+br_x+" y = "+br_y+"<br/>\n"+
                   "window tl: x = "+window_tl_x+" y = "+window_tl_y+"<br/>\n"+
                   "window br: x = "+window_br_x+" y = "+window_br_y+"<br/>\n"+
                   "scrollHeight = "+document.body.scrollHeight+" scrollMaxY = "+window.scrollMaxY
                   );*/

            // set the top position
            tip_el.style.left = tl_x + 'px';
            tip_el.style.top  = tl_y + 'px';

            // Sync the shim if we have it
            instance.create_shim();
            instance.sync_shim(tip_el);
            instance.show_shim();

            // show it!
            tip_el.style.visibility = 'visible';

        }

        this.clear_timer = clear_timer_func;
        function clear_timer_func() {
            if(instance.timer) {
                window.clearTimeout(instance.timer);
                instance.timer = null;
            }
        }


        this.set_timer = set_timer_func;
        function set_timer_func(fn, time) {
            instance.clear_timer();
            instance.timer = window.setTimeout(fn, time);
        }

        this.hidden_state = hidden_state_func;
        function hidden_state_func(e) {
            if( !e ) { if (window.event) e = event; else return; }

            instance.hide_tip();

            instance.clear_timer();

            if(attach_el) {
                removeEvent(attach_el, "mouseout", instance.hidden_state, false);
                removeEvent(attach_el, "mouseover", instance.show_attach_state, false);

                // if we have a delay then the next state is 'might show', otherwise we'll show it immediately
                if(instance.show_delay > 0)
                    addEvent(attach_el, "mouseover", instance.might_show_state, false);
                else
                    addEvent(attach_el, "mouseover", instance.show_attach_state, false);
            }

            if(tip_el) {
                removeEvent(tip_el, "mouseout", instance.hidden_state, false);
                removeEvent(tip_el, "mouseover", instance.show_tip_state, false);
            }
        }

        this.might_show_state = might_show_state_func;
        function might_show_state_func(e) {
            if( !e ) { if (window.event) e = event; else return; }

            // set a timer to show the tip
            var old_e = copyObj(e);
            instance.set_timer(function() { instance.show_attach_state(old_e) }, instance.show_delay);

            // Setup the state change if the mouse moves out
            if(attach_el) {
                removeEvent(attach_el, "mouseover", instance.might_show_state, false);

                addEvent(attach_el, "mouseout", instance.hidden_state, false);
            }
        }


        this.show_attach_state = show_attach_state_func;
        function show_attach_state_func(e) {
            if( !e ) { if (window.event) e = event; else return; }

            instance.show_tip();

            // TODO - add timer to hide
            instance.clear_timer();

            if(attach_el) {
                removeEvent(attach_el, "mouseover", instance.show_attach_state, false);
                removeEvent(attach_el, "mouseout", instance.hidden_state, false);

                if(instance.hide_out_delay > 0)
                    addEvent(attach_el, "mouseout", instance.might_hide_state, false);
                else
                    addEvent(attach_el, "mouseout", instance.hidden_state, false);
            }

            if(tip_el) {

                // TODO: add a condition around this
                addEvent(tip_el, "mouseover", instance.show_tip_state, false);
            }
        }

        this.might_hide_state = might_hide_state_func;
        function might_hide_state_func(e) {
            if( !e ) { if (window.event) e = event; else return; }

            // set a timer
            var old_e = copyObj(e);
            instance.set_timer(function () { instance.hidden_state(old_e); }, instance.hide_out_delay);

            if(attach_el) {
                removeEvent(attach_el, "mouseout", instance.might_hide_state, false);
                removeEvent(attach_el, "mouseover", instance.show_attach_state, false);

                addEvent(attach_el, "mouseover", instance.show_attach_state, false);
            }

            if(tip_el) {
                removeEvent(tip_el, "mouseout", instance.might_hide_state, false);
                removeEvent(tip_el, "mouseover", instance.show_tip_state, false);

                addEvent(tip_el, "mouseover", instance.show_tip_state, false);
            }
        }

        this.show_tip_state = show_tip_state_func;
        function show_tip_state_func(e) {
            if( !e ) { if (window.event) e = event; else return; }

            instance.show_tip();

            // TODO - add timer to hide
            instance.clear_timer();

            if(tip_el) {
                removeEvent(tip_el, "mouseout", instance.might_hide_state, false);
                removeEvent(tip_el, "mouseover", instance.show_tip_state, false);

                if(instance.hide_out_delay > 0)
                    addEvent(tip_el, "mouseout", instance.might_hide_state, false);
                else
                    addEvent(tip_el, "mouseout", instance.hidden_state, false);
            }

            if(attach_el) {
                removeEvent(attach_el, "mouseover", instance.show_attach_state, false);

                addEvent(attach_el, "mouseover", instance.show_attach_state, false);
            }
        }

        this.create_shim = create_shim_func;
        function create_shim_func() {
            if(instance.shim) return;

            // Hacks for IE
            if( document.getElementById && navigator.appVersion.indexOf('Win') != -1 && document.all) {
                // create a shim that follows the suggestbox
                var shim = document.createElement('iframe');
                shim.style.visibility = 'hidden';
                shim.src = "";
                //shim.scrolling = "no";
                shim.style.border = "none";
                shim.style.position = 'absolute';
                shim.style.left = "0px";
                shim.style.top = "0px";
                shim.style.zIndex = 1;
                shim.style.height = "1px";
                shim.style.width = "1px";

                // stick it in the tip element, a bit of a cheat
                document.body.appendChild(shim);

                instance.shim = shim; // store the shim
            }
        }

        this.sync_shim = sync_shim_func;
        function sync_shim_func(el) {
            // Sync the shim if we have it
            if(instance.shim) {
                instance.shim.style.position = 'absolute';
                instance.shim.style.left = el.style.left;
                instance.shim.style.top = el.style.top;
                instance.shim.style.zIndex = el.style.zIndex - 1;
                instance.shim.style.height = el.offsetHeight + 'px';
                instance.shim.style.width = el.offsetWidth + 'px';
            }
        }

        this.show_shim = show_shim_func
        function show_shim_func() {
            if(instance.shim) {
                instance.shim.style.visibility = 'visible';
            }
        }

        this.hide_shim = hide_shim_func
        function hide_shim_func() {
            if(instance.shim) {
                instance.shim.style.visibility = 'hidden';
            }
        }


        instance.hidden_state(true);
        addEvent(attach_el, "mousemove", instance.update_pos);
    }
};
