var map_marker_img = new Img("/images/mapicons/marker-sm.png",53,100);
var map_marker_icon = new Icon(map_marker_img);
var all_images = [map_marker_icon, map_marker_icon, map_marker_icon, map_marker_icon, map_marker_icon, map_marker_icon];





function gvGetNewVarName(varStem)
{
	var varName = varStem + "_" + gvMonotonicCounter;
	gvMonotonicCounter++;
	return varName;
}

function EuclideanProjection(NumZoomLevels)
{
		this.pixelsPerLonDegree=[];
		this.pixelsPerLonRadian=[];
		this.pixelOrigo=[];
		this.tileBounds=[];
		var BitmapSize = 256;
		var c=1;
		
		for(var d=0; d < NumZoomLevels; d++)
		{
				var e= BitmapSize / 2;
				this.pixelsPerLonDegree.push(BitmapSize / 360);
				this.pixelsPerLonRadian.push(BitmapSize / (2*Math.PI));
				this.pixelOrigo.push(new GPoint(e,e));
				this.tileBounds.push(c);
				BitmapSize *= 2;
				c*=2
		}
}

// == Attach it to the GProjection() class ==
EuclideanProjection.prototype=new GProjection();


// == A method for converting latitudes and longitudes to pixel coordinates == 
EuclideanProjection.prototype.fromLatLngToPixel=function(LatLng,zoom)
{
		// Work out the position on the GV map, which is a notional 10k square positioned at the origin, 
		// i.e. we are mapping the 90 LatLng square onto the 10k GV square
		var RawMapX = LatLng.lng() / gvMapFactor;
		var RawMapY = -LatLng.lat() / gvMapFactor;
		
		// Now map this 10k square onto a 1:1 bitmap of the entire GV map, based
		// on the size of GV map tiles (at zoom level 1, the closest)
		var RawPixelX = RawMapX * gvTileSize;
		var RawPixelY = RawMapY * gvTileSize;
		
		// Now account for the fact that the map may be zoomed out
		zoom = gvConvertGMapZoomToGVZoom(zoom);
		var ZoomFactor = Math.pow(2, zoom - 1);

		var PixelX = RawPixelX / ZoomFactor;
		var PixelY = RawPixelY / ZoomFactor;
		
		return new GPoint(PixelX, PixelY)
};

// == a method for converting pixel coordinates to latitudes and longitudes ==
EuclideanProjection.prototype.fromPixelToLatLng=function(pos,zoom,c)
{
		// First, account for the fact that the map may be zoomed out
		zoom = gvConvertGMapZoomToGVZoom(zoom);
		var ZoomFactor = Math.pow(2, zoom - 1);

		var RawPixelX = pos.x * ZoomFactor;
		var RawPixelY = pos.y * ZoomFactor;
		
		// Now map this 1:1 bitmap position onto a 10k square of GV tiles, located at the origin
		var RawMapX = RawPixelX / gvTileSize;
		var RawMapY = RawPixelY / gvTileSize;
		
		// Now map this 10k GV square onto a 90 LatLng square
		var Lng = RawMapX * gvMapFactor;
		var Lat = RawMapY * gvMapFactor;
		
		return new GLatLng(-Lat,Lng,c)
};

// == a method that checks if the x/y value is in range ==
EuclideanProjection.prototype.tileCheckRange=function(pos, zoom, tileSize)
{
		if ((pos.x < 0) || (pos.y < 0)) 
				return false;
				
		return true
}

// == a method that returns the width of the tilespace ==      
EuclideanProjection.prototype.getWrapWidth=function(zoom) 
{
		return this.tileBounds[zoom]*256;
}

function landCustomGetTileUrl(pos, zoom) 
{
		zoom = gvConvertGMapZoomToGVZoom(zoom);
		
		var Factor = Math.pow(2, zoom - 1);
		
		
		
		
		var sub = 1279.0;
		sub += 256;
		if (zoom == 2)
		{
		    pos.x *= 2.0;
		    sub = sub - 1;
		    pos.y *= zoom;
		    
		 }
		 else if (zoom == 3)
		{
		    pos.x *= 4.0;
		    sub = sub - 3;
		    //sub = 1276.0;
		    pos.y *= 4.0;
		    
		 }
		  else if (zoom == 4)
		{
		    pos.x *= 8.0;
		    sub = sub - 7;
		    //sub = 1272.0;
		    pos.y *= 8.0;
		    
		 }
		 else if (zoom == 5)
		{
		    pos.x *= 16.0;
		    sub = sub - 15;
		    //sub = 1264.0;
		    pos.y *= 16.0;
		    
		 }
		 else if (zoom == 6)
		{
		    pos.x *= 32.0;
		    sub = sub - 31;
		    //sub = 1248.0;
		    pos.y *= 32.0;
		 }
		 else if (zoom == 7)
		{
		    pos.x *= 64.0;
		    sub = sub - 63;
		    //sub = 1216.0;
		    pos.y *= 64.0;
		    
		 }
		 else if (zoom == 8)
		{
		    pos.x *= 128.0;
		    sub = sub - 127;
		    //sub = 1152.0;
		    pos.y *= 128.0;
		    
		 }
		 
		    
		var x = pos.x;
		var y = sub - pos.y;

		//f = "GetImage.aspx?gv_x="+x+"&gv_y="+y+"&zoom="+zoom;
		f = "http://Gridaverse.com/sims2/256/" + zoom + "/sim_" + x + "_" + y + ".jpg";
		document.title = f;
		return f;
}




// ------------------------------------
//
//              XYPoint
//
//
// ------------------------------------

function XYPoint(x,y)
{
		this.x = x;
		this.y = y;
}

XYPoint.prototype.GetGLatLng = function()
{
		InversIt = 1279.0 - this.y;
		InversIt += 1;
		
		return new GLatLng(-InversIt * gvMapFactor, this.x * gvMapFactor);
}

XYPoint.prototype._SetFromGLatLng = function(gpos)
{
		this.x = gpos.lng() / gvMapFactor;
		this.y = -gpos.lat() / gvMapFactor;
		
		// Invert the GV coord hack (gorgeous!)
		this.y -= 1;
		this.y = 1279.0 - this.y;
}

// ------------------------------------
//
//               Bounds
//
// ------------------------------------

function Bounds(xMin, xMax, yMin, yMax)
{
		if (arguments.length == 4)
		{
				this.xMin = xMin;
				this.xMax = xMax;
				this.yMin = yMin;
				this.yMax = yMax;
		}
		else
		{
				this.xMin = 0;
				this.xMax = 0;
				this.yMin = 0;
				this.yMax = 0;
		}
}

Bounds.prototype._SetFromGLatLngBounds = function(gbounds)
{
		var SW = new XYPoint();
		var NE = new XYPoint();
		
		SW._SetFromGLatLng(gbounds.getSouthWest());
		NE._SetFromGLatLng(gbounds.getNorthEast());
		
		this.xMin = SW.x;
		this.yMin = SW.y;
		
		this.xMax = NE.x;
		this.yMax = NE.y;
}

// ------------------------------------
//
//       GVPoint - UNIMPLEMENTED!
//
// ------------------------------------

function GVPoint(regionName, localX, localY)
{
		this.x = 0;
		this.y = 0;
}


// ------------------------------------
//
//               Img
//
// ------------------------------------

function Img(imgURL, imgWidth, imgHeight, hasAlpha)
{
		this.isAlpha = function()
		{
				return this.alpha;
		};
		
		this.URL = imgURL;
		this.width = imgWidth;
		this.height = imgHeight;
		
		if (hasAlpha)
		{
				this.alpha = true;
		}
		else
		{
				this.alpha = false;
		}
}


// ------------------------------------
//
//               Icon
//
// ------------------------------------

function Icon(imageMain, imageShadow)
{
		this.hasShadow=function()
		{
				if (this.shadowImg)
				{
						return true;
				}
				else
				{
						return false;
				}
		};
		
		this.mainImg=imageMain;
		
		if (imageShadow)
		{
				this.shadowImg = imageShadow;
		}
}






// ------------------------------------
//
//            GVMapOptions
//
// ------------------------------------

function GVMapOptions(options)
{
		this.hasZoomControls=true;
		this.hasOverviewMapControl=true;
		this.hasScaleControl = false;
		this.hasMenuMapTypeControl = false;
		this.doubleClickHandler=null;
		this.singleClickHandler=null;
		this.onStateChangedClickHandler=null;
		this.zoomMin = gvMinZoomLevel;
		this.zoomMax = gvMaxZoomLevel;
		
		if (options)
				Object.extend(this, options);
};

// ------------------------------------
//
//               GVMap
//
// ------------------------------------


function GVMap(map_element, map_options)
{
	this.ID = null;
	this.showingHoverWindow = false;
	
	if (GBrowserIsCompatible()) 
	{
        this.options = new GVMapOptions(map_options);
        this.mapProjection = new EuclideanProjection(18);

        // Create our custom map types and initialise map with them
        var mapTypes = this.CreateMapTypes();
        var mapDiv = this.CreateMapDiv(map_element);
        this.GMap = new GMap2(mapDiv, {"mapTypes" : mapTypes});

        // Link GMap back to us
        this.GMap.gvMap = this;

        // No GMap info windows open yet
        this.currentMapWindow = null;

	    // No data markers yet
	    this.dataMarkers = [];
			
		if (this.options.hasZoomControls)
		{
			this.GMap.addControl(new GLargeMapControl());
		}
		
		if (this.options.hasOverviewMapControl)
		{
			this.GMap.addControl(new GOverviewMapControl());
		}
		
		if (this.options.hasMenuMapTypeControl)
		{
		    this.GMap.addControl(new GMenuMapTypeControl());
		}
		
		if (this.options.hasScaleControl)
		{
		    this.GMap.addControl(new GScaleControl());
		}
		
		// Use GMaps xtra control methods
		this.GMap.enableContinuousZoom();
		this.GMap.enableScrollWheelZoom();
		
		this.GMap.setCenter(new GLatLng(0, 0), 16);

		// Allow user to switch map types
		this.GMap.addControl(new GMapTypeControl());

		// Install our various event handlers
		var gvMap = this;
		
		GEvent.addListener(
				this.GMap, 
				"click", 
				function(marker, point) 
				{
						gvMapClickHandler(gvMap, marker, point);
				});
				

		GEvent.addListener(
				this.GMap, 
				"dblclick", 
				function(marker, point) 
				{
						gvMapDoubleClickHandler(gvMap, marker, point);
				});
		
		GEvent.addListener(
				this.GMap, 
				"moveend", 
				function() { gvMap.onStateChangedHandler(); });

		if (!this.options || !this.options.disableDataInfo)
		{
			// Data marker info/tooltips enabled, so we need mouse move/mouse out handlers
			GEvent.addListener(
					this.GMap, 
					"mousemove", 
					function(pos) { gvMap.onMouseMoveHandler(pos); });
	
			GEvent.addListener(
					this.GMap, 
					"mouseout", 
					function(pos) { gvMap.onMouseOutHandler(pos); });
		}

				
	    GEvent.addListener(
			    this.GMap, 
			    "dragstart", 
			    function() 
			    {
					    gvMapDragHandler(gvMap);
			    }); 
			    
	    GEvent.addListener(
			    this.GMap, 
			    "zoomend", 
			    function(oldLevel, newLevel) 
			    {
					    gvMapZoomEndHandler(gvMap, oldLevel, newLevel);
			    });        
				
	    // Moved this to the end as GMaps seemed to fail if I did it right
	    // after map creation, and I don't have time to debug other people's code.
        this.GMarkerManager = new GMarkerManager(this.GMap);			
	}
	else
	{
			// Browser does not support Google Maps
			this.GMap = null;
	}
}



GVMap.prototype.CreateMapTypes = function()
{
	var mapTypes = [];
	
	var copyCollection = new GCopyrightCollection('Gridaverse');
	var copyright = new GCopyright(1, new GLatLngBounds(new GLatLng(0, 0), new GLatLng(-90, 90)), 0, "(C) 2008 Gridaverse");
	copyCollection.addCopyright(copyright);

	// Create the 'Land' type of map
	var landTilelayers = [new GTileLayer(copyCollection, 10, 16)];
	landTilelayers[0].getTileUrl = landCustomGetTileUrl;
	
	var landMap = new GMapType(landTilelayers, this.mapProjection, "Land", {errorMessage:"No GV data available"});
	landMap.getMinimumResolution = function() { return gvConvertGMapZoomToGVZoom(gvMinZoomLevel); };
	landMap.getMaximumResolution = function() { return gvConvertGMapZoomToGVZoom(gvMaxZoomLevel); };

	mapTypes.push(landMap);
		
	return mapTypes;
}

GVMap.prototype.CreateMapDiv = function(mainDiv)
{
	var GVMap = this;

	// Create a unique ID for the region name field
	var inputFieldID = "gvRegionNameField_" + getRandomNumber(10000);
	
	// Create a click handler
	var clickHandler = function() 
	{ 
		var textField = document.getElementById(inputFieldID);
		
		if (textField)
		{
			GVMap.gotoRegion(textField.value); 
		}
		else
		{
			alert("Can't find textField!");
		}
		
		return false;
	};
	
	// Create a div to be the main map container as a child of the main div
	var mapDiv = document.createElement("div");
	
	// Match parent height
	mapDiv.style.height = "99%";
	
	
	mainDiv.appendChild(mapDiv);

	return mapDiv;
}

GVMap.prototype.gotoRegion = function(regionName)
{
	
}

function CenterMap(gv_x, gv_y, pos_x, pos_y, zoom)
{
    theMap.centerAndZoomAtGVCoord(gv_x, gv_y, pos_x, pos_y, zoom);
}

GVMap.prototype.centerAndZoomAtGVCoord = function(gv_x, gv_y, pos_x, pos_y, zoom)
{
		if (this.GMap != null)
		{
		    var coords = {'x' : gv_x, 'y' : gv_y };
            var gvurl_grid_x = parseFloat(coords.x + (pos_x) / 256);
            var gvurl_grid_y = parseFloat(coords.y + (pos_y) / 256);

			zoom = this._forceZoomToLimits(zoom);
			this.GMap.setCenter(new XYPoint(gvurl_grid_x, gvurl_grid_y).GetGLatLng(), gvConvertGVZoomToGMapZoom(zoom));
		}
}

GVMap.prototype.disableDragging = function()
{
		if (this.GMap != null)
		{
				this.GMap.disableDragging();
		}
}

GVMap.prototype.enableDragging = function()
{
		if (this.GMap != null)
		{
				this.GMap.enableDragging();
		}
}

GVMap.prototype.getViewportBounds = function()
{
		if (this.GMap != null)
		{
				gLatLngBounds = this.GMap.getBounds();
				
				viewBounds = new Bounds();
				viewBounds._SetFromGLatLngBounds(gLatLngBounds);
				return viewBounds;
		}
}

GVMap.prototype.getMapCenter = function()
{
		if (this.GMap != null)
		{
				gCenter = this.GMap.getCenter();
				
				center = new XYPoint();
				center._SetFromGLatLng(gCenter);
				return center;
		}
}

function gvMapDragHandler(gvMap)
{
		if (gvMap.currentMapWindow != null)
		{
				if (gvMap.currentMapWindow.options)
				{
						if (gvMap.currentMapWindow.options.closeOnMove)
						{
								gvMap.GMap.closeInfoWindow();
								gvMap.currentMapWindow = null;
						}
				}
		}    
}





GVMap.prototype.addMapWindow = function(mapWindow, pos)
{
		if (this.GMap != null)
		{                           
				this.GMap.openInfoWindowHtml(pos.GetGLatLng(), mapWindow.text, mapWindow.getGMapOptions());
				this.currentMapWindow = mapWindow;
		}
}



GVMap.prototype.panBy = function(x, y)
{
		if (this.GMap != null)
		{
				var pos = this.GMap.getCenter();
				
				var tileSize = new XYPoint(x, y);
				
				var offset = this.mapProjection.fromPixelToLatLng(tileSize, this.GMap.getZoom());
				
				var newPos = new GLatLng(pos.lat() + offset.lat(), pos.lng() + offset.lng());
				this.GMap.panTo(newPos);
		}
}

GVMap.prototype.panLeft = function()
{
		this.panBy(-gvTileSize, 0);
}

GVMap.prototype.panRight = function()
{
		this.panBy(gvTileSize, 0);
}

GVMap.prototype.panUp = function()
{
		this.panBy(0, -gvTileSize);
}

GVMap.prototype.panDown = function()
{
		this.panBy(0, gvTileSize);
}

GVMap.prototype.panOrRecenterToGVCoord = function(pos, forceCenter)
{
		if (this.GMap != null)
		{
				this.GMap.panTo(pos.GetGLatLng());
		}
}

GVMap.prototype.showMarkersForTile = function(xPos, yPos)
{
	// Check to see if we already have these markers
	if (this.dataMarkers[xPos]) 
	{
		if (this.dataMarkers[xPos][yPos])
		{
			// We already have these markers, or are currently fetching them
			return;
		}
	}
	
	// Need to add markers - init array if required
	if (!this.dataMarkers[xPos])
	{
		this.dataMarkers[xPos] = [];
	}

	// Make sure we only do this once - this gvot will be filled in with the actual
	// tile info when it arrives (see addMarkersToMap)
	this.dataMarkers[xPos][yPos] = true;
	
	// Fetch information about these markers	
	var varName = gvGetNewVarName("gvMarkerInfo");
		var scriptURL = gvMarkerInfoService + "?var=" + varName + "&x=" + xPos + "&y=" + yPos + "&blocksize=" + gvDataMarkerChunkSize;

		var GVMap = this;

//	alert ("Fetching info for (" + xPos + ", " + yPos + ")");    

		// Once the script has loaded, we use the result to center the map on the position
		var onLoadHandler = function () 
		{
			// Pick up the value
			var tileInfo = eval(varName);
			
			if (tileInfo.error)
			{
				// Unable to find data marker info - fail silently
		}
		else
		{
			GVMap.addMarkersToMap(tileInfo, xPos, yPos);
		}
		};
						
		gvAddDynamicScript(scriptURL, onLoadHandler);
}

