//
// Map Proxy - use a single interface to access map api's from multiple vendors.
// Requires: desired mapping api script links, imUtils.js
// Author: Ben Siroshton
// Date: 09/03/2009
// Maps Supported: Map Quest, Bing (Virtual Earth)
//

var IMMapType = {};
{
	IMMapType.MAPQUEST = 0;
	IMMapType.BING = 1;
}

var IMMapStyle = {};
{
	IMMapStyle.ROAD = 0;
	IMMapStyle.AERIAL = 1;
	IMMapStyle.HYBRID = 2;
}


// We have to use this for Bing maps, unlike MQ I can not get a handle back to the object I need from events.
var global_map_instance = null;

// notes:
// longitude are vertical lines from -180 (westward) to +180 (eastward)
// latitude are horizontal lines from -90 (south pole) to +90 (north pole) 0 is at the equator
/**
* @class
* @constructor
*/
function imLatLong(latitude, longitude){

	this.latitude = latitude;
	this.longitude = longitude;

	this.isSouthOf = function(lngLat){
		return this.latitude<lngLat.latitude;
	};

	this.isNorthOf = function(lngLat){
		return this.latitude>lngLat.latitude;
	};

	this.isEastOf = function(lngLat){
		return this.longitude>lngLat.longitude;
	};

	this.isWestOf = function(lngLat){
		return this.longitude<lngLat.longitude;
	};

	this.toString = function(){
		return "latitude: " + this.latitude + " longitude: " + this.longitude;
	};
}


/**
* @class
* @constructor
*/
function imLatLongRect(topLeft,bottomRight){

	this.topLeft = topLeft;
	this.bottomRight = bottomRight;

	this.getIntersection = function(area){
		if( area.topLeft.isSouthOf(this.bottomRight) ) return null;
		if( this.bottomRight.isNorthOf(area.topLeft) ) return null;
		if( area.topLeft.isEastOf(this.bottomRight) ) return null;
		if( this.bottomRight.isWestOf(this.topLeft) ) return null;

		return new imLatLongRect(new imLatLong(Math.min(this.topLeft.latitude,area.topLeft.latitude),
												Math.max(this.topLeft.longitude,area.topLeft.longitude)),
									new imLatLong(Math.max(this.bottomRight.latitude,area.bottomRight.latitude),
											Math.min(this.bottomRight.longitude,area.bottomRight.longitude)) );
	};

	this.expand = function(lngLat){
		this.topLeft = new imLatLong(Math.min(this.topLeft.latitude,lngLat.latitude), Math.max(this.topLeft.longitude,lngLat.longitude));
		this.bottomRight = new imLatLong(Math.max(this.bottomRight.latitude,lngLat.latitude), Math.min(this.bottomRight.longitude,lngLat.longitude));
	}

	this.contains = function(lngLat){
		if( lngLat.isSouthOf(this.bottomRight) ) return false;
		if( lngLat.isNorthOf(this.topLeft) ) return false;
		if( lngLat.isEastOf(this.bottomRight) ) return false;
		if( lngLat.isWestOf(this.topLeft) ) return false;
		return true;
	};

	this.toString = function(){
		return this.topLeft.toString() + ", " + this.bottomRight.toString();
	};
}


/**
* @class
* @constructor
*/
function imPointArray(){

	this.list = new Array();

	this.add = function(point){
		this.list[this.list.length] = point;
	};

}


/**
* @class
* @constructor
*/
function imMapIcon(latLng, imgUrl, imgWidth, imgHeight){

	this.latLng = latLng;
	this.imgUrl = imgUrl;
	this.imgWidth = imgWidth;
	this.imgHeight = imgHeight;
	this.label = null;
	this.apiId = null;
	this.map = null;

	// Functions
	this.setLocation = function(latLng){

		this.latLng = latLng;

		if( this.map==null ){
			return;
		}

		if( this.apiId==IMMapType.MAPQUEST ){

			var Map = this.map;
			Map.removeIcon(this);
			Map.addIcon(this);

		}else if( this.apiId==IMMapType.BING ){

			this._shape.SetPoints(new VELatLong(latLng.latitude,latLng.longitude));

		}

	};

	this.setLabel = function(text){

		this.label = text;

		if( this.map==null || this._shape==null){
			return;
		}

		if( this.apiId==IMMapType.MAPQUEST ){

			this._shape.setValue('labelClass', 'imMapLabelClass');
			this._shape.setValue('labelText', text==null?'':text);
			this._shape.setValue('labelVisible', text!=null);

		}else if( this.apiId==IMMapType.BING ){

			var H = this._imgHtml;
			if( text!=null ){
				H += "<div class='imMapLabelClass' style='position: absolute; left: " + (this.imgWidth/2) + "px; top: " + (-this.imgHeight/2) + "px;'>" + text + "</div>";
			}
			this._shape.SetCustomIcon(H);

		}

	};

	this.moveToTop = function(){
		if( this.map==null || this._shape==null ){
			return;
		}
		var Map = this.map;
		Map.removeIcon(this);
		Map.addIcon(this);
	};

	// User Events
	this.onClick = function(){
	};

}


/**
* @class
* @constructor
*/
function imMap(htmlElement, apiId){

	this.id = (new Date()).valueOf();
	this.apiId = apiId;
	this.map = null;
	this.htmlElement = htmlElement;
	this.shapeLayer = null;

	if( global_map_instance==null ){
		global_map_instance = this;
	}

	if(apiId==IMMapType.MAPQUEST){

		var Zoom = null;

		// Create MQ Map
		this.map = new MQA.TileMap(htmlElement,Zoom,null,this.id);

		this.map.addControl(new MQA.LargeZoomControl);
		this.map.addControl(new MQA.ViewControl);

		// Functions
		this.setStyle = function(mode){
			if( mode==IMMapStyle.ROAD ){
				this.map.setMapType("map");
			}else if( mode==IMMapStyle.AERIAL ){
				this.map.setMapType("sat");
			}else if( mode==IMMapStyle.HYBRID ){
				this.map.setMapType("hyb");
			}
		}	

		this.addImmersiveLayer = function(){
			this.shapeLayer = new MQA.TileLayer(this.map,"immersive media",MQCoverageTileLayer,null,null,1,16,0.75,60,null);
			this.map.addTileLayer(this.shapeLayer);
		};

		this.resize = function(){
			if( imUtils.isNumeric(this.htmlElement.style.width) && imUtils.isNumeric(this.htmlElement.style.height) ){
				this.map.setSize(new MQA.Size(this.htmlElement.style.width,this.htmlElement.style.height));
			}
		};

		this.panToLatLng = function(latitude, longitude){
			this.map.panToLatLng(new MQA.LatLng(latitude, longitude));
		};

		this.getBounds = function(){
			var R = this.map.getBounds();
			return new imLatLongRect(
					new imLatLong(R.getUpperLeft().getLatitude(),R.getUpperLeft().getLongitude()),
					new imLatLong(R.getLowerRight().getLatitude(),R.getLowerRight().getLongitude()));
		};

		this.getZoomLevel = function(){
			// MQ: Valid values range from 1 through 16
			var Z = this.map.getZoomLevel();
			return Z/16.0 * 100.0;
		};

		this.setZoomLevel = function(zoom){
			// MQ: Valid values range from 1 through 16
			this.map.setZoomLevel(Math.round(imUtils.clamp(zoom/100.0 * 16.0,1,16)));
		};

		this.getZoomStep = function(){
			return 16.0/100.0;
		};

		this.addIcon = function(icon){

			icon.apiId = this.apiId;
			icon.map = this;

			if( icon.imgUrl!=null ){
				icon._shape = new MQA.Poi(new MQA.LatLng(icon.latLng.latitude,icon.latLng.longitude),new MQA.Icon(icon.imgUrl,icon.imgWidth,icon.imgHeight));
			}else{
				icon._shape = new MQA.Poi(new MQA.LatLng(icon.latLng.latitude,icon.latLng.longitude));
			}

			this.map.addShape(icon._shape);
			MQA.EventManager.addListener(icon._shape,"click",icon.onClick,icon);

			icon._shape.setValue('shadow',null);
			icon.setLabel(icon.label);

		};

		this.removeIcon = function(icon){
			if( icon._shape!=null ){
				MQA.EventManager.removeListener(icon._shape,"click",icon.onClick,icon);
				this.map.removeShape(icon._shape);
				icon._shape = null;
				icon.map = null;
			}
		};

		this.addLine = function(pointArray){

			if( pointArray==null ){
				return;
			}

			var Points = new Array();

			var Line = new MQA.LineOverlay();
			var LinePoints = new MQA.LatLngCollection(); 

			for (var Key in pointArray.list){
				var point = pointArray.list[Key];
				LinePoints.add(new MQA.LatLng(point.latitude, point.longitude));
			}

			Line.setValue("color","#ffff00");
			Line.setValue("borderWidth",2);
			Line.setValue("shapePoints",LinePoints);	

			this.map.addShape(Line);		

		};
		
		// Setup Event Proxies
		/**
		* @ignore
		**/
		this.proxy_onMapClick = function(e){
			if( imUtils.isDefined(e.srcObject) || e.srcObject!=null ){
				return;
			}

			this.onClick(e.ll.getLatitude(), e.ll.getLongitude());
		};

		/**
		* @ignore
		**/
		this.proxy_onMapZoomStart = function(e){
			this.onZoomStart();
		};

		/**
		* @ignore
		**/
		this.proxy_onMapZoomEnd = function(e){
			this.onZoomEnd();
			this.onViewChanged();
		};

		/**
		* @ignore
		**/
		this.proxy_onMapMoveStart = function(e){
			this.onPanStart();
		};

		/**
		* @ignore
		**/
		this.proxy_onMapMoveEnd = function(e){
			this.onPanEnd();
			this.onViewChanged();
		};

		// Register Events
		MQA.EventManager.addListener(this.map,"click",this.proxy_onMapClick,this);
		MQA.EventManager.addListener(this.map,"zoomStart",this.proxy_onMapZoomStart,this);
		MQA.EventManager.addListener(this.map,"zoomend",this.proxy_onMapZoomEnd,this);
		MQA.EventManager.addListener(this.map,"movestart",this.proxy_onMapMoveStart,this);
		MQA.EventManager.addListener(this.map,"moveend",this.proxy_onMapMoveEnd,this);

	}else if( apiId==IMMapType.BING ){

		// Create some member variables specific to this api
		this._mouseDownPos = null;
		this._mouseDownShape = null;

		// Create Bing Map
		this.map = new VEMap(htmlElement.id);
		this.map.LoadMap();

		// Functions
		this.setStyle = function(mode){
			if( mode==IMMapStyle.ROAD ){
				this.map.SetMapStyle( VEMapStyle.Road );	
			}else if( mode==IMMapStyle.AERIAL ){
				this.map.SetMapStyle( VEMapStyle.Aerial );
			}else if( mode==IMMapStyle.HYBRID ){
				this.map.SetMapStyle( VEMapStyle.Hybrid );
			}
		}		

		this.addImmersiveLayer = function(){
			addImmersiveBingLayer(this.map);
		};

		this.resize = function(){
		};

		this.panToLatLng = function(latitude, longitude){
			this.map.PanToLatLong(new VELatLong(latitude, longitude));
		};

		this.getBounds = function(){
			var V = this.map.GetMapView();
            var TL = V.TopLeftLatLong;
            var BR  = V.BottomRightLatLong;
            //var TR = V.TopRightLatLong; // 3d mode only
            // var BL  = V.BottomLeftLatLong; // 3d mode only

			return new imLatLongRect(
					new imLatLong(TL.Latitude,TL.Longitude),
					new imLatLong(BR.Latitude,BR.Longitude));
		};

		this.getZoomLevel = function(){
			var Z = this.map.GetZoomLevel();
			// Bing: Valid values range from 1 through 19
			return Z/19.0 * 100.0;
		};

		this.setZoomLevel = function(zoom){
			// Bing: Valid values range from 1 through 19
			this.map.SetZoomLevel(Math.round(imUtils.clamp(zoom/100.0 * 19.0,1,19)));
		};

		this.getZoomStep = function(){
			return 19.0/100.0;
		};

		this.addIcon = function(icon){

			icon.apiId = this.apiId;
			icon.map = this;

			icon._shape = new VEShape(VEShapeType.Pushpin,
										new VELatLong(icon.latLng.latitude,icon.latLng.longitude)
										);
			icon._shape._im_Icon = icon;
			if( icon.imgUrl!=null ){
				// Bing needed this offset of 14 to make things line up properly.
				var OffsetX = 14 - Math.max(1,Math.round(icon.imgWidth/2));
				var OffsetY = 14 - Math.max(1,Math.round(icon.imgHeight/2));

				icon._imgHtml = "<img src='" + icon.imgUrl + "' style='position: relative; left: " + OffsetX + "px; top: " + OffsetY +"px; width: " + icon.imgWidth + "px; height: " + icon.imgHeight + "px;'/>";
				icon._shape.SetCustomIcon(icon._imgHtml);
			}

			this.map.AddShape(icon._shape);
			icon.setLabel(icon.label);
		};

		this.removeIcon = function(icon){
			if( icon._shape!=null ){
				this.map.DeleteShape(icon._shape);
				icon._shape = null;
				icon.map = null;
			}
		};

		this.addLine = function(pointArray){

			if( pointArray==null ){
				return;
			}

			var Points = new Array();

			for (var Key in pointArray.list){
				var point = pointArray.list[Key];
				var P = new VELatLong(point.latitude, point.longitude, 0, VEAltitudeMode.RelativeToGround);
				Points[Points.length] = P;
			}

			var Id = (new Date()).valueOf();
			var Line = new VEPolyline(Id, Points, new VEColor(255,255,0,1), 2);
			this.map.AddPolyline(Line);

		};

		// Setup Event Proxies
		/**
		* @ignore
		**/
		this.proxy_onMapClick = function(e){
		};

		/**
		* @ignore
		**/
		this.proxy_onMapMouseDown = function(e){

			if( e.elementID!=null ){
				var Shape = global_map_instance.map.GetShapeByID(e.elementID);
				if( Shape!=null ){
					global_map_instance._mouseDownShape = Shape;
					return true;
				}
			}

			global_map_instance._mouseDownPos = new VEPixel(e.mapX, e.mapY);

		};

		/**
		* @ignore
		**/
		this.proxy_onMapMouseUp = function(e){


			if( e.elementID!=null ){
				var Shape = global_map_instance.map.GetShapeByID(e.elementID);
				if( Shape!=null && Shape==global_map_instance._mouseDownShape ){
					global_map_instance._mouseDownShape._im_Icon.onClick();
					global_map_instance._mouseDownShape = null;
					return true;
				}
			}

			if( global_map_instance._mouseDownPos!=null && global_map_instance._mouseDownPos.x==e.mapX && global_map_instance._mouseDownPos.y==e.mapY ){
				var LL = global_map_instance.map.PixelToLatLong(new VEPixel(e.mapX, e.mapY));
				global_map_instance.onClick(LL.Latitude, LL.Longitude);
			}

			global_map_instance._mouseDownPos = null;
			global_map_instance._mouseDownShape = null;

		};

		/**
		* @ignore
		**/
		this.proxy_onMapZoomStart = function(e){
			global_map_instance.onZoomStart();
		};

		/**
		* @ignore
		**/
		this.proxy_onMapZoomEnd = function(e){
			global_map_instance.onZoomEnd();
			global_map_instance.onViewChanged();
		};

		/**
		* @ignore
		**/
		this.proxy_onPanStart = function(e){
			global_map_instance.onPanStart();
		};

		/**
		* @ignore
		**/
		this.proxy_onPanEnd = function(e){
			global_map_instance.onPanEnd();
			global_map_instance.onViewChanged();
		};

		// Register Events
		//this.map.AttachEvent("onclick", this.proxy_onMapClick);
		this.map.AttachEvent("onmousedown", this.proxy_onMapMouseDown);
		this.map.AttachEvent("onmouseup", this.proxy_onMapMouseUp);
		this.map.AttachEvent("onstartzoom", this.proxy_onMapZoomStart);
		this.map.AttachEvent("onendzoom", this.proxy_onMapZoomEnd);
		this.map.AttachEvent("onstartpan", this.proxy_onPanStart);
		this.map.AttachEvent("onendpan", this.proxy_onPanEnd);

	}

	// Functions not specific to a given API
	this.ensureVisibility = function(latitude, longitude){
		var B = this.getBounds();
		if( latitude>B.topLeft.latitude || latitude<B.bottomRight.latitude ||
			longitude<B.topLeft.longitude || longitude>B.bottomRight.longitude ){
				this.panToLatLng(latitude,longitude);
		}
	};

	// Events for user override
	this.onClick = function(latitude, longitude){
	};

	this.onZoomStart = function(){
	};

	this.onZoomEnd = function(){
	};

	this.onPanStart = function(){
	};

	this.onPanEnd = function(){
	};

	this.onViewChanged = function(){
	};

}

/**
* @ignore
**/
MQCoverageTileLayer = function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a){

	var latFactor  = 6378137.0*2.0*3.14159265/360.0;
	var longFactor = 0.7933533402912351645797769615013*latFactor;

	MQA.ImageTile.call(this,_1,_2,_3,_4,_5,_6,_7,_8,_9,_a);
	this.options=_a;

	this.url="http://condor.immersivemedia.com/cgi-bin/imc_ms_db?&REQUEST=GetMap&SERVICE=WMS&VERSION=1.1.1&LAYERS=IMC_Routes&FORMAT=image/png&TRANSPARENT=TRUE&SRS=EPSG:54005&BBOX=%1,%2,%3,%4&WIDTH=256&HEIGHT=256&reaspect=false&STYLES=";

	this.getUrl=function(){
		var myScale=this.scale;
		var myCenterLL=this.parent.getTileCenterLatLng(this.x,this.y,this.size);
		//calculate a tile's width worth of degrees
		var longOffset = 128*myScale/MQA.PIXERSPERLNGDEGREE;
		var latOffset  = 128*myScale/MQA.PIXERSPERLATDEGREE;

		var myLowerLeft = new Object();
		var myUpperRight = new Object();
		myLowerLeft.x  = (myCenterLL.lng - longOffset)*longFactor;
		myUpperRight.x = (myCenterLL.lng + longOffset)*longFactor;
		myLowerLeft.y  = (myCenterLL.lat - latOffset)*latFactor;
		myUpperRight.y = (myCenterLL.lat + latOffset)*latFactor;

		var _16=this.url.replace(/%1/,myLowerLeft.x).replace(/%2/,myLowerLeft.y).replace(/%3/,myUpperRight.x).replace(/%4/,myUpperRight.y);
		//alert(_16);
		return (_16);
	};
	this.isPng=function(){
		return true;
	};
};


/**
* @ignore
**/
function addImmersiveBingLayer(map){

	var url = "http://condor.immersivemedia.com/cgi-bin/imc_ms_db_ve?";
	    url += "layers=IMC_Routes&";
	    url += "mode=tile&";
	    url += "tilemode=ve&";
	    url += "tile=%4";

	var TSource = new VETileSourceSpecification(
		"immersive media",
		url
	);
	TSource.ZIndex = 100;

	map.AddTileLayer(TSource, true);

}


