function Map()
{
	var self = this;
	
	// properties
	this.MapLeft;
	this.MapTop;
	this.MapWidth;
	this.MapHeight;
	this.XmlReqMap; // XMLHttpRequest object used to request the map images
	this.Scale;
	this.Units;
	this.ActiveLayer;
	this.ExtentArray = new Array(); // Array of Extent objects
	this.ExtentIndex = 0;
	this.AcetatePoints = new Array(); // Array of Points to place on the acetate layer
	this.ShowEventAssets = false;
	this.ShowCustomRings = false;
	this.EventId = null;
	this.RingGroups;
	
	// methods
	this.getMap = getMap;
	this.zoomToFeature = zoomToFeature;
	this.getFirstMap = getFirstMap;
	this.getAdjoiningParcelsMap = getAdjoiningParcelsMap;
	this.getIdentifyMap = getIdentifyMap;
	this.processGetMap = processGetMap;
	this.processFirstGetMap = processFirstGetMap;
	this.processIdentifyMap = processIdentifyMap;
	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);
		calcScale();
		checkVisScale();
		
		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=";
		
		if(window.ActiveXObject)
		{
			self.XmlReqMap = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqMap)
			{
				self.XmlReqMap.onreadystatechange = processGetMap;
				self.XmlReqMap.open("GET", url, true); //async call
				self.XmlReqMap.send();
			}
		}
	}
	
	function getAdjoiningParcelsMap(objectId)
	{
		showLoading();
		updateCoords(self.getMinX(), self.getMinY(), self.getMaxX(), self.getMaxY(), true);
		calcScale();
		checkVisScale();
		
		url = server + "MapService.asmx/ProcessAdjoiningParcelsMap?imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=" + self.getMinX() + "&y1=" + self.getMinY() + "&x2=" + self.getMaxX() + "&y2=" + self.getMaxY() + "&layers=" + getVisibleLayerString() + "&objectId=" + objectId;
		
		if(window.ActiveXObject)
		{
			self.XmlReqMap = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqMap)
			{
				self.XmlReqMap.onreadystatechange = processGetMap;
				self.XmlReqMap.open("GET", url, true); //async call
				self.XmlReqMap.send();
			}
		}
	}
	
	function getIdentifyMap(x, y, dist, layer)
	{
		showLoading();
		updateCoords(self.getMinX(), self.getMinY(), self.getMaxX(), self.getMaxY(), false);
		calcScale();
		checkVisScale();
		
		url = server + "MapService.asmx/ProcessIdentifyMap?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=&x=" + x + "&y=" + y + "&dist=" + dist + "&activeLayer=" + layer;
		
		if(window.ActiveXObject)
		{
			self.XmlReqMap = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqMap)
			{
				self.XmlReqMap.onreadystatechange = processIdentifyMap;
				self.XmlReqMap.open("GET", url, true); //async call
				self.XmlReqMap.send();
			}
		}
	}
	
	function updateMap()
	{
		self.getMap("ZOOMIN", self.getMinX(), self.getMinY(), self.getMaxX(), self.getMaxY(), true);
	}
	
	function zoomToFeature(mapx1, mapy1, mapx2, mapy2)
	{
		if( (mapx1 == mapx2) && (mapy1 == mapy2) )
		{
			// this is a point feature, add the buffer to the envelope
			var x = mapx1;
			var y = mapy1;
			
			mapx1 = x - parseFloat(zoomTolerance);
			mapy1 = y - parseFloat(zoomTolerance);
			mapx2 = parseFloat(x) + parseFloat(zoomTolerance);
			mapy2 = parseFloat(y) + parseFloat(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=";

		if(window.ActiveXObject)
		{
			self.XmlReqMap = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqMap)
			{
				self.XmlReqMap.onreadystatechange = processGetMap;
				self.XmlReqMap.open("GET", url, true); //async call
				self.XmlReqMap.send();
			}
		}
	}
	
	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 url = server + "MapService.asmx/ProcessMap?command=ZOOMIN&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight + "&x1=11348983.626245823&y1=6897372.220456387&x2=11590000.675452957&y2=7088704.8023536";
		
		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=";
		
		if(window.ActiveXObject)
		{
			self.XmlReqMap = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqMap)
			{
				self.XmlReqMap.onreadystatechange = processFirstGetMap;
				self.XmlReqMap.open("GET", url, true); //async call
				self.XmlReqMap.send();
			}
		}
	}

	function processGetMap()
	{
		if(self.XmlReqMap.readyState == 4)
		{
			if(self.XmlReqMap.status == 200)
			{
				refreshMap(self.XmlReqMap.responseXML.documentElement);
			}
		}
	}
	
	function processIdentifyMap()
	{
		if(self.XmlReqMap.readyState == 4)
		{
			if(self.XmlReqMap.status == 200)
			{
				// set timer to flash map a few times
				var mapUrl = self.XmlReqMap.responseXML.documentElement.getElementsByTagName('mapUrl')[0].firstChild.data;
				
				// mapImageOverlay
				document.getElementById('divMapImageOverlay').innerHTML = "<img id=\"mapImageOverlay\" src=\"" + mapUrl + "\" style=\"position:absolute;top:0px;left:0px;width:" + self.MapWidth + ";height:" + self.MapHeight + ";\" onmousemove=\"return false;\">";
				
				setTimeout("divMapImageOverlay.style.display = 'block';", 500);
				setTimeout("divMapImageOverlay.style.display = 'none';", 1000);
				setTimeout("divMapImageOverlay.style.display = 'block';", 1500);
				setTimeout("divMapImageOverlay.style.display = 'none';", 2000);
				setTimeout("divMapImageOverlay.style.display = 'block';", 2500);
				setTimeout("divMapImageOverlay.style.display = 'none';", 3000);
		
				// disable loading div
				loading.style.visibility = "hidden";
			}
		}
	}
	
	function processFirstGetMap()
	{	
		if(self.XmlReqMap.readyState == 4)
		{
			if(self.XmlReqMap.status == 200)
			{				
				refreshMap(self.XmlReqMap.responseXML.documentElement);
				
				var layer = new Layer();
				layer.loadLayerList();
			}
		}
	}

	function refreshMap(response)
	{
		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);
			
			// 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
			var print = document.getElementById("printUrl");
			print.href = "print.aspx?minX=" + map.getMinX() + "&minY=" + map.getMinY() + "&maxX=" + map.getMaxX() + "&maxY=" + map.getMaxY() + "&layers=" + getVisibleLayerString() + "&acetatePoints=" + map.getAcetatePoints() + "&showEventAssets=" + map.ShowEventAssets + "&showEventRings=" + map.ShowEventRings + "&eventId=" + map.EventId + "&imageWidth=" + map.MapWidth + "&imageHeight=" + map.MapHeight ;
			
			calcScale();
			checkVisScale();
			
			mapLoaded = true;
			zs.setCurrent();
		}
		catch(e)
		{
			alert("System temporarily unavailable.  Please try your request again shortly.");
		}
		
		// disable loading div
		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;
		}
	}
	
	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)
	{
		//alert("input\npixelX: " + pixelX + "\npixelY: " + 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;
	}
}