// -----------------------------------------------------------------
//								 ArcWeb USA Site Starters 
//            Environmental Systems Research Institute
//                   Redlands, CA
// -----------------------------------------------------------------
// Class    : map 
// Purpose  : Abstracts map extent management issues.
// -----------------------------------------------------------------
// Calls    :
// Called by:
// -----------------------------------------------------------------
// Arguments:
// Globals  :
// Returns  :
// -----------------------------------------------------------------
// Notes    :
// -----------------------------------------------------------------
// History  :
// =================================================================

function map(left, top, width, height, widths, extent) {

	var PAN_FACTOR = 0.5;

	var m_rctExtent;     // Extent of the map in map units.  
											 // Can be set by a number of methods
											 // (e.g. setExtent, recenter, etc.).

	var m_iCurrentLevel; // Current level of the map.

	var m_iTagLeft = parseInt(left);     // These are dimensions, in
	var m_iTagTop = parseInt(top);       // page units, which describe
	var m_iTagWidth = parseInt(width);   // the position of the image 
	var m_iTagHeight = parseInt(height); // tag representing the map.

	var m_arrWidths = widths;  // Array of levels widths to associated with the map
					    							 // as an alternate means of zooming in or out.

		// Set the extent.

	setExtent(extent);

		// Method assignments.

  this.getTagLeft = getTagLeft;
	this.getTagTop = getTagTop;
	this.getTagWidth = getTagWidth;
	this.getTagHeight = getTagHeight;

	this.setExtent = setExtent;
	this.getExtent = getExtent;

	this.setLevel = setLevel;
	this.getLevel = getLevel;

	this.isEventOverMap = isEventOverMap;
	this.toMapPoint = toMapPoint;
	this.toPagePoint = toPagePoint;

	this.recenter = recenter;
	this.zoom = zoom;

	this.moveEast = moveEast;
	this.moveNorth = moveNorth;
	this.moveSouth = moveSouth;
	this.moveWest = moveWest;
	this.moveIn = moveIn;
	this.moveOut = moveOut;
	this.moveNorthEast = moveNorthEast;
	this.moveNorthWest = moveNorthWest;
	this.moveSouthEast = moveSouthEast;
	this.moveSouthWest = moveSouthWest;

  // -----------------------------------------------
  // --------------- Public Methods ----------------
  // -----------------------------------------------
	// The public methods for this class fall in fall 
  // into one of three categories.

  // -----------------------------------------------
	// Category 1: Methods that have to do with the 
  //             extent of the map.
  // -----------------------------------------------

	function getExtent() {
		return m_rctExtent;
	}

	function setExtent(rctExtent) {
		m_rctExtent = getAdjustedRect(rctExtent, m_iTagWidth, m_iTagHeight);
		m_iCurrentLevel = findLevel(m_rctExtent);
	}

	function getLevel() {
		return m_iCurrentLevel;
	}

	function setLevel(level) {
		m_iCurrentLevel = level;
		m_rctExtent = getAdjustedRect(buildLevelRect(m_iCurrentLevel), 
                                  m_iTagWidth, 
                                  m_iTagHeight);
	}

	function moveEast() {

		recenter(
						 new point(
											 m_rctExtent.getCenter().x + (m_rctExtent.getWidth() * PAN_FACTOR), 
											 m_rctExtent.getCenter().y
											 )
						);
		
	}

	function moveNorth() {

		recenter(
						 new point(
											 m_rctExtent.getCenter().x, 
											 m_rctExtent.getCenter().y + (m_rctExtent.getHeight() * PAN_FACTOR)
											 )
						);
		
	}
	
	function moveSouth() {

		recenter(
						 new point(
											 m_rctExtent.getCenter().x, 
											 m_rctExtent.getCenter().y - (m_rctExtent.getHeight() * PAN_FACTOR)
											 )
						);
		
	}

	function moveWest() {

		recenter(
						 new point(
											 m_rctExtent.getCenter().x - (m_rctExtent.getWidth() * PAN_FACTOR), 
											 m_rctExtent.getCenter().y
											 )
						);
		
	}
	
		function moveNorthEast(){
		moveEast();
		moveNorth();
	}
		function moveNorthWest(){
		moveWest();
		moveNorth();
	}
		function moveSouthEast(){
		moveEast();
		moveSouth();
	}
		function moveSouthWest(){
		moveWest();
		moveSouth();
	}

	function moveIn() {
		m_rctExtent.scale(0.5);
		
	}

	function moveOut() {
		m_rctExtent.scale(1.5);
		
	}
	
	function recenter(ptNewCenter) {
		m_rctExtent = new rect (
														ptNewCenter.x - (m_rctExtent.getWidth() / 2),
														ptNewCenter.y - (m_rctExtent.getHeight() / 2), 
														ptNewCenter.x + (m_rctExtent.getWidth() / 2),
														ptNewCenter.y + (m_rctExtent.getHeight() / 2)
													 );
	}

	function zoom(scale) {
		m_rctExtent.scale(scale);
		m_iCurrentLevel = findLevel(m_rctExtent);	
	}

  // -----------------------------------------------
	// Category 2: Methods that perform map/page transformation.
  // -----------------------------------------------

	function isEventOverMap(eventX, eventY) {

		if ((eventX <= this.getTagLeft()) ||
        (eventY <= this.getTagTop()) ||
				(eventX > (this.getTagWidth() + this.getTagLeft())) ||
				(eventY > (this.getTagHeight() + this.getTagTop()))) {
			return false;
		} else {
			return true;
		}

  }

	function toMapPoint(pageX, pageY) {

		var ptPixelsInTag = new point(pageX - m_iTagLeft, pageY - m_iTagTop);

		var dblX = m_rctExtent.getLeft() + ((ptPixelsInTag.x / m_iTagWidth) * (m_rctExtent.getWidth()));
		var dblY = m_rctExtent.getBottom() + (((m_iTagHeight - ptPixelsInTag.y) / m_iTagHeight) * (m_rctExtent.getHeight()));

		return (new point(dblX, dblY));

	}

	function toPagePoint(mapX, mapY) {

		var iX = ((mapX - m_rctExtent.getLeft()) / m_rctExtent.getWidth()) * m_iTagWidth;
		var iY = ((mapY - m_rctExtent.getBottom()) / m_rctExtent.getHeight()) * m_iTagHeight;

		return (new point((m_iTagLeft + iX), (m_iTagTop + (m_iTagHeight - iY))));

  }

  // -----------------------------------------------
	// Category 3: Just plain properties.
  // -----------------------------------------------

	function getTagLeft() {
		return m_iTagLeft;
	}

	function getTagTop() {
		return m_iTagTop;
	}

	function getTagWidth() {
		return m_iTagWidth;
	}

	function getTagHeight() {	
		return m_iTagHeight;
	}

  // ------------------------------
  // ------ Private Functions -----
  // ------------------------------


	function buildLevelRect(iLevel) {

		var dblWidth = m_arrWidths[iLevel - 1];

		return new rect(
										m_rctExtent.getCenter().x - (dblWidth / 2),
										m_rctExtent.getCenter().y, 
										m_rctExtent.getCenter().x + (dblWidth / 2), 
										m_rctExtent.getCenter().y
										);
	}

  function findLevel(rctExtent) {

		var iNumLevels = m_arrWidths.length;
		var iLevel = 0;

		for (i=0;i<iNumLevels;i++) {
			if (m_arrWidths[i] >= rctExtent.getWidth()) {
				iLevel = i + 1;
				break;
			}
		}

		if (iLevel == 0) {
			iLevel = iNumLevels;
		}

		return iLevel;

	}

	function getAdjustedRect(rctExtent, iWidthPixels, iHeightPixels) {

		var dblExtentRatio;
		var dblMapRatio;

		var dblNewWidth;
		var dblNewHeight;
		var rctReturn;

	  dblExtentRatio = rctExtent.getHeight() / rctExtent.getWidth();
		dblMapRatio = iHeightPixels / iWidthPixels;
		
		// -- adjust the width or height of the rectangle so its aspect ratio
		// -- matches that of the given dimensions

		if (dblExtentRatio > dblMapRatio) {
			rctReturn = makeRectFromCenter(rctExtent.getCenter(), (rctExtent.getHeight() / dblMapRatio), rctExtent.getHeight());
		} else {
	    rctReturn = makeRectFromCenter(rctExtent.getCenter(), rctExtent.getWidth(), (rctExtent.getWidth() * dblMapRatio));
		}

		return rctReturn;

	}

	function makeRectFromCenter(ptCenter, dblWidth, dblHeight) {

		var dblLeft, dblRight, dblBottom, dblTop;

		dblLeft = ptCenter.x - (dblWidth / 2);
		dblRight = ptCenter.x + (dblWidth / 2);
		dblBottom = ptCenter.y - (dblHeight / 2);
		dblTop = ptCenter.y + (dblHeight / 2);

		return new rect(dblLeft, dblBottom, dblRight, dblTop);

	}

}