/*******************************************************************
RubberBand object

works as follows.  there must be a <div id="rubberBand"> element on the page.
Feel free to style this element as you see fit with CSS or what have you.  this
class just manipulates its position and provides an interface for mapping
events to the class's event handlers.

so, some sort of onclick type action should call startRubber.  when that
happens, a number of things are done to set this business up.  for one, the
<div>'s display style is set to block, when presumably before it was "none".
then, we grab the mouse's position and set up the selector's top: and left:
properties to that position.  booyah!  after that, we map document.onmousemove
to moveRubber, which handles it from there.

*******************************************************************/ 

function RubberBand() {
  this.isPoint=false;
  this.isRectangle=false;
  this.start_x = this.start_y = 0;
  this.mouse_x = this.mouse_y = 0;
  this.x1=this.y1=this.x2=this.y2;
  this.offsetX=this.offsetY=0;
  
  this.init=function() {
    this.div=document.getElementById('rubberBand');
  }
  
  this.pointRubber=function(evt) {
    // set up our constant start_x and start_y
    var evt = evt || window.event;
    rubber.start_x=evt.clientX;
    rubber.start_y=evt.clientY;
    rubber.mouse_x=evt.clientX;
    rubber.mouse_y=evt.clientY;
    
    if (document.layers) {
      document.releaseEvents(Event.MOUSEMOVE);
    }
    
    document.onmousemove = null;
    document.onmouseup = null;
    document.onmousedown = null;
    
    rubber.calculateCoordinates();
    if (panels.currentToolObj.selectionHandler && panels.currentToolObj.selectionHandler.length>0) {
      eval(panels.currentToolObj.selectionHandler+"();");
    }
  }
  
  /*
    this function is used as an event handler. when assigned as an event
    handler, any "this" reference in the function does not refer to the
    RubberBand object, but rather to the calling object. To reference the
    RubberBand object, it's variable name must be used instead of "this".
  */
  this.startRubber=function(evt) {
    // set up our constant start_x and start_y
    var evt = evt || window.event;
    rubber.start_x=evt.clientX;
    rubber.start_y=evt.clientY;
    
    // set up the div and make it visible
    rubber.div.style.left = rubber.start_x;
    rubber.div.style.top = rubber.start_y;
    rubber.div.style.width = 0;
    rubber.div.style.height = 0;
    if (panels.currentToolObj.showRubberBand)
      rubber.div.style.display = 'block';
    else
      rubber.div.style.display = 'none';
    
    // map our event handlers
    document.onmousemove=rubber.moveRubber;
    document.onmousedown=rubber.moveRubber;
    document.onmouseup=rubber.stopRubber;
  }
  
  /*
    this function is used as an event handler. when assigned as an event
    handler, any "this" reference in the function does not refer to the
    RubberBand object, but rather to the calling object. To reference the
    RubberBand object, it's variable name must be used instead of "this".
  */
  this.getMouse=function getMouse(evt) {
    var evt = evt || window.event;
    rubber.mouse_x=evt.clientX;
    rubber.mouse_y=evt.clientY;
  }
  
  /*
    this function is used as an event handler. when assigned as an event
    handler, any "this" reference in the function does not refer to the
    RubberBand object, but rather to the calling object. To reference the
    RubberBand object, it's variable name must be used instead of "this".
  */
  this.moveRubber=function(evt) {
    // current mouse position
    rubber.getMouse(evt);
    
    // check that we're within map box bounds and if not, update mouse position
    if(rubber.mouse_x <= map.mapX1) {
      rubber.mouse_x = map.mapX1 + 2;
    }
    
    if(rubber.mouse_y <= map.mapY1) {
      rubber.mouse_y = map.mapY1 + 2;
    }
    
    if(rubber.mouse_x >= map.mapX2) {
      rubber.mouse_x = map.mapX2 - 2;
    }
    
    if(rubber.mouse_y >= map.mapY2) {
      rubber.mouse_y = map.mapY2 - 2;
    }
    
    // diff_x, diff_y: difference between beginning click position and current mouse position
    var diff_x = rubber.mouse_x - rubber.start_x;
    var diff_y = rubber.mouse_y - rubber.start_y;
    var div = rubber.div;
    
    // single-click, no drag.
    if(diff_x == 0 && diff_y == 0) {
      div.style.cursor = "crosshair";
      div.style.top = rubber.start_y + 'px';
      div.style.left = rubber.start_x + 'px';
      div.style.width = 0 + 'px';
      div.style.height = 0 + 'px';
    } else if (diff_x >= 0 && diff_y >= 0) {
      div.style.left = rubber.start_x + 'px';
      div.style.top = rubber.start_y + 'px';
      div.style.width = diff_x + 'px';
      div.style.height = diff_y + 'px';
    } else if (diff_x <= 0 && diff_y >= 0) {
      div.style.left = rubber.start_x + diff_x + 'px';
      div.style.top = rubber.start_y + 'px';
      div.style.width = Math.abs(diff_x) + 'px';
      div.style.height = diff_y + 'px';
    } else if (diff_x >= 0 && diff_y <= 0) {
      div.style.left = rubber.start_x + 'px';
      div.style.top = rubber.start_y + diff_y + 'px';
      div.style.width = diff_x + 'px';
      div.style.height = Math.abs (diff_y) + 'px';
    } else if (diff_x <= 0 && diff_y <= 0) {
      div.style.left = rubber.start_x + diff_x + 'px';
      div.style.top = rubber.start_y + diff_y + 'px';
      div.style.width = Math.abs(diff_x) + 'px';
      div.style.height = Math.abs(diff_y) + 'px';
    } 

		map.mapdiv.style.cursor = rubber.div.style.cursor;

		if (panels.currentToolObj.mapBackgroundMoves) {
			map.mapdiv.style.backgroundPosition = (diff_x) + "px "+ (diff_y) +"px";
			map.mapdiv.style.backgroundRepeat = "no-repeat";
			map.mapdiv.style.cursor = "move";
		}
	}

  this.stopRubber=function(evt) {
    var r = rubber.div;
    /*
    map.mapdiv.style.cursor = "crosshair";
    r.style.cursor = "crosshair";
    */
    r.style.height = "0px";
    r.style.width = "0px";
    r.style.top = "0px";
    r.style.left = "0px";
    r.style.display = "none";
    
    if (document.layers) {
      document.releaseEvents(Event.MOUSEMOVE);
    }
    
    document.onmousemove = null;
    document.onmouseup = null;
    document.onmousedown = null;
    
    rubber.calculateCoordinates();
    if (panels.currentToolObj.selectionHandler && panels.currentToolObj.selectionHandler.length>0) {
      eval(panels.currentToolObj.selectionHandler+"();");
    }
  }
  
  this.calculateCoordinates=function() {
    /*************************************************************************
     * mapserver expects bounding box extents to be in the form of 2 points
     * whose coorinates describe the lower left point and upper right point as
     * minx, miny and maxx, maxy respectively.
     *  (x1,y1)
     *     ------- UR (values for maxx, maxy)
     *     |     |
     *     |     |
     *     |     |
     *     ------- (x2,y2)
     *     LL (values for minx, miny)
     * 
     * these coordinates are expressed as pixel coordinates within the map 
     * image, with (0,0) being the upper left corner. 
     * (the maxy in the rect object should be < miny value)
     ************************************************************************/
    this.isPoint=this.isRectangle=false;
    if (this.start_x>map.mapX1 && this.start_x<map.mapX2 &&
        this.start_y>map.mapY1 && this.start_y<map.mapY2 &&
        this.mouse_x>map.mapX1 && this.mouse_x<map.mapX2 &&
        this.mouse_y>map.mapY1 && this.mouse_y<map.mapY2) {
      // bounding coordinates are in range (inside the map image)
      // set x1 to the left most x value
      // and x2 to the right most x value
      this.x1=this.start_x-map.mapX1;
      this.x2=this.mouse_x-map.mapX1;
      this.y1=this.start_y-map.mapY1;
      this.y2=this.mouse_y-map.mapY1;
      /*
      if (this.start_x<=this.mouse_x) {
        this.x1=this.start_x-map.mapX1;
        this.x2=this.mouse_x-map.mapX1;
      } else {
        this.x1=this.mouse_x-map.mapX1;
        this.x2=this.start_x-map.mapX1;
      }
      // set y1 to the top most y value
      // and y2 to the bottom most y value
      if (this.start_y<=this.mouse_y) {
        //y2=map.mapY2-this.start_y;
        //y1=map.mapY2-this.mouse_y;
        this.y2=this.mouse_y-map.mapY1;
        this.y1=this.start_y-map.mapY1;
      } else {
        //y2=map.mapY2-this.mouse_y;
        //y1=map.mapY2-this.start_y;
        this.y2=this.start_y-map.mapY1;
        this.y1=this.mouse_y-map.mapY1;
      }
      this.offset_x=this.mouse_x-this.start_x;
      this.offset_y=this.mouse_y-this.start_y;
      */
      // determine if the selection is a point or a rectangle
      var point=(Math.abs(this.x2-this.x1)<4 && Math.abs(this.y2-this.y1)<4);
      this.isPoint=point;
      this.isRectangle=!point;
    }
  }
  
  // returns the coordinates of the last bounding action that the 
  // rubberband performed as a querystring of the form zoom['x1']=x1, ...
  // it is the reponsibility of the caller to specify a zoom factor if one
  // is required.
  this.queryString=function(queryType) {
    var qs=new Array();
    
    qs.push(queryType+"[x1]="+this.x1);
    qs.push(queryType+"[y1]="+this.y1);
    if (this.isRectangle) {
      qs.push(queryType+"[x2]="+this.x2);
      qs.push(queryType+"[y2]="+this.y2);
    }
    // additional arguments may be zoom factor, offset, zoom to scale, etc.
    for (var i=1;i<arguments.length;++i) {
      qs.push(arguments[i]);
    }
    
    return qs.join("&");
  }
}
