/* vim: set sw=2 ts=8 sts=2 sta et : */
/* UMN MapServer JavaScript Client functions - Dynamic HTML
 *   
 *   Authors:   Frank Koormann <frank.koormann@intevation.de>
 *              Silke Reimer <silke.reimer@intevation.de>
 *   Copyright: 2004, Intevation GmbH, Germany
 *		This program is free software under the GPL (>=v2)
 *		See the GNU General Public License at 
 *		http://www.gnu.org/copyleft/gpl.html 
 *		for more details.
 *
 *   Description: 
 *              The dynamic user feedback is realized by a collection of layers
 *              ontop of the map:
 *              - The box is a layer with (red) border which is positioned
 *                an resized according to mouse clicks and drags.
 *              - The (optional) crosshair is a combination of two layers
 *                covering x/y-oriented lines whic are repositioned according
 *                to mouse movements.
 *              - line printing (for digitization or measurements) is
 *                implemented by two layers (IE needs this) with jsgraphics.
 *                
 *              The overall functionality is controlled by MouseMoveHandler(),
 *              bound to the document.onmousemove: It checks if the mouse is
 *              over the map and depending on this and the currently active
 *              tool performs necessary actions.
 *              
 *              Available tools are defined in dynamap-buttons.js
*/
      
// Some globals
var activeTool = "";
var actionType = "";
var measure    = false;
var drawLyr    = "drawLayer";
var mapserv    = null;		// mapserver form element
var gpath      = "";		// graphics path

var crosshair  = false;
var mousePressed = false;

var zr = new Array(new Array(-1,-1), new Array(-1,-1));
var pl = new Array();

var cnt = 0;

// The map image properties object
// (.Width, .Height, .Left, .Top, .MinX, .MinY, .Cellsize)
var myMap       = null;
    		
// Some global booleans for Browser detection, faster than string comparisons
var ns4 = false;
var ns6 = false;

var ie4 = false;
var ie5 = false;

// Custom Error Handler
function customHandler(desc,page,line,chr)  {
 alert(
  'JavaScript error occurred! \n'
 +'The error was handled by '
 +'a customized error handler.\n'
 +'\nError description: \t'+desc
 +'\nPage address:      \t'+page
 +'\nLine number:       \t'+line
 )
 return true
}

window.onerror=customHandler;

// Detect Browser version (Netscape 4/6, IE 4/5)
function DetectBrowser() {
  var docRoot = '';
  ns4 = (document.layers)? true:false;
  ns6 = (document.getElementById)? true:false;
  ie4 = (document.all)? true:false;
  if (ie4) docRoot = 'document.body';

  if (ie4) {
    if ((navigator.userAgent.indexOf('MSIE 5') > 0) 
        || (navigator.userAgent.indexOf('MSIE 6') > 0)) {
      if (document.compatMode 
          && document.compatMode == 'CSS1Compat') {
        docRoot = 'document.documentElement';
      }
      ie5 = true;
      drawLyr = "topLayer";
    }
    if (ns6) {
      ns6 = false;
    }
  }

  if (ns4 || ns6) {
    document.getElementById("topLayer").style.visibility = "hidden";
  }
} // DetectBrowser


// Initialize all stuff
function initMap( active, imgpath ) {
  // do not initialize in case of error message without map form
  if (document.getElementById('__MAPDIV__') == null) return;

  DetectBrowser();
  // The default is movement over the map, it is decided later to show a 
  // crosshair or not.
  if ( (ns4) || (ie4) || (ns6) ) {
    document.onmousemove = MouseMoveHandler;
    if (ns4) 
      document.captureEvents(Event.MOUSEMOVE);
  }

  myMap = new MapObject;	// get the map object
  positionCrosshairDraw(myMap); // Position crosshair and draw layer

  // bind the jsGraphics to the drawLyr
  jg = new jsGraphics(drawLyr);
  jg.setColor("#ff0000");

  mapMovedLeft = myMap.Left;
  mapMovedTop  = myMap.Top;

  // if lastactivetool template was not replaced by mapserver (i.e. very 
  // initial loading) set to zoomin
  activeTool = active;
  if (activeTool == '') activeTool = 'zoomin';

  // Set path to button images
  gpath = imgpath;

  // Start ...
  mapserv = document.mapserv;
  setActiveTool(activeTool);

  window.status.clear;
  window.status = 'X/Y: ';

} // initMap

function mousehandlerReset () {
  if ( (ns4) || (ie4) || (ns6) ) 
    document.onmousemove = null;
  if (ns4) 
    document.captureEvents(null);
}

function positionCrosshairDraw (myMap) {
  // position crosshair over the map
  document.getElementById("chx").style.top    = myMap.Top;
  document.getElementById("chx").style.left   = myMap.Left;
  document.getElementById("chy").style.top    = myMap.Top;
  document.getElementById("chy").style.left   = myMap.Left;

  // position the draw layer over the map
  document.getElementById(drawLyr).style.top  = myMap.Top;
  document.getElementById(drawLyr).style.left = myMap.Left;
} // positionCrosshairDraw

// fill MapObject object with initial values
function MapObject(Width, Height, Left, Top, Cellsize, MinX, MaxY) {
  var map      = document.getElementById('__MAPDIV__');

  this.Width   = map.offsetWidth;
  this.Height  = map.offsetHeight;
  this.Left    = map.offsetLeft; 
  this.Top     = map.offsetTop;
  // correct for element nesting
  var offpar   = map.offsetParent;
  while (offpar) {
    this.Left  += offpar.offsetLeft;
    this.Top   += offpar.offsetTop;
    offpar     = offpar.offsetParent;
  }
  // Map coordinates
  this.MinX    = parseFloat(document.mapserv.minx.value);
  this.MaxY    = parseFloat(document.mapserv.maxy.value);
  // Cellsize
  this.Cellsize = parseFloat(document.mapserv.cellsize.value);
} // MapObject

// The most important Handler: Move the mouse ...
// Depending on the current mode different actions are performed on event
function MouseMoveHandler(e) {
  
  // Get the mouse coords depending on the detected browser
  if ( (ns4) || (ns6) ) {
    mouse_x = e.pageX; 
    mouse_y = e.pageY;
  }
  if (ie4) {
    mouse_x = event.x; 
    mouse_y = event.y;
  }
  if (ie5) {
    mouse_x = window.event.x-2;
    mouse_y = window.event.y;
  }

  // if we are over the map ...
  if ((myMap.Left <= mouse_x) && (mouse_x <= myMap.Left + myMap.Width) 
      && (myMap.Top <= mouse_y) && (mouse_y <= myMap.Top + myMap.Height)) {
      
    // Make the cross hair visible?
    CrosshairVisible(mouse_x, mouse_y);

    // Tool dependent actions ...
    if (actionType == 'box') {         // zoomin
      ActionTypeBox(mouse_x, mouse_y, mousePressed);
    } else if (actionType == 'poly') { // measure
      ActionTypePoly();
    } else if (actionType == 'point') {
      ActionTypePoint();
    } else {                           // Any other active tool (zoomout, info)
      ActionTypeOther();      
    }
    WriteStatusCoordinates(mouse_x, mouse_y, zr);
  }
  else { // Outside the map ...
    OutsideMap(mousePressed);
  }
} // MouseMoveHandler

function ActionTypeBox (mouse_x, mouse_y, mousePressed) {
    document.getElementById(drawLyr).style.cursor = 'crosshair';
    document.onmousedown = MouseDownBoxHandler;
    document.onmouseup = MouseUpBoxHandler;

    // Initial point of zoom already set?
    if (mousePressed == 1) {
        // If so, draw the zoom box ...
        DrawZoomBox(mouse_x, mouse_y);
    }
} // ActionTypeBox

function ActionTypePoly () {
    document.getElementById(drawLyr).style.visibility = 'visible';
    document.getElementById(drawLyr).style.cursor = 'crosshair';
    document.onkeypress = KeyPressPolyHandler;
} // ActionTypePoly

function ActionTypePoint () {
    document.getElementById(drawLyr).style.visibility = 'visible';
    document.getElementById(drawLyr).style.cursor = 'crosshair';
    document.onmousedown = MouseDownPointHandler;
} // ActionTypePoint

function ActionTypeOther () { // Any other active tool (zoomout, info)
    document.getElementById('__MAP__').style.cursor = 'crosshair';

    document.getElementById(drawLyr).style.visibility = 'visible';
    document.getElementById(drawLyr).style.cursor = 'crosshair';

    document.onmousedown = MouseMapDownDefault;
    document.onmouseup = MouseMapUpDefault;
} // ActionTypeOther

function OutsideMap (mousePressed) {
    if ((mousePressed) && (actionType == 'box')) {
      document.onmouseup = MouseUpBoxHandler;
    }
    else {
      document.onmouseup = MouseUpDefault;
    }

    document.onmousedown = MouseDownDefault;

    document.getElementById("chx").style.visibility = 'hidden';
    document.getElementById("chy").style.visibility = 'hidden';
} // OutsideMap

function WriteStatusCoordinates (mouse_x, mouse_y, zr) {
  mouse_x -= myMap.Left + 1;	//make mouse coordinates relativ
  mouse_y -= myMap.Top - 1;		//to map image, not to page
  window.status = 'X/Y: ' 
        + String(Math.round(Math.abs(mouse_x - zr[0][0]) * myMap.Cellsize) + myMap.MinX) + ' ' 
        + String(Math.round((Math.abs(mouse_y - zr[0][1]) - 2) * myMap.Cellsize) + myMap.MaxY);
} // WriteStatusCoordinates

// Draw zoom box for zoom in
function DrawZoomBox (mouse_x, mouse_y) {
   var boxstyle  = document.getElementById("box").style;
   boxstyle.visibility = 'hidden';
   boxstyle.top  = (Math.min(zr[0][1],mouse_y)) + 'px';
   boxstyle.left = (Math.min(zr[0][0],mouse_x)) + 'px';
   if (ie5) {
      boxstyle.width  = (Math.abs(mouse_x - zr[0][0]) + 1) + 'px';
      boxstyle.height = (Math.abs(mouse_y - zr[0][1]) + 1) + 'px';
   }
   else { // ns6
      boxstyle.width  = (Math.abs(mouse_x - zr[0][0])) + 'px';
      boxstyle.height = (Math.abs(mouse_y - zr[0][1]) - 2) + 'px';
   }
   boxstyle.visibility = 'visible';
} // DrawZoomBox

// set crosshair visible if required
function CrosshairVisible (mouse_x, mouse_y) {
    if (crosshair) {
      document.getElementById("chx").style.visibility = 'visible';
      document.getElementById("chy").style.visibility = 'visible';
      document.getElementById("chx").style.left = mouse_x + 1;
      document.getElementById("chy").style.top = mouse_y - 1;
    } else {
      document.getElementById("chx").style.visibility = 'hidden';
      document.getElementById("chy").style.visibility = 'hidden';
      document.getElementById("chx").style.left = mouse_x + 0;
      document.getElementById("chy").style.top = mouse_y - 0;
    }
} // CrosshairVisible

function MouseMapUpDefault(e) {
  var x = mouse_x - myMap.Left;
  var y = mouse_y - myMap.Top;
  mapserv.imgxy.value = x+" "+y;
  mapserv.submit();
} // MouseMapUpDefaults

function MouseMapDownDefault(e) {
// dummy
}

function MouseDownDefault(e) {
// dummy
}

function MouseUpDefault(e) {
// dummy
}

// Handler Mouse Button pressed in Box mode
function MouseDownBoxHandler(e) {
  drawLyr_clear();
  mousePressed = true;

  // Save the inital corner of zoom box
  zr[0][0] = zr[1][0] = mouse_x + 1;
  zr[0][1] = zr[1][1] = mouse_y - 1;

  // Start drawing 
  jg.drawLine(zr[0][0] - 5 - myMap.Left, zr[0][1] - myMap.Top, 
              zr[0][0] + 5 - myMap.Left, zr[0][1] - myMap.Top);
  jg.drawLine(zr[0][0] - myMap.Left, zr[0][1] - 5 - myMap.Top, 
              zr[0][0] - myMap.Left, zr[0][1] + 5 - myMap.Top);
  jg.paint();
} // MouseDownBoxHandler

// Handler Mouse Button released in Box mode: submit box coordinates
function MouseUpBoxHandler() {
  mousePressed = false;
  // Get the second box corner
  zr[1][0] = mouse_x + 1;
  zr[1][1] = mouse_y - 1;

  // Final painting 
  jg.drawLine(zr[1][0] - 5 - myMap.Left, zr[1][1] - myMap.Top, 
              zr[1][0] + 5 - myMap.Left, zr[1][1] - myMap.Top);
  jg.drawLine(zr[1][0] - myMap.Left, zr[1][1] - 5 - myMap.Top, 
              zr[1][0] - myMap.Left, zr[1][1] + 5 - myMap.Top);
  jg.paint();

  // Prepare the mapserv form values
  if ((zr[1][0] != zr[0][0]) || (zr[1][1] != zr[0][1])) {
    var minx = Math.min(zr[0][0], zr[1][0]) - myMap.Left;
    var miny = Math.min(zr[0][1], zr[1][1]) - myMap.Top;
    var maxx = Math.max(zr[0][0], zr[1][0]) - myMap.Left;
    var maxy = Math.max(zr[0][1], zr[1][1]) - myMap.Top;
    mapserv.imgbox.value = minx+" "+miny+" "+maxx+" "+maxy;
  }
  else {
    var x = zr[0][0] - myMap.Left;
    var y = zr[0][1] - myMap.Top;
    mapserv.imgxy.value = x+" "+y;
  }
  // Submit
  mapserv.submit();
} // MouseUpBoxHandler

// Handler Mouse Button pressed Point Mode: pick mouse coords and submit
function MouseDownPointHandler() {
  var x = mouse_x - myMap.Left;
  var y = mouse_y - myMap.Top;
  mapserv.imgxy.value = x+" "+y;
  mapserv.submit();
} // MouseDownPointHandler

// Handler KeyPressPolyHandler: Checked pressed key and process
function KeyPressPolyHandler(e) {

    var imgpoly = "";
    var vertices = pl.length;
    var keycode = GetPressedKey(e);
    var i=0;

    if ( keycode == 13 ) { // Return / Enter pressed, submit ..
        for (i=0; i < vertices; ++i)
            imgpoly = imgpoly +" "+ pl[i][0] +","+ pl[i][1];
        mapserv.imgpoly.value = imgpoly;
        pl = new Array();
        drawLyr_clear();
        // Measure tool doesn't lead to submit
        if (! measure) mapserv.submit();
    } else if (( keycode == 82 ) || ( keycode == 114 )) {
        // 'R' or 'r' (delete the last vertex)
        pl.pop();
        drawLyr_clear();
        vertices = pl.length;
        for (i=0; i < vertices; ++i) {
            jg.drawLine(pl[i][0] - 5, pl[i][1], 
                        pl[i][0] + 5, pl[i][1]);
            jg.drawLine(pl[i][0], pl[i][1] - 5, 
                        pl[i][0], pl[i][1] + 5);
            if (i > 0) jg.drawLine(pl[i-1][0], pl[i-1][1], pl[i][0], pl[i][1]);
        }
        jg.paint();
        if (measure) {  
            calculateMeasure(pl);
            document.getElementById("__AREA__").value = '';
}
    } else if ( (keycode == 65) || (keycode == 97) || 
                (keycode == 70) || (keycode == 102) ) { // a/A/f/F
        if (vertices > 2) {	// only if more than 2 vertices
            jg.drawLine(pl[vertices-1][0], pl[vertices-1][1], 
                        pl[0][0], pl[0][1]); // draw line back to starting point
            jg.paint();
 	    calculateArea(pl, vertices);	// calculate area of all triangles so far
            var vertex = new Array(pl[0][0], pl[0][1]);
	    pl[vertices] = vertex;
            calculateMeasure(pl);
        }
    } else if ( (keycode == 0) || (keycode == 27) ) {  // Escape
    // Delete (delete the entire line and measure value if necessary)
        pl = new Array();
        drawLyr_clear();
        if (measure) {
            document.getElementById("__MEAS__").value = '';
            document.getElementById("__AREA__").value = '';
        }
    } 
}

function GetPressedKey(e) {
  // Get the pressed key ...
  if (ns4 || ns6) {   
	return e.which; 
  } else {
    if (ie4 || ie5) {
      return window.event.keyCode;
    }
  }
}

function drawLyr_clear() {
  // Clean the graphics ground
  jg.clear();

  // hide the box layer
  document.getElementById("box").style.visibility = 'hidden';
  document.getElementById("box").style.width = 1;
  document.getElementById("box").style.height = 1;

  return;
}


function mapClick() {

  if (actionType == 'poly') {
    var vertices = pl.length;
    var vertex = new Array(mouse_x - myMap.Left, mouse_y - myMap.Top);
    pl[vertices] = vertex;    

    if (vertices == 0) {
      // First vertex, draw a single marker
      jg.drawLine(pl[vertices][0] - 5, pl[vertices][1], 
                  pl[vertices][0] + 5, pl[vertices][1]);
      jg.drawLine(pl[vertices][0], pl[vertices][1] - 5, 
                  pl[vertices][0], pl[vertices][1] + 5);
    } else {
      // next vertex, draw the marker and a line from the previous one ...
      jg.drawLine(pl[vertices][0] - 5, pl[vertices][1], 
                  pl[vertices][0] + 5, pl[vertices][1]);
      jg.drawLine(pl[vertices][0], pl[vertices][1] - 5, 
                  pl[vertices][0], pl[vertices][1] + 5);
      jg.drawLine(pl[vertices-1][0], pl[vertices-1][1], 
                  pl[vertices][0], pl[vertices][1]);
    }
    jg.paint(); // Draw vertices
    if (measure)
      calculateMeasure(pl);
  } // polyline mode
} // mapClick()

// This function is called by setActiveTool to choose input type (box,
// point, polyline)
function setActionType(type) {
  // Clean up
  if (actionType == 'poly') {
     // if set, drop a polyline
     if (pl.length > 0) pl = new Array();
     drawLyr_clear();
  }
  actionType = type
}

// calculate length of current polyline in mapunits
function calculateMeasure(pl) {

      var vertices = pl.length
      var totalLength = 0;

      for (i=1; i < vertices; ++i) {
          x0 = pl[i-1][0];
          x1 = pl[i][0];
          y0 = pl[i-1][1];
          y1 = pl[i][1];
          dx = (x1 - x0);
          dy = (y1 - y0);
          totalLength += Math.sqrt( (dx * dx) + (dy * dy) ); 
      }
      var siglen = String(Math.ceil(totalLength * myMap.Cellsize)).length
                   - String(Math.ceil(myMap.Width)).length;
      var reallength = String(Math.round( totalLength * myMap.Cellsize 
                           / Math.pow(10, siglen)) * Math.pow(10, siglen));
      if ( reallength.indexOf('.') > -1 ) {
          var por = reallength.indexOf('00000000')
          if ( por > -1 ) 
              reallength = reallength.substring(0, por);
      }	
      document.getElementById("__MEAS__").value = reallength + ' m';
} 

function calculateArea (pl, vertices) {
    if (vertices < 3) return;	// need minimum of three points
    var term = 0;
    for ( j=0; j < vertices - 1; j++ ) {
      term += pl[j][0] * pl[j+1][1] - pl[j+1][0] * pl[j][1];
    }
    term += pl[vertices - 1][0] * pl[0][1] - pl[0][0] * pl[vertices - 1][1];
    var totalarea = Math.abs(term * 0.5 * myMap.Cellsize * myMap.Cellsize);
    var siglen = String(Math.ceil(totalarea)).length
                    - String(myMap.Width).length - 1;
    var realarea = String(Math.round(totalarea / Math.pow(10, siglen)) 
	* Math.pow(10, siglen));
    if ( realarea * 1 > 1000000) {
        realarea = realarea.substring(0,realarea.length - 6) + ',' + 
                   realarea.substring(realarea.length - 6, 6);

        document.getElementById("__AREA__").value = realarea + ' kmē';
    } else 
        document.getElementById("__AREA__").value = realarea + ' mē';
//     document.getElementById("area").style.visibility = "visible";
}

// Update the map after a layer selection action etc.
function doUpdate() {
    mapserv.zoomdir.value = '0';
    mapserv.submit();
}

// // Evaluate the crosshair checkbox
// function doToggleCrosshair() {
//   if (document.getElementsByName("crosshair")[0].checked == true) {
//     crosshair = true;
//     mapserv.crosshairactive.value = "checked";
//   } else {
//     crosshair = false;
//     mapserv.crosshairactive.value = "";
//   }
// }

// Recenter map around (x,y)
function doRecenter(x,y) {
    mapserv.mode.value = 'browse';
    mapserv.zoomdir.value = '0';
    mapserv.imgxy.value= x + ' ' + y;
    mapserv.submit();
}

// Set old mapcenter and a given scale after change of mapsize etc.
function setMapxy(scale)
{
    /* Calculate and set center of current mapextent */
    extent = mapserv.extent.value.split(" ");
    minx = parseFloat(extent[0]);
    miny = parseFloat(extent[1]);
    maxx = parseFloat(extent[2]);
    maxy = parseFloat(extent[3]);

    diff_x=maxx-minx;
    shift_x=diff_x/2;
    center_x=minx+shift_x;

    diff_y=maxy-miny;
    shift_y=diff_y/2;
    center_y=miny+shift_y;
    mapserv.mapxy.value=center_x+" "+center_y;

    // Set scale to current scale
    mapserv.scale.value= scale

    // Submit form
    mapserv.zoomin.value= '0';
    mapserv.submit();
}

