function Map()
{
	var self = this;
	
	// properties
	this.Units = "FEET"; // Needed for calculating distance, scale, etc.
	this.Projection = "VAS"; // projection of the map.  VALAM - VDOT Lambert, VAS - Virginia South...
	this.MapLeft;
	this.MapTop;
	this.MapWidth;
	this.MapHeight;
	this.XmlReqMap; // XMLHttpRequest object used to request the map images
	this.Scale;
	this.ActiveLayer = 152; // maintain active layer position number
	this.ExtentArray = new Array(); // Array of Extent objects
	this.ExtentIndex = 0;
	this.AcetatePoints = new Array(); // Array of Points to place on the acetate layer
	
	// methods
	this.getMap = getMap;
	this.getMapQ = getMapQ;
	this.zoomToFeature = zoomToFeature;
	this.getFirstMap = getFirstMap;
	this.processGetMap = processGetMap;
	this.processAdjoinersGetMap = processAdjoinersGetMap;
	this.getAdjoiningFeaturesMap = getAdjoiningFeaturesMap;
	this.processFirstGetMap = processFirstGetMap;
	this.refreshMap = refreshMap;
	this.updateCoords = updateCoords;
	this.getMinX = getMinX;
	this.getMinY = getMinY;
	this.getMaxX = getMaxX;
	this.getMaxY = getMaxY;
	this.setMinX = setMinX;
	this.setMinY = setMinY;
	this.setMaxX = setMaxX;
	this.setMaxY = setMaxY;
	this.getLastMap = getLastMap;
	this.getNextMap = getNextMap;
	this.zoomToFullExtent = zoomToFullExtent;
	this.getAcetatePoints = getAcetatePoints;
	this.updateMap = updateMap;
	this.toMapPoint = toMapPoint;
	
	function getMap(command, mapx1, mapy1, mapx2, mapy2, fromUserAction)
	{
		showLoading();
		updateCoords(mapx1, mapy1, mapx2, mapy2, fromUserAction);
		
		
		//url = server + "MapService.asmx/ProcessMap?command=" + command + "&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + mapx1 + "&y1=" + mapy1 + "&x2=" + mapx2 + "&y2=" + mapy2 + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
		self.XmlReqMap = new TXmlHttp();
		//document.write(url);
		if(self.XmlReqMap)
		{
			self.XmlReqMap.onreadystatechange = processGetMap;
			url = server + "MapService.asmx/ProcessMap"; 
			self.XmlReqMap.open("POST", url, true); //async call
			self.XmlReqMap.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
			var vars = "command=" + command + "&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + mapx1 + "&y1=" + mapy1 + "&x2=" + mapx2 + "&y2=" + mapy2 + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
			self.XmlReqMap.send(vars);

		}
	}
	
	function getMapQ(command, mapx1, mapy1, mapx2, mapy2, fromUserAction,q)
	{
		showLoading();
		
		//adjust the zoomto envelope
		if(mapx1 == mapx2 || mapy1 == mapy2){
		//point feature...zoom to 1800
		dx = map.getMaxX() - map.getMinX();
		dy = map.getMaxY() - map.getMinY();
		centerX = mapx1;//map.getMaxX() - dx / 2;
		centerY = mapy1;//map.getMaxY() - dy / 2;
		
		scale = 1800 / map.Scale;
		dx = dx * scale / 2;
		dy = dy * scale / 2;
		
		mapx1 = parseInt(mapx1) - dx;
		mapy1 = parseInt(mapy1) - dy;
		mapx2 = parseInt(mapx2) + dx;
		mapy2 = parseInt(mapy2) + dy;
		
		}else if (command != "PAN"){
			mapx1 = parseInt(mapx1) - zoomTolerance;
			mapy1 = parseInt(mapy1) - zoomTolerance;
			mapx2 = parseInt(mapx2) + zoomTolerance;
			mapy2 = parseInt(mapy2) + zoomTolerance;
		}
		updateCoords(mapx1, mapy1, mapx2, mapy2, fromUserAction);
		
		
		//url = server + "MapService.asmx/ProcessMap?command=" + command + "&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + mapx1 + "&y1=" + mapy1 + "&x2=" + mapx2 + "&y2=" + mapy2 + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
		self.XmlReqMap = new TXmlHttp();
		//document.write(url);
		if(self.XmlReqMap)
		{
			self.XmlReqMap.onreadystatechange = processGetMap;
			url = server + "MapService.asmx/ProcessMap"; 
			self.XmlReqMap.open("POST", url, true); //async call
			self.XmlReqMap.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
			var vars = "command=" + command + "&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + mapx1 + "&y1=" + mapy1 + "&x2=" + mapx2 + "&y2=" + mapy2 + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=" + q;
			self.XmlReqMap.send(vars);

		}
	}
	
	
	function getAdjoiningFeaturesMap(objectId)
	{
		updateCoords(self.getMinX(), self.getMinY(), self.getMaxX(), self.getMaxY(), true);
		calcScale();
		checkVisScale();
		
		//url = server + "MapService.asmx/ProcessMap?command=ADJOINERS&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + self.getMinX() + "&y1=" + self.getMinY() + "&x2=" + self.getMaxX() + "&y2=" + self.getMaxY() + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
		self.XmlReqMap = new TXmlHttp();
		
		if(self.XmlReqMap)
		{
			self.XmlReqMap.onreadystatechange = processAdjoinersGetMap;
			url = server + "MapService.asmx/ProcessMap"; 
			self.XmlReqMap.open("POST", url, true); //async call
			self.XmlReqMap.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
			var vars = "command=" + command + "&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + mapx1 + "&y1=" + mapy1 + "&x2=" + mapx2 + "&y2=" + mapy2 + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
			self.XmlReqMap.send(vars);
		}
	}
	
	function updateMap()
	{
		self.getMap("ZOOMIN", self.getMinX(), self.getMinY(), self.getMaxX(), self.getMaxY(), true);
	}
	
	function zoomToFeature(mapx1, mapy1, mapx2, mapy2)
	{
		var zoomTol = 100;
		
		//adjust the zoomto envelope
		if(mapx1 == mapx2 || mapy1 == mapy2)
		{
			//point feature...zoom to 1800
			dx = map.getMaxX() - map.getMinX();
			dy = map.getMaxY() - map.getMinY();
			centerX = mapx1;//map.getMaxX() - dx / 2;
			centerY = mapy1;//map.getMaxY() - dy / 2;
			
			scale = 1800 / map.Scale;
			dx = dx * scale / 2;
			dy = dy * scale / 2;
			
			mapx1 = parseInt(mapx1) - dx;
			mapy1 = parseInt(mapy1) - dy;
			mapx2 = parseInt(mapx2) + dx;
			mapy2 = parseInt(mapy2) + dy;
		
		}else{
			mapx1 = parseInt(mapx1) - zoomTolerance;
			mapy1 = parseInt(mapy1) - zoomTolerance;
			mapx2 = parseInt(mapx2) + zoomTolerance;
			mapy2 = parseInt(mapy2) + zoomTolerance;
		}
		

	
		showLoading();
		updateCoords(mapx1, mapy1, mapx2, mapy2, true);
		calcScale();
		//url = server + "MapService.asmx/ProcessMap?command=ZOOMIN&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + mapx1 + "&y1=" + mapy1 + "&x2=" + mapx2 + "&y2=" + mapy2 + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
		self.XmlReqMap = new TXmlHttp();
		
		if(self.XmlReqMap)
		{
			self.XmlReqMap.onreadystatechange = processGetMap;
			url = server + "MapService.asmx/ProcessMap"; 
			self.XmlReqMap.open("POST", url, true); //async call
			self.XmlReqMap.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
			var vars = "command=ZOOMANDSELECT" + "&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + mapx1 + "&y1=" + mapy1 + "&x2=" + mapx2 + "&y2=" + mapy2 + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
			self.XmlReqMap.send(vars);
		}
	}
	
	function getFirstMap()
	{
		showLoading();
		// attempt to pull type/query from hidden textboxes on default.aspx page
		// this would happen if params were passed to the application to load 
		// only a certain set of layers to begin with
		var type = document.getElementById("type");
		var query = document.getElementById("query");
		var vars = "command=REFRESH&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=0&y1=0&x2=0&y2=0";
		
		if( (type != null && type.value != "") && (query != null && query.value != "") )
		{
			if( type.value == 'layer' )
				url += "&layers=" + query.value;
		}
		else
		{
			url += "&layers=";
		}
		
		url += "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
		
		self.XmlReqMap = new TXmlHttp();
		
		if(self.XmlReqMap)
		{
			self.XmlReqMap.onreadystatechange = processFirstGetMap;
			url = server + "MapService.asmx/ProcessMap"; 
			self.XmlReqMap.open("POST", url, true); //async call
			self.XmlReqMap.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
			//var vars = "command=" + command + "&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + mapx1 + "&y1=" + mapy1 + "&x2=" + mapx2 + "&y2=" + mapy2 + "&layers=" + getVisibleLayerString() + "&showNorthArrow=true&acetatePoints=" + self.getAcetatePoints() + "&spatialQuery=";
			self.XmlReqMap.send(vars);
		}
	}

	function processGetMap()
	{
		if(self.XmlReqMap.readyState == 4)
		{
			if(self.XmlReqMap.status == 200)
			{
				refreshMap(self.XmlReqMap.responseXML.documentElement, true);
			}
		}
	}
	
	function processAdjoinersGetMap()
	{
		if(self.XmlReqMap.readyState == 4)
		{
			if(self.XmlReqMap.status == 200)
			{
				refreshMap(self.XmlReqMap.responseXML.documentElement, false);
			}
		}
	}
	
	function processFirstGetMap()
	{	
		if(self.XmlReqMap.readyState == 4)
		{
			if(self.XmlReqMap.status == 200)
			{
				
				refreshMap(self.XmlReqMap.responseXML.documentElement, true);
				
				var layer = new Layer();
				layer.loadLayerList();
			}
		}
	}

	function refreshMap(response, doTurnOffLoading)
	{

		try
		{
			
			var mapUrl = response.getElementsByTagName('mapUrl')[0].firstChild.data;
			
			var legendUrl = response.getElementsByTagName('legendUrl')[0].firstChild.data;
			
			updateCoords(response.getElementsByTagName('minX')[0].firstChild.data, response.getElementsByTagName('minY')[0].firstChild.data, response.getElementsByTagName('maxX')[0].firstChild.data, response.getElementsByTagName('maxY')[0].firstChild.data, false);
			calcScale();
			checkVisScale();
			zs.setCurrent()
			//s.setValue(map.Scale);
			
			// get legend html element
			var imgLegend = document.getElementById('legendImage');
			
			// set map image to the url
			divMapImage.innerHTML = "<img id=\"mapImage\" src=\"" + mapUrl + "\" style=\"position:absolute;top:0px;left:0px;width:" + self.MapWidth + ";height:" + self.MapHeight + ";\" onmousemove=\"return false;\">";
			imgLegend.src = legendUrl;
			
			// must update the PRINT url on the screen
			document.getElementById("printminX").value = map.getMinX();
			document.getElementById("printminY").value = map.getMinY();
			document.getElementById("printmaxX").value = map.getMaxX();
			document.getElementById("printmaxY").value = map.getMaxY();
			document.getElementById("printvisibleLayers").value = getVisibleLayerString();
			document.getElementById("printacetatePoints").value = map.getAcetatePoints();
			//print.href = "print.aspx?minX=" + map.getMinX() + "&minY=" + map.getMinY() + "&maxX=" + map.getMaxX() + "&maxY=" + map.getMaxY() + "&layers=" + getVisibleLayerString() + "&acetatePoints=" + map.getAcetatePoints();
			
			
			// must update the EXPORT url on the screen
			//var exportLink = document.getElementById("exportUrl");
			//exportLink.href = "export.aspx?minX=" + map.getMinX() + "&minY=" + map.getMinY() + "&maxX=" + map.getMaxX() + "&maxY=" + map.getMaxY() + "&layers=" + getVisibleLayerString() + "&acetatePoints=" + map.getAcetatePoints();
			document.getElementById("exportminX").value = map.getMinX();
			document.getElementById("exportminY").value = map.getMinY();
			document.getElementById("exportmaxX").value = map.getMaxX();
			document.getElementById("exportmaxY").value = map.getMaxY();
			document.getElementById("exportvisibleLayers").value = getVisibleLayerString();
			document.getElementById("exportacetatePoints").value = map.getAcetatePoints();
			
			mapLoaded = true;
		}
		catch(e)
		{
			alert("System temporarily unavailable.  Please try your request again shortly.");
		}
		
		// disable loading div
		if( doTurnOffLoading )
			loading.style.visibility = "hidden";
	}

	function updateCoords(mapx1, mapy1, mapx2, mapy2, fromUserAction)
	{
		var ext = new Extent();
		ext.MinX = mapx1;
		ext.MinY = mapy1;
		ext.MaxX = mapx2;
		ext.MaxY = mapy2;
		
		// false if trying to view next or last map in memory
		if( fromUserAction )
		{
			// use current index as starting point
			// delete all other elements in array after this point
			self.ExtentIndex++;
			self.ExtentArray[self.ExtentIndex] = ext;
			
			self.ExtentArray.splice(self.ExtentIndex + 1, self.ExtentArray.length - 1);
		}
		else
		{
			self.ExtentArray[self.ExtentIndex] = ext;
		}
		//zs.setCurrent();
	}
	
	function getMinX() { return self.ExtentArray[self.ExtentIndex].MinX; }	
	function getMinY() { return self.ExtentArray[self.ExtentIndex].MinY; }	
	function getMaxX() { return self.ExtentArray[self.ExtentIndex].MaxX; }	
	function getMaxY() { return self.ExtentArray[self.ExtentIndex].MaxY; }
	function setMinX(pt) { self.ExtentArray[self.ExtentIndex].MinX = pt; }	
	function setMinY(pt) { self.ExtentArray[self.ExtentIndex].MinY = pt; }	
	function setMaxX(pt) { self.ExtentArray[self.ExtentIndex].MaxX = pt; }
	function setMaxY(pt) { self.ExtentArray[self.ExtentIndex].MaxY = pt; }
	
	function getNextMap()
	{		
		var ext = self.ExtentArray[self.ExtentIndex + 1];
		
		if( ext != null )
		{
			self.ExtentIndex++;
			getMap("MAP_FORWARD", ext.MinX, ext.MinY, ext.MaxX, ext.MaxY, false);
		}
	}
	
	function getLastMap()
	{	
		var ext = self.ExtentArray[self.ExtentIndex - 1];
		
		if( ext != null )
		{
			self.ExtentIndex--;
			getMap("MAP_BACK", ext.MinX, ext.MinY, ext.MaxX, ext.MaxY, false);
		}
	}
	
	function zoomToFullExtent()
	{
		var ext = self.ExtentArray[0];
		
		if( ext != null )
			getMap("REFRESH", ext.MinX, ext.MinY, ext.MaxX, ext.MaxY, true);
	}
	
	function getAcetatePoints()
	{
		// take self.AcetatePoints Array and convert to string
		// output: label,x,y|label,x,y|...
		var finalString = "";
		
		for( var i = 0; i < self.AcetatePoints.length; i++ )
		{
			var point = self.AcetatePoints[i];
			finalString += point.getLabel() + "," + point.getX() + "," + point.getY() + "|";
		}
		
		// remove last | from string
		finalString = finalString.substr(0, finalString.lastIndexOf('|'));
		
		return finalString;
	}
	
	function toMapPoint(pixelX, pixelY)
	{

		xPx = (map.getMaxX() - map.getMinX()) / map.MapWidth;//units/pixel
		yPx = (map.getMaxY() - map.getMinY()) / map.MapHeight;//units/pixel
		
		mapX = (pixelX * xPx) + parseFloat(map.getMinX());
		mapY = (map.MapHeight - pixelY) * yPx + parseFloat(map.getMinY());
		
		var arrPt = new Array();
		arrPt[0] = mapX;
		arrPt[1] = mapY;
		
		//alert("OUTPUT\nmapX: " + mapX + "\nmapY: " + mapY);
		return arrPt;
	}
}