function VEManager()
{
	// CONSTRUCTOR
}

Function.prototype.bind = function() {
  var __method = this, argsi = arguments || [];
  var args = [];
  var object = argsi[0];
  for(var i = 1; i < argsi.length; ++i){
  	args.push(argsi[i]);
  }
  return function() {
    return __method.apply(object, args);
  }
}

VEManager.prototype = {
	/** CONSTANTS **/
	MAP_REFRESH: "MAP_REFRESH",
	MAP_UPDATE: "MAP_UPDATE",
	MAP_ROUTE_COMPLETE: "MAP_ROUTE_COMPLETE",
	
	ZOOM_MAP: "ZOOM_MAP",
	SHOW_ROUTE: "SHOW_ROUTE",
	SHOW_DEALERS: "SHOW_DEALERS",
	CHANGE_MAP_TYPE: "CHANGE_MAP_TYPE",
	
	/** PROPERTIES **/
	activeEvent: null,	
	currentZoomLevel: 0,
	eventCount: 0,
	requiresZoom: false,
	requiresRefresh: false,
	checkForZoom: false,
	tileCount: 0,
	thresholdCount: 0,
	lastTileCount: 0,	
	
	swfInstance: null,
	refreshRequired: false,
	map: null,
	mapHolder: null,
	mapNode: null,
	loadInterval: null,
	diffTreeInterval: null,
	currentTree: null,
	route: null,
	isIE: false,
	isSafari: false,
	isFirefox: false,
	ignoreEvent: false,
	ignoreZoom: false,
	currentTimeout: null,
	refreshQueue: [],
	time: null,
	
	/** CONSTRUCTOR **/
	init: function(swfRef)
	{
		try
		{	
			window.document.veinstance = this;
			//this.swfInstance =  swfRef;
			// this.swfInstance =  window.parent.document[swfRef];
			this.mapHolder = window.document.getElementById('mapsource');
			this.time = new Date();
			
			if(document.all)
			{ 
				this.isIE = true; 
			} else { 
				this.isFirefox = true; 
			}
			
			// Firefox 2.0 fix
			Msn.Drawing.Graphic.CreateGraphic = function(f,b) 
			{
				if (document.all)
				{
					return new Msn.Drawing.VMLGraphic(f,b);
				} else {
	            	var d = new RegExp("Firefox/(.*)").exec(navigator.userAgent);
	            	if(d[1] && parseFloat(d[1]) >= 1.5) return new Msn.Drawing.SVGGraphic(f,b); 
	            	throw new Msn.Drawing.Exception(L_GraphicsInitError_Text);
				}
	        }
			
			this.map = new VEMap('mapsource');
			VEMAPTOKEN = Address.getParameters()["vetoken"];
			this.map.SetClientToken(VEMAPTOKEN);
			try
			{
				this.map.LoadMap();
			} catch (vmlError) {
				if(vmlError.message == "Your Web browser does not support SVG or VML. Some graphics features may not function properly.")
				{
					// HACK TO SUPPORT FIREFOX 2 -- Not FF2 fault, error being thrown via VE.
				}
			}
			
			this.map.DisambiguationCallback = this.unknownLocation;
			/* Following line commented out for Safari 3 functionality */
			//this.map.ShowDisambiguationDialog(false);
			
			// register any map events
			this.map.AttachEvent("onchangeview", this.handleEvent);
			this.map.AttachEvent("onendzoom", this.handleEvent);
			this.map.AttachEvent("onendpan", this.timeOutUpdateVEMap);
			
			//handle token events
			this.map.AttachEvent("ontokenexpire", this.handleTokenExpire);
			this.map.AttachEvent("ontokenerror", this.handleTokenError);
	
			// check to see when the map has actually loaded
			this.loadInterval = setInterval(this.mapLoadEvent, 500);
			
		} catch (err){
			this.error("init() ==> " + err.message);
		}
	},
	
	/** METHODS **/
	
	/* called to verify if the map has been loaded */
	mapLoadEvent: function()
	{
		// get all the tiles
		var t = window.document.veinstance;
		trace('VEManager: mapLoadEvent');
		try
		{
			trace("VEManager: calledMapLoadEvent");
			t.mapNode = t.findMap();
			if(!t.mapNode) return;
			var imgList = t.findMapImages();
			if(imgList.length > 0)
			{
				// clear the interval
				clearInterval(t.loadInterval);
				trace("VEManager: found images.");
				window.parent.callSwf("initMapComplete", imgList);
				// this.diffTreeInterval = setInterval(this.diffTree.bind(this), 10);
			}
		} catch (err) {
			t.error("mapLoadEvent() ==> " + err.message);
		}
	},
	
	/* called by all event types outside of pan and start */
	handleEvent: function(event)
	{
		var t = window.document.veinstance;
		trace("VEManager: Active Event: "+ t.activeEvent);
		if(t.activeEvent)
		{
			// we have an active event
			t.eventCount++;
			if(t.checkForZoom)
			{
				t.checkForZoom = false;
				if(t.currentZoomLevel != event.zoomLevel) t.requiresZoom = true;
			}
			switch(t.activeEvent)
			{
				case t.ZOOM_MAP:
					if(t.eventCount >= 2) t.eventComplete();
					break;
					
				case t.SHOW_DEALERS:
					if(t.eventCount >= 4) t.eventComplete();
					break;
					
				case t.SHOW_ROUTE:
					if(t.requiresZoom)
					{
						if(t.eventCount >= 2) t.eventComplete();
					} else {
						t.eventComplete();
					}
					break;
				
				case t.CHANGE_MAP_TYPE:
					t.eventComplete();
					break;
			}
		}
	},
	
	handleTokenExpire: function()
	{
		//reset token
	},
	
	handleTokenError: function()
	{
		//?
	},
	
	eventComplete: function()
	{
		var t = window.document.veinstance;
		t.activeEvent = null;
		t.eventCount = 0;
		t.requiresZoom = false;
		t.requiresRefresh = true;
		t.thresholdCount = 0;
		t.lastTileCount = 0;
		setTimeout(t.updateVEMap, 100);
	},
	timeOutUpdateVEMap: function()
	{
		var t = window.document.veinstance;
		setTimeout(t.updateVEMap, 300);
	},
	
	updateVEMap: function()
	{
		var t = window.document.veinstance;
		try
		{
			var imageList = t.findMapImages();
			// wait for tiles
			var requiredTiles = (t.getZoomLevel() < 4) ? 6 : 6;
			if(t.tileCount < requiredTiles)
			{
				setTimeout(t.updateVEMap, 100);
				t.lastTileCount = t.tileCount;
				if(t.zoomFlag)
				{
					t.zoomFlag = false;
					if(t.tileCount < 4) return;
				} 
				if(t.tileCount < 5 && t.requiresRefresh == false) 
				{
					return;
				}
			} else {
				// check for threshold
				t.thresholdCount++;
				if(t.thresholdCount < 3 && requiredTiles == 6 && t.tileCount < 30)
				{
					setTimeout(t.updateVEMap, 1000);
					if(t.lastTileCount != t.tileCount)
					{
						t.lastTileCount = t.tileCount;
					} else {
						// do not update the map
						return;
					}
				}
			}
			
			if( imageList.length < 4 || t.tileCount < 2 )
			{
				// not enough images populated yet, even though the tile data is there
				// set a longer return time, because it seems that we're just waiting on the files to load
				setTimeout(t.updateVEMap, 1000);
				return;
			}
			// or the image data isn't complete yet
			if(imageList[0].properties == null) 
			{	
				// set a longer return time, because it seems that we're just waiting on the files to load
				setTimeout(t.updateVEMap, 1000);
				return;
			}
			
			// call the SWF
			window.parent.callSwf("mapChange", imageList, t.requiresRefresh);
			t.requiresRefresh = false;
			
		} catch (err) {
			t.error("updateVEMap() ERROR ==> " + err.message);
		}		
	},
	
	/* finds the map <div> instance */
	findMap: function()
	{
		var nodes = window.document.veinstance.mapHolder.getElementsByTagName("div");
		for(var i = 0; i < nodes.length; i++)
		{
			if(nodes[i].className == 'MSVE_Map')
			{
				return nodes[i];
			}
		}
		return false;
	},
	
	findLocation: function(loc)
	{
		var t = window.document.veinstance;
		try
		{			
			t.eventType = "FIND_LOCATION";
			t.ignoreEvent = true;
			t.map.DeleteRoute();
			t.map.Find ('',loc,null,null,0,1,true,true,false,true,t.foundLocation);
		}  catch (err) {
			t.error("findLocation() ==> " + err.message);
		}
		
	},
	
	foundLocation: function(a,b,foundLoc)
	{
		if(foundLoc == null || foundLoc == undefined)
		{
			window.parent.callSwf("veReturnError", null, null);
		} else {
			window.parent.callSwf("foundLocation", foundLoc);
		}
	},
	
	hasVector: function()
	{
		var t = window.document.veinstance;
		var hasVector = false;
		try
		{
			if(document.getElementById("veDDHighlight")) hasVector = true;
		} catch (err) {
			t.error("hasVector() ==> " + err.message);
		} finally {
			return hasVector;
		}
	},
	
	/* gets all the images from the current Map <div> instance */
	findMapImages: function()
	{
		// build a list of the images
		var t = window.document.veinstance;
		t.tileCount = 0;
		//window.parent.imageList = new Array();
		var imagePath = [];		
		try
		{		
			var imgList = t.mapNode.getElementsByTagName("img");
			
			// find all anchors
			var pushpinList = t.findAnchor();
			var haspush = (pushpinList.length > 0)  ? true : false;
			
			for(var i=0; i < imgList.length; i++)
			{
				var img = new Object();
				img.source = imgList[i].getAttribute('src');
				if(haspush)
				{
					// There are 'pushpins' (route markers) in the return data, so they need to be sorted out from the tile images
					if( imgList[i].className.indexOf('Tile') != -1 ) 
					{
						// image is a tile image
						img.type = "tile";
						img.properties = window.document.veinstance.convertProperties(imgList[i].getAttribute('style'));
						t.tileCount++;
					} else {
						// image is a route marker image.  
						img.type = "route";
						img.properties = pushpinList[i].style;
						// this filters out the start and end markers, but we're doing that in the AS instead, now
						//var pushpinIndex = i+1;
						//if( pushpinList[pushpinIndex] )
						//{
						//	img.properties = pushpinList[pushpinIndex].style;
						//} else {
						//	continue;
						//}
						
					}
				} else {
					img.type = "tile";
					t.tileCount++;
					img.properties = window.document.veinstance.convertProperties(imgList[i].getAttribute('style'));
				}
				
				imagePath.push(img);
			}
		} catch (err) {
			t.error("findMapImages() ==> " + err.message);
		} finally {
			return imagePath;
		}		
	},
	
	findAnchor: function ()
	{
		var t = window.document.veinstance;
		var dataList = new Array();
		try {
			var anchorList = document.getElementsByTagName('a');
			for(var i=0; i < anchorList.length; i++ )
			{
				if(anchorList[i].className == 'VEAPI_Pushpin')
				{
					// set the property
					var prop = new Object();
					prop.id = anchorList[i].getAttribute('id');
					prop.style = t.convertProperties(anchorList[i].getAttribute('style'));
					dataList.push(prop);
					dataList[prop.id] = prop;
				}
			}
		} catch (err) {
			t.error("findAnchor() ==> " + err.message);
		} finally {
			return dataList;
		}
	},
	
	convertProperties: function(props)
	{
		var proplist = "";
		if(this.isIE)
		{
			for(var i in props)
			{
				if(props[i])
				{
					proplist += i + ":" + props[i] + ";";
				}
			}
			proplist = proplist.toLowerCase();
		} else {
			return props;
		}
		return proplist;
	},
	
	unknownLocation: function(event)
	{
		var t = window.document.veinstance;
		t.eventType = "UNKNOWN_LOCATION";
		trace("VEManager: called unknown: " + event);
		window.parent.callSwf("unknownLocation", event);
	},
	
	routeComplete: function(route)
	{
		var t = document.veinstance;
		t.eventType = "ROUTE_COMPLETE";	
		if(route == null || route == undefined)
		{
			trace("VEManager: veReturnError");
			window.parent.callSwf("veReturnError", null, null);
		} else {
			trace("VEManager: routeComplete(" + route + ")");
			window.parent.callSwf("routeComplete", route);
		}
	},
	
	getSvgPath: function()
	{
		try
		{
			if(document.veinstance.isIE)
			{
				if(document.getElementById('veDDHighlight') != null)
				{
					var myDatapoints = document.getElementById('veDDHighlight').path + " ";
					return myDatapoints;
				}
			} else {
				return document.getElementById('veDDHighlight').getAttribute('points');
			}
			return null;
		} catch (err) {
			t.error("getSvgPath() ==> " + err.message);
		}
		
	},
	
	panMap: function(deltaX, deltaY)
	{
		var t = document.veinstance;
		t.eventType = "PAN_MAP";
		try {
			t.ignoreEvent = true;		
			t.map.Pan(deltaX, deltaY);
		} catch (err) {
			t.error("panMap() ==> " + err.message);
		}		
	},
	
	zoomMapAndCenter: function(x, y, direction, zoomLevel)
	{
		var t = document.veinstance;
		t.activeEvent = t.ZOOM_MAP;
		try
		{			
            var pixel = new VEPixel(x, y);
			var latlong = this.map.PixelToLatLong(pixel);
			t.ignoreEvent = false;
			if(zoomLevel > -1)
			{
				t.map.SetCenterAndZoom(latlong, zoomLevel);
			} else {
				var currentZoom = t.map.GetZoomLevel();
				var amount = (direction == "in") ? ++currentZoom : --currentZoom;
				t.map.SetCenterAndZoom(latlong, amount);
			}			
		} catch (err) {
			t.error("zoomMapAndCenter() ==> " + err.message);
		}
		
	},
	
	zoom: function(zoomLevel)
	{
		var t = document.veinstance;
		t.activeEvent = t.ZOOM_MAP;
		try
		{
			t.ignoreEvent = false;
			t.zoomFlag = true;
			if(zoomLevel > -1)
			{
				t.map.SetZoomLevel(zoomLevel);
			} else {
				var currentZoom = t.map.GetZoomLevel();
				var amount = (direction == "in") ? ++currentZoom : --currentZoom;
				t.map.SetZoomLevel(amount);
			}			
		} catch (err) {
			t.error("zoom() ==> " + err.message);
		}
		
	},
	
	getZoomLevel: function()
	{
		return document.veinstance.map.GetZoomLevel();
	},
	
	getMapHolderPosition: function()
	{
		var temp = document.veinstance.mapNode.getAttribute('style');
		if(typeof temp == "object")
		{
			var stringTemp = "";
			if(temp.zIndex) {
				stringTemp += ("z-index:" +temp.zIndex + ";");
			}
			if(temp.left) {
				stringTemp += ("left:" +temp.left + ";");
			}
			if(temp.top) {
				stringTemp += ("top:" +temp.top + ";");
			}
			return stringTemp;
		} else {
			return temp;
		}
	},
	
	/* Displays the map in the best format for a list of Lat/Long pairs.  The array should be a
	 * a simple multi-dimensional array that has the latitude in the [0] position and the longitude
	 * at the [1] position. The method builds the VELatLong from the multi-dimensional array.
	 */
	getBestMap: function(arrayOfPoints)
	{
		var t = document.veinstance;
		t.activeEvent = t.SHOW_DEALERS;
		try
		{	
			t.map.SetCenterAndZoom(new VELatLong(82.7, 114.4), 1);
			var pointList = new Array();
			for(var i = 0; i < arrayOfPoints.length; i++)
			{
				pointList.push(new VELatLong(arrayOfPoints[i].latitude, arrayOfPoints[i].longitude));
			}
			t.map.DeleteRoute();		
			t.map.SetMapView(pointList);
		} catch (err) {
			t.error("getBestMap() ==> " + err.message);
		}
	},
	
	displayRoute: function(startLocation, endLocation)
	{
		var t = document.veinstance;
		t.activeEvent = t.SHOW_ROUTE;
		t.currentZoomLevel = t.getZoomLevel();
		t.checkForZoom = true;
		t.map.DeleteRoute();
		var start = (startLocation.latitude) ? new VELatLong(startLocation.latitude, startLocation.longitude) : startLocation;
		var end = (endLocation.latitude) ? new VELatLong(endLocation.latitude, endLocation.longitude) : endLocation;
		t.map.GetRoute(start, end, VEDistanceUnit.Miles, VERouteType.Quickest, t.routeComplete);
	},
	
	setMapStyle: function(style)
	{
		var t = document.veinstance;
		t.activeEvent = t.CHANGE_MAP_TYPE;
		try{
			switch(style)
			{
				case "Road":
					t.map.SetMapStyle(VEMapStyle.Road);
					break;
				case "Hybrid":
					t.map.SetMapStyle(VEMapStyle.Hybrid);
					break;
				case "Aerial":
					t.map.SetMapStyle(VEMapStyle.Aerial);
					break;
			}
			//t.mapUpdateRequired(t.MAP_ROUTE_COMPLETE);
		} catch (err) {
			t.error("setMapStyle() ==> " + err.message);
		}
	},
	
	getPixelForLatLong: function(latitude, longitude)
	{
		return document.veinstance.map.LatLongToPixel(new VELatLong(latitude, longitude));
	},
	
	getIsIE: function()
	{
		return document.veinstance.isIE;
	},
	
	/* error messaging */
	error: function(str)
	{
		if(window.parent.console) window.parent.console.error(str);
		window.parent.callSwf("trace", str);
		//document.veinstance.swfInstance.browserTrace(str, true);
	}
}