/**
 * $Id: anchoredinterface.js 64367 2009-02-20 01:56:36Z kchiu $
 * $Author: kchiu $
 * $Revision: 64367 $
 * $Name$
 * $Date: 2009-02-19 17:56:36 -0800 (Thu, 19 Feb 2009) $
 *
 * @jsRequire DomUtils
 * @jsRequire interfaces.Interface
 *
 *
 * @version    $Revision: 64367 $
 * @author     Philip Snyder <philip@pricegrabber.com>
 * @copyright  Copyright &copy; 2006, Philip Snyder, PriceGrabber.com
 * @see        interfaces.Interface
 */

/**
 * AnchoredInterface Constructor / Definition
 *
 * This interface is built on top of the Interface object
 * and is NOT intended to be instantiated directly. See
 * documentation on interfaces.Interface for a complete
 * explanation.
 *
 * @access public
 * @since  v1.1
 * @return AnchoredInterface
 */
function AnchoredInterface() {
    this.elemId            = null;
    this.anchorId          = null;
    this.disableScroll     = false;
    this.getAnchorX        = AnchoredInterface_GetAnchorX;
    this.getAnchorY        = AnchoredInterface_GetAnchorY;
    this.getAnchorZ        = AnchoredInterface_GetAnchorZ;
    this.getAnchorWidth    = AnchoredInterface_GetAnchorWidth;
    this.getAnchorHeight   = AnchoredInterface_GetAnchorHeight;
    this.getElemWidth      = AnchoredInterface_GetElemWidth;
    this.getElemHeight     = AnchoredInterface_GetElemHeight;
    this.getAnchorPosition = AnchoredInterface_GetAnchorPosition;
    this.setAnchor         = AnchoredInterface_SetAnchor;
    this.alignElement      = AnchoredInterface_AlignElement;
}

// Setup AnchoredInterface prototype chain
AnchoredInterface.prototype             = new Interface;
AnchoredInterface.prototype.constructor = AnchoredInterface;
AnchoredInterface.superclass            = Interface.prototype;

/**
 * Constants defining anchor points around the element.
 *
 * !DO NOT MODIFY THIS!
 *
 * @access public
 * @since  v1.1
 * @var    AnchoredInterface.ALIGN   struct
 */
AnchoredInterface.ALIGN = { RIGHT_TOP: 1, RIGHT_BOTTOM: 2, LEFT_BOTTOM: 3, LEFT_TOP: 4 };

/**
 * Returns the anchor element's X coordinate.
 *
 * @access public
 * @since  v1.1
 * @return integer
 */
function AnchoredInterface_GetAnchorX() {
    return DomUtils.getElementLeft(document.getElementById(this.anchorId));
}

/**
 * Returns the anchor element's Y coordinate.
 *
 * @access public
 * @since  v1.1
 * @return integer
 */
function AnchoredInterface_GetAnchorY() {
    return DomUtils.getElementTop(document.getElementById(this.anchorId));
}

/**
 * Returns the anchor element's Z index.
 *
 * @access public
 * @since  v1.1
 * @return integer
 */
function AnchoredInterface_GetAnchorZ() {
    return DomUtils.getZIndex(document.getElementById(this.anchorId));
}

/**
 * Returns the anchor element's width.
 *
 * @access public
 * @since  v1.1
 * @return integer
 */
function AnchoredInterface_GetAnchorWidth() {
    //window.messageQueue.add( new Message(funcname(this), 'getting anchor width') );
    return DomUtils.getElementWidth(document.getElementById(this.anchorId));
}

/**
 * Returns the anchor element's height.
 *
 * @access public
 * @since  v1.1
 * @return integer
 */
function AnchoredInterface_GetAnchorHeight() {
    return DomUtils.getElementHeight(document.getElementById(this.anchorId));
}

/**
 * Returns the element's width.
 *
 * @access public
 * @since  v1.1
 * @return integer
 */
function AnchoredInterface_GetElemWidth() {
    //window.messageQueue.add( new Message(funcname(this), 'getting element width') );
    return DomUtils.getElementWidth(document.getElementById(this.elemId));
}

/**
 * Returns the element's height.
 *
 * @access public
 * @since  v1.1
 * @return integer
 */
function AnchoredInterface_GetElemHeight() {
    return DomUtils.getElementHeight(document.getElementById(this.elemId));
}

/**
 * Calculates the best alignment position for the element based on window size and position of the anchor element.
 *
 * Returns a value from the AnchoredInterface.ALIGN struct.
 *
 * @access public
 * @since  v1.1
 * @return integer
 */
function AnchoredInterface_GetAnchorPosition() {
    //window.messageQueue.add( new Message('AnchoredInterface_GetAnchorPosition', 'called') );
    var pos    = false;
    var elem   = document.getElementById(this.elemId);
    if (!elem)   throw new Error('Unable to find element: '+this.elemId);
    var anchor = document.getElementById(this.anchorId);
    if (!anchor) throw new Error('Unable to find anchor element: '+this.anchorId);

    //window.messageQueue.add( new Message(funcname(this), 'starting getElementWidth') );
    var wWidth  = parseInt(DomUtils.getWindowWidth(window));
    var wHeight = parseInt(DomUtils.getWindowHeight(window));
    var scrollX = parseInt(DomUtils.getWindowScrollX(window));
    var scrollY = parseInt(DomUtils.getWindowScrollY(window));
    var aWidth  = parseInt(DomUtils.getElementWidth(anchor));
    var aHeight = parseInt(DomUtils.getElementHeight(anchor));
    var aX      = parseInt(DomUtils.getElementLeft(anchor));
    var aY      = parseInt(DomUtils.getElementTop(anchor));
    var aWidth  = parseInt(DomUtils.getElementWidth(anchor));
    var aHeight = parseInt(DomUtils.getElementHeight(anchor));
    var eWidth  = parseInt(DomUtils.getElementWidth(elem));
    var eHeight = parseInt(DomUtils.getElementHeight(elem));
    //window.messageQueue.add( new Message(funcname(this), 'passed getElementWidth') );


    if (this.disableScroll) {
    
        /**
         * DO NOT TOUCH. I'M SERIOUS. This is difficult enough to figure out the first time. ;)
         */
        var enoughRoomOnTop    = (aY - scrollY > eHeight ? true : false);
        var enoughRoomOnBottom = (wHeight + scrollY - aHeight - aY > eHeight ? true : false);
        var enoughRoomOnLeft   = (aX - scrollX + aWidth > eWidth ? true : false);
        var enoughRoomOnRight  = (wWidth - aX + scrollX > eWidth ? true : false);
    
        /**
         * Preferred position order:
         *
         *   left top
         *   left bottom
         *   right bottom
         *   right top
         *
         * Please note that this is the position of the anchor in
         * relation to the main element, NOT the other way around
         */
//alert('enoughRoomOnTop= '+enoughRoomOnTop+'\nenoughRoomOnBottom= '+enoughRoomOnBottom+'\nenoughRoomOnLeft= '+enoughRoomOnLeft+'\nenoughRoomOnRight= '+enoughRoomOnRight);
        // Left Top
        if (enoughRoomOnBottom && enoughRoomOnRight) {
            pos = AnchoredInterface.ALIGN.LEFT_TOP;
        // Left Bottom
        } else if (enoughRoomOnTop && enoughRoomOnRight) {
            pos = AnchoredInterface.ALIGN.LEFT_BOTTOM;
        // Right Bottom
        } else if (enoughRoomOnTop && enoughRoomOnLeft) {
            pos = AnchoredInterface.ALIGN.RIGHT_BOTTOM;
        // Right Top
        } else if (enoughRoomOnBottom && enoughRoomOnLeft) {
            pos = AnchoredInterface.ALIGN.RIGHT_TOP;
        } else if (!enoughRoomOnRight) {
            pos = AnchoredInterface.ALIGN.LEFT_TOP;
        } else {
            pos = AnchoredInterface.ALIGN.LEFT_TOP;
        }
        return pos;
    
    } else {
    
        //how much is missing to display on the right (<=0 means enough to display)
        var not_enough_right = ((aX-scrollX) +  eWidth) - wWidth;
        //how much is missing to display on the left (<=0 means enough to display)
        var not_enough_left =  eWidth - (aX-scrollX);
        //how much is missing to display on the left if not scrolled (<=0 means enough to display)
        var not_enough_absolute_left = eWidth - aX;
    
        //how much is missing to display on the bottom (<=0 means enough to display)
        var not_enough_bottom = ((aY-scrollY) +  eHeight) - wHeight;
        //how much is missing to display on the top (<=0 means enough to display)
        var not_enough_top =  eHeight - (aY-scrollY);
        //how much is missing to display on the top if not scrolled (<=0 means enough to display)
        var not_enough_absolute_top = eHeight - aY;
    
        //alert("window is "+wWidth+" by "+wHeight+" and scrolled to "+scrollX+" by "+scrollY+"\nthumb is "+aWidth+" by "+aHeight+" and at "+aX+" by "+aY+"\npop is "+eWidth+" by "+eHeight+"\n\nif there is enough room, value <=0:\n\nright: "+not_enough_right+"\nleft: "+not_enough_left+"\nabsleft: "+not_enough_absolute_left+"\nbottom: "+not_enough_bottom+"\ntop: "+not_enough_top+"\nabstop: "+not_enough_absolute_top);
    
        var horiz = '';
        var vertic = '';
        var ss = 10;
    
        // #1 pref position: top right
        // #2 pref position: bottom right
        // #3 pref position: top left
        // #4 pref position: bottom left
        // if we fit or are closer from fitting to the right than to the left or there is not enough room on the left
        if (not_enough_right <= 0 || not_enough_right < not_enough_left || not_enough_absolute_left > 0) {
            horiz = 'right';
            // scroll if necessary
            if (not_enough_right > 0) {
                for (var i=0; i<=(not_enough_right/(ss*2))+1; i++) {
                    setTimeout('window.scroll(parseInt(DomUtils.getWindowScrollX(window))+'+ss+',parseInt(DomUtils.getWindowScrollY(window)))',100);
                }
            }
        // else if we are closer from the left and there is room if we scroll
        } else {
            horiz = 'left';
            // scroll if necessary
            if (not_enough_left > 0 && !this.disableScroll) {
                for (var i=0; i<=(not_enough_left/(ss*2))+1; i++) {
                    setTimeout('window.scroll(parseInt(DomUtils.getWindowScrollX(window))-'+ss+',parseInt(DomUtils.getWindowScrollY(window)))',100);
                }
            }
        }
    
        // if we are closer from fitting to the top than to the bottom and there is enough room at the top
        if (not_enough_top <= 0 || (not_enough_top < not_enough_bottom && not_enough_absolute_top <= 0)) {
            vertic = 'top';
            // scroll if necessary
            if (not_enough_top > 0 && !this.disableScroll) {
                for (var i=0;i<=(not_enough_top/(ss*2))+1;i++) {
                    setTimeout('window.scroll(parseInt(DomUtils.getWindowScrollX(window)),parseInt(DomUtils.getWindowScrollY(window))-'+ss+')',100);
                }
            }
        // else if we are closer from the top and there is room if we scroll
        } else {
            vertic = 'bottom';
            // scroll if necessary
            if (not_enough_bottom > 0 && !this.disableScroll) {
                for (var i=0; i<=(not_enough_bottom/(ss*2))+1; i++) {
                    setTimeout('window.scroll(parseInt(DomUtils.getWindowScrollX(window)),parseInt(DomUtils.getWindowScrollY(window))+'+ss+')',100);
                }
            }
        }
    
        if      (horiz == 'right' && vertic == 'top'   ) pos = AnchoredInterface.ALIGN.RIGHT_TOP;
        else if (horiz == 'right' && vertic == 'bottom') pos = AnchoredInterface.ALIGN.RIGHT_BOTTOM;
        else if (horiz == 'left'  && vertic == 'top'   ) pos = AnchoredInterface.ALIGN.LEFT_TOP;
        else if (horiz == 'left'  && vertic == 'bottom') pos = AnchoredInterface.ALIGN.LEFT_BOTTOM;
    
        return pos;
    }
}

/**
 * Sets the anchor element.
 *
 * @access public
 * @since  v1.1
 * @param  DOMElement  anchor
 * @return void
 */
function AnchoredInterface_SetAnchor(anchor) {
    //alert('anchor id: '+anchor.id);
    this.anchorId = anchor.id;
}

/**
 * Aligns the element based on the best placement determined by AnchoredInterface_GetAnchorPosition.
 *
 * @access public
 * @since  v1.1
 * @param  integer   pos   A value from the AnchoredInterface.ALIGN struct
 * @return void
 */
function AnchoredInterface_AlignElement(pos) {
    //window.messageQueue.add( new Message('AnchoredInterface_AlignElement', 'called') );
    var elem   = document.getElementById(this.elemId);
    if (!elem)   throw new Error('Unable to find element: '+this.elemId);

    var anchor = document.getElementById(this.anchorId);
    if (!anchor) throw new Error('Unable to find anchor element: '+this.anchorId);

    pos = pos || this.getAnchorPosition();

    //window.messageQueue.add( new Message('AnchoredInterface_AlignElement', 'starting getElementWidth') );
    var aLeft   = DomUtils.getElementLeft(anchor);
    var aTop    = DomUtils.getElementTop(anchor);
    var aHeight = DomUtils.getElementHeight(anchor);
    var aWidth  = DomUtils.getElementWidth(anchor);
    var eHeight = DomUtils.getElementHeight(elem);
    var eWidth  = DomUtils.getElementWidth(elem);
    /*alert('aLeft: '+aLeft+"\n"+
          'aTop: '+aTop+"\n"+
          'aHeight: '+aHeight+"\n"+
          'aWidth: '+aWidth+"\n"+
          'eHeight: '+eHeight+"\n"+
          'eWidth: '+eWidth+"\n");*/
    //window.messageQueue.add( new Message('AnchoredInterface_AlignElement', 'passed getElementWidth') );
    switch (pos) {
        /**
         * Right Top alignment means:
         *
         *             +--------+
         *             | anchor |
         *             +--------+
         *    +-----------------+
         *    | elem            |
         *    +-----------------+
         */
        case AnchoredInterface.ALIGN.RIGHT_TOP:
            elem.style.left = parseInt( aLeft - eWidth  + aWidth  )+'px';
            elem.style.top  = parseInt( aTop  + aHeight )+'px';
            break;
        /**
         * Right Bottom alignment means:
         *
         *    +-----------------+
         *    | elem            |
         *    +-----------------+
         *             +--------+
         *             | anchor |
         *             +--------+
         */
         case AnchoredInterface.ALIGN.RIGHT_BOTTOM:
            elem.style.left = parseInt( aLeft - eWidth + aWidth )+'px';
            elem.style.top  = parseInt( aTop  + aHeight )+'px';
            break;
        /**
         * Left Top alignment means:
         *
         *    +--------+
         *    | anchor |
         *    +--------+
         *    +-----------------+
         *    | elem            |
         *    +-----------------+
         */
         case AnchoredInterface.ALIGN.LEFT_TOP:
            elem.style.left = parseInt( aLeft )+'px';
            elem.style.top  = parseInt( aTop + aHeight)+'px';
            break;
        /**
         * Left Bottom alignment means:
         *
         *    +-----------------+
         *    | elem            |
         *    +-----------------+
         *    +--------+
         *    | anchor |
         *    +--------+
         */
         case AnchoredInterface.ALIGN.LEFT_BOTTOM:
        default:
            elem.style.left = parseInt( aLeft )+'px';
            elem.style.top  = parseInt( aTop - eHeight )+'px';
            break;
    }
    // fanatic memory cleanup
    elem   = null;
    anchor = null;
    //window.messageQueue.add( new Message('AnchoredInterface_AlignElement', 'finished') );
}

