/*
	Author: Jon Howell
	Date: November 2007
	Copyright 2007 Microsoft Corporation.
*/
function SendAsyncRequest(url)
{
	//dbg("Requesting "+url);
	scriptTag = document.createElement("script");
	scriptTag.src = url;
	scriptTag.type = "text/javascript";
	document.body.appendChild(scriptTag);
}
function radToDeg(rad)
{
	return rad*180.0/Math.PI;
}

function degToRad(deg)
{
	return deg*Math.PI/180.0;
}

function sinh(v)
{
	var x = Math.exp(v);
	return (x-1/x)/2.0;
}

function Point(x,y)
{
	this.x = x;
	this.y = y;
	this.toString = function toString()
	{
		return "("+this.x+","+this.y+")";
	}
	return this;
}

function MercatorProjection()
{
	this.xCenter = 0.0;
	this.xScale = 1.0/360.0;
	this.xOff = 0.5;
	this.yBound = 85.0;
	this.yScale = -0.5/Math.log(Math.tan(Math.PI/4.0+degToRad(this.yBound)/2.0));
	this.yOff = 0.5;

	this.Clip = function(ll)
	{
		return new VELatLong(
			Math.max(Math.min(ll.Latitude, 85.0), -85.0),
			Math.max(Math.min(ll.Longitude, 180.0), -180.0));
	}

	this.LLToMercator = function(ll)
	{
		ll = this.Clip(ll);
		var pt = new Point(0,0);
		pt.x = ll.Longitude - this.xCenter;
		pt.x = pt.x*this.xScale + this.xOff;
		pt.y = Math.log(Math.tan(Math.PI/4.0+degToRad(ll.Latitude)/2.0));
		pt.y = pt.y * this.yScale + this.yOff;
		return pt;
	}

	this.MercatorToLL = function(pt)
	{
		pt = Point(
			(pt.x-this.xOff)/this.xScale,
			(pt.y-this.yOff)/this.yScale);
		ll = new VELatLong(
			radToDeg(Math.atan(sinh(pt.y))),
			pt.x + this.xCenter);
		return ll;
	}

	return this;
}

function MercatorToTile(xy, zoom)
{
	var zf = 1<<zoom;
	pt = new Point(
		Math.min(parseInt(xy.x*zf), zf-1),
		Math.min(parseInt(xy.y*zf), zf-1));
	pt.z = zoom;
	return pt;
}

function TileULToMercator(x, y, zoom)
{
	var zf = (1<<zoom)*1.0;
	pt = new Point(
		x/zf,
		y/zf);
	pt.z = zoom;
	return pt;
}

function TileAddress(x, y, z)
{
	var str = "";
	for (var shift=0; shift<z; shift++)
	{
		var xc = (x>>(z-shift-1))&1;
		var yc = 2*((y>>(z-shift-1))&1);
		str += ""+(xc+yc);
	}
	return str;
}
function GetSize()
{
	var myWidth = 0, myHeight = 0;

	if( typeof( window.innerWidth ) == 'number' ) {
		//Non-IE
		myWidth = window.innerWidth;
		myHeight = window.innerHeight;
	} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
		//IE 6+ in 'standards compliant mode'
		myWidth = document.documentElement.clientWidth;
		myHeight = document.documentElement.clientHeight;
	} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
		//IE 4 compatible
		myWidth = document.body.clientWidth;
		myHeight = document.body.clientHeight;
	}

	if (myWidth > 0)
	{
		var result = new Array();
		result[0] = myWidth;
		result[1] = myHeight;
		return result;
	}
	else
	{
		return null;
	}
}

function SetMapSize()
{
	var browserSize = GetSize();

	var div = document.getElementById("Map");

	if (browserSize != null)
	{
			div.style.width = (browserSize[0] - 25) + "px";
			div.style.height = (browserSize[1] - 25) + "px";
	}
	else
	{
			div.style.width = "800px";
			div.style.height = "600px";
	}
	div.style.overflow = "hidden";
	div.style.position = "relative";
}

var liveLayers = new Array();	// maps basename to VectorTileLayer object.

function Tile(tileAddress, bounds, vectorTileLayer)
{
	this.tileAddress = tileAddress;
	this.bounds = bounds;
	this.vectorTileLayer = vectorTileLayer;
	this.debugLayer = null;
	this.dataLayer = null;
	this.segmentCount = 0;

	this.Init = function Init()
	{
		if (false)	// enable to see tile boundaries
		{
			this.debugLayer = new VEShapeLayer();
			var ctrPin = new VEShape(
				VEShapeType.Pushpin, this.Interpolate(0.5,0.5));
			ctrPin.SetTitle(this.tileAddress);
			ctrPin.SetDescription("Look at <a href=\"states/testTiles/"
				+this.tileAddress
				+".js\">this tile's data</a>.");
			this.debugLayer.AddShape(ctrPin);

			var locs = new Array();
			locs.push(this.Interpolate(0.05, 0.05));
			locs.push(this.Interpolate(0.95, 0.05));
			locs.push(this.Interpolate(0.95, 0.95));
			locs.push(this.Interpolate(0.05, 0.95));
			locs.push(this.Interpolate(0.05, 0.05));
			var boundary = new VEShape(VEShapeType.Polyline, locs);
			boundary.SetLineColor(new VEColor(0, 255, 64, 1.0));
			boundary.SetLineWidth(2);
			boundary.HideIcon();
			this.debugLayer.AddShape(boundary);

			this.vemap.AddShapeLayer(this.debugLayer);
		}

		this.vectorTileLayer.liveTiles[tileAddress] = this;
		SendAsyncRequest(this.vectorTileLayer.vectorTileUrlBase+tileAddress+".js");
	}

	this.Interpolate = function Interpolate(kx, ky)
	{
		return new VELatLong(
			this.bounds.TopLeftLatLong.Latitude+
			kx*(this.bounds.BottomRightLatLong.Latitude
				-this.bounds.TopLeftLatLong.Latitude),
			this.bounds.TopLeftLatLong.Longitude+
			ky*(this.bounds.BottomRightLatLong.Longitude
				-this.bounds.TopLeftLatLong.Longitude));
	}

	this.Dispose = function Dispose()
	{
		if (this.debugLayer != null)
		{
			this.vectorTileLayer.vemap.DeleteShapeLayer(this.debugLayer);
		}
		if (this.dataLayer != null)
		{
			this.vectorTileLayer.vemap.DeleteShapeLayer(this.dataLayer);
		}
		this.vectorTileLayer.ChangeSegmentCount(-this.segmentCount);
		this.segmentCount = 0;
	}

	this.AddDataLayer = function AddDataLayer(shapeLayer)
	{
		if (this.dataLayer != null)
		{
			this.vectorTileLayer.vemap.DeleteShapeLayer(this.dataLayer);
		}
		this.dataLayer = shapeLayer;
		this.vectorTileLayer.vemap.AddShapeLayer(this.dataLayer);

		var newCount = this.dataLayer._segmentCount;
		if (newCount==null)
		{
			newCount = 0;
		}
		this.vectorTileLayer.ChangeSegmentCount(newCount - this.segmentCount);
		this.segmentCount = newCount;
	}

	this.Init();
	return this;
}

// Short function names, to be called by .js-encoded tiles
_LSh = function LoadShape(uglyArray)
{
	var shapeLayer = new VEShapeLayer();
	var segmentCount = 0;
	for (var i=0; i<uglyArray.length-1; i+=1)
	{
		shape = uglyArray[i];
		shapeLayer.AddShape(shape);
		segmentCount += shape._segmentCount;
	}
	shapeLayer._segmentCount = segmentCount;
	return shapeLayer;
}

_LSg = function LoadSegment(uglyArray)
{
	var locs = new Array()
	for (var i=0; i<uglyArray.length-1; i+=2)
	{
		locs.push(new VELatLong(uglyArray[i], uglyArray[i+1]));
	}
	var shape = new VEShape(VEShapeType.Polyline, locs);
	shape.HideIcon();
	shape.SetLineColor(new VEColor(0, 0, 192, 1.0));
	shape._segmentCount = uglyArray.length-1;
	return shape;
}

// Called by tile loads
function DefineVectorTile(basename, tileAddress, shapeLayer)
{
	vectorTileLayer = liveLayers[basename];
	if (vectorTileLayer == null)
	{
		// late callback for unknown layer?
		//dbg("Ignoring callback for "+basename);
		return;
	}

	//dbg("callback received for "+tileAddress+": "+shapeLayer);
	tile = vectorTileLayer.liveTiles[tileAddress]
	if (tile!=null)
	{
		tile.AddDataLayer(shapeLayer)
	}
}

function VectorTileLayer(vemap, basename, vectorTileUrlBase, maxZoom, zoomOffset)
{
	this.vemap = vemap;
	this.basename = basename;
	this.vectorTileUrlBase = vectorTileUrlBase;
	this.loadedTiles = new Array();
	this.mercatorProjection = new MercatorProjection();
	this.liveTiles = new Array();
	this.segmentCount = 0;
	if (maxZoom != null)
	{
		this.maxZoom = maxZoom;
	}
	else
	{
		this.maxZoom = 999;	// always try to find tiles at server.
	}
	if (zoomOffset != null)
	{
		this.zoomOffset = zoomOffset;
	}
	else
	{
		this.zoomOffset = 0;	// use 256x256-pixel tile coverage
	}

	this.UpdateMap = function UpdateMap()
	{
		var zoom = Math.min(this.maxZoom,
			Math.max(this.vemap.GetZoomLevel()+this.zoomOffset, 1));
		var mapView = this.vemap.GetMapView();
		var tl_tile = MercatorToTile(this.mercatorProjection.LLToMercator(mapView.TopLeftLatLong), zoom);
		var br_tile = MercatorToTile(this.mercatorProjection.LLToMercator(mapView.BottomRightLatLong), zoom);


		var oldTiles = this.loadedTiles;
		this.loadedTiles = new Array();
		for (var tiley = tl_tile.y; tiley<=br_tile.y; tiley++)
		{
			for (var tilex = tl_tile.x; tilex<=br_tile.x; tilex++)
			{
				var tileAddress = TileAddress(tilex, tiley, zoom);
				var tileULLL = this.mercatorProjection.MercatorToLL(TileULToMercator(tilex, tiley, zoom));
				var tileBRLL = this.mercatorProjection.MercatorToLL(TileULToMercator(tilex+1.0, tiley+1.0, zoom));
				var tileRectangle = new VELatLongRectangle(tileULLL, tileBRLL);
				//dbg("tile merc = "+tileULMercator+"; ll = "+tileULLL);
				if (oldTiles[tileAddress]==null)
				{
					// new tile
					//dbg("load "+tileAddress);
					tile = new Tile(tileAddress, tileRectangle, this);
					this.loadedTiles[tileAddress] = tile;
				}
				else
				{
					// Tile already loaded
					this.loadedTiles[tileAddress] = oldTiles[tileAddress];
					delete oldTiles[tileAddress];
				}
			}
		}
		for (var tileAddress in oldTiles)
		{
			oldTiles[tileAddress].Dispose();
			//dbg("unload "+tileAddress);
		}
	}
	
	this.ChangeSegmentCount = function ChangeSegmentCount(crement)
	{
		this.segmentCount += crement;
		var div = document.getElementById("segmentCountSpan");
		if (div != null)
		{
			div.innerHTML = ""+this.segmentCount;
		}
	}

	liveLayers[this.basename] = this;
	return this;
}

