/**
 * Microsoft Virtual Earth abstraction
 */
HYSMaps.MicrosoftMap = function (plotter) {
	
	this.markers = {};
	this.activeMarkers = {};
	this.bounds = new HYSMaps.Bounds();
	this.plotter = plotter;
	this.zoomLevel = 0;
};

/**
 * Initialise function must return the class (or false)
 */
HYSMaps.MicrosoftMap.prototype.initialise = function (element, type) {
	
	// error checking
	if (VEMap == undefined) {
		
		//TODO: display error message
		return false;
	}
	
	// instantiate Microsoft Virtual Earth
	this.map = new VEMap(element.id);
	this.map.HYSEvents = {}; // custom events structure
	
	var options = new VEMapOptions(false,false);
	options.EnableBirdseye = false;
	this.map.LoadMap(null,null,null,null,null,null,null, options);
	
	// event control
	this.events = {
		
		onEndZoom: this.onEndZoom.bind(this),
		onEndPan: this.onEndPan.bind(this),
		onMouseMove: this.onMouseMove.bind(this),
		onMouseDown: this.onMouseDown.bind(this),
		onMouseUp: this.onMouseUp.bind(this)
	};
	
	this.map.AttachEvent('onclick', this.events.onMouseUp);
	this.map.AttachEvent('onmousedown', this.events.onMouseDown);
	this.map.AttachEvent('onmousemove', this.events.onMouseMove);
	this.map.AttachEvent('onendpan', this.events.onEndPan);
	
	// set map type
	switch (HYSMaps[type.toLowerCase() + 'Map']) {
		
		default:
		case HYSMaps.type.road:
			type = VEMapStyle.Road;
			break;
		case HYSMaps.type.aerial:
			type = VEMapStyle.Aerial;
			break;
		case HYSMaps.type.hybrid:
			type = VEMapStyle.Hybrid;
			break;
		case HYSMaps.type.terrain:
			type = VEMapStyle.Shaded;
			break;
	}
	this.map.SetMapStyle(type);
	
	// Firefox 3 Bugfix:
	(function()
	{
	    var mouseEvt;
	    if (typeof document.createEvent !== 'undefined')
	    {
	        mouseEvt = document.createEvent('MouseEvents');
	    }
	    if (mouseEvt && mouseEvt.__proto__ && mouseEvt.__proto__.__defineGetter__)
	    {
	        mouseEvt.__proto__.__defineGetter__('pageX', function()
	        {
	            return this.clientX + window.pageXOffset;
	        });
	        mouseEvt.__proto__.__defineGetter__('pageY', function()
	        {
	            return this.clientY + window.pageYOffset;
	        });
	    }
	})();
	
	return this;
};

HYSMaps.MicrosoftMap.prototype.setCentre = function (geo, zoom) {
	
	this.map.AttachEvent('onendzoom', this.events.onEndZoom);
	
	if (geo == undefined && !HYSMaps.isEmpty(this.markers)) {

		// TODO: this is inefficient, but I can't get the map to work
		// with my Bounds object output. To address at a later date
		var points = [];
		for (i in this.markers) {
			
			points.push(this.markers[i].GetPoints()[0]);
		}
		
		this.map.SetMapView(points);
		
		if (this.map.GetZoomLevel() > HYSMaps.zoom.maximum)
			this.map.SetZoomLevel(HYSMaps.zoom.maximum);
		
		return;
	}
	
	if (geo == undefined) {
		geo = new HYSMaps.GeoCoords(HYSMaps.defaults.latitude, HYSMaps.defaults.longitude);
	}
	
	if (zoom == undefined) {			
		zoom = HYSMaps.defaults.zoom;
	}
	else if (isNaN(zoom.toString().charAt(0))) {
		zoom = this.map.GetZoomLevel() + parseInt(zoom);
	}
	
	this.map.SetCenterAndZoom(new VELatLong(geo.latitude, geo.longitude), zoom);
};

HYSMaps.MicrosoftMap.prototype.makeMarker = function (coords, icon, draggable) {
	
	var msCoords = new VELatLong(coords.latitude, coords.longitude);
	var marker = new VEShape(VEShapeType.Pushpin, msCoords);
	
	var mapIcon = new VECustomIconSpecification();
	mapIcon.Image = icon.image;
	mapIcon.ImageOffset = new VEPixel((-icon.iconAnchor.x/2)+4, (-icon.iconAnchor.y/2)-4);
	
	mapIcon.TextContent=" ";
	
	marker.SetCustomIcon(mapIcon);
	marker.HYSEvents = { draggable: draggable }; // custom events structure
	return marker;
};

/**
 * Add marker to map (must return marker)
 */
HYSMaps.MicrosoftMap.prototype.addMarker = function (coords, icon, date, grouped, draggable) {

	if (!this.markers[coords]) {
		var marker = this.makeMarker(coords, icon, draggable);
		marker.HYSEvents.grouped = grouped;
		marker.HYSEvents.date = date;
		marker.Hide();
		
		this.markers[coords] = marker;
		this.activeMarkers[coords] = marker;
		this.bounds.extend(coords);
		this.map.AddShape(marker);
		
		return marker;
	}
	else {
		
		this.activeMarkers[coords] = this.markers[coords];
	}
};

HYSMaps.MicrosoftMap.prototype.clearMarkers = function () {

	for (var i in this.activeMarkers) {

		this.activeMarkers[i].Hide();
	
		if (!(i in this.markers)) {
			this.map.DeleteShape(this.activeMarkers[i]);
			delete this.activeMarkers[i];
		}
	}

	this.activeMarkers = {};
};

HYSMaps.MicrosoftMap.prototype.redraw = function () {

	// get map size
	var view = this.map.GetMapView();
	var size = this.map.LatLongToPixel(view.BottomRightLatLong);
	var fLonSpan = Math.abs(view.TopLeftLatLong.Longitude - view.BottomRightLatLong.Longitude); 
	var fPixelsPerDegree = (size.x / fLonSpan);
	var topLeft = new HYSMaps.GeoCoords(view.TopLeftLatLong.Latitude, view.TopLeftLatLong.Longitude);
	var bottomRight = new HYSMaps.GeoCoords(view.BottomRightLatLong.Latitude, view.BottomRightLatLong.Longitude);
	var bounds = new HYSMaps.Bounds(topLeft, bottomRight);
	
	var markers = this.plotter.plot(this, this.activeMarkers, bounds, fPixelsPerDegree);
	
	for (var i in this.activeMarkers) {
		
		if (i in markers) {
			this.activeMarkers[i].Show();
		} else {
			this.activeMarkers[i].Hide();
		}
	}
		
	for (var i in markers) {
		
		if (!(i in this.activeMarkers) && !markers[i].GetID()) {
			this.map.AddShape(markers[i]);
			this.activeMarkers[i] = markers[i];
		}
	}
};

HYSMaps.MicrosoftMap.prototype.bind = function (target, event, object, method) {

	target = target ? target : this.map;
	target.HYSEvents['on' + event] = function (map, event) { method.call(object, map, event); };
};

HYSMaps.MicrosoftMap.prototype.onMouseMove = function (event) {
	
	this.events.mouseMove = this.events.mouseDown;
			
	if (this.events.dragging) {
		this.events.draggedMarker.SetPoints(this.map.PixelToLatLong(new VEPixel(event.mapX, event.mapY)));
	}
};

HYSMaps.MicrosoftMap.prototype.onMouseUp = function (event) {
	
	this.map.vemapcontrol.EnableGeoCommunity(false);
	
	// convert click x/y into latitude/longitude		
	var msCoord = this.map.PixelToLatLong(new VEPixel(event.mapX, event.mapY));
	var newEvent = { latitude: msCoord.Latitude, longitude: msCoord.Longitude, wasMoving: this.events.mouseMove, movedMarker: this.events.dragging };
	
	this.events.mouseDown = false;
	this.events.mouseMove = false;
	this.events.dragging = false;
	this.events.draggedMarker = marker;
	
	var marker = this.map.GetShapeByID(event.elementID);
	if (marker && marker.HYSEvents.onmouseup) {
		marker.HYSEvents.onmouseup(this, newEvent);
	}
	else if (this.map.HYSEvents.onmouseup) {
		this.map.HYSEvents.onmouseup(this, newEvent);
	}
};

HYSMaps.MicrosoftMap.prototype.onMouseDown = function (event) {
	
	this.events.mouseDown = true;

	var marker = this.map.GetShapeByID(event.elementID);

	if (marker && marker.HYSEvents.draggable) 
	{
		this.map.vemapcontrol.EnableGeoCommunity(true);
		this.events.dragging = true;
		this.events.draggedMarker = marker;
	}
};

HYSMaps.MicrosoftMap.prototype.onEndZoom = function (event) {

	this.zoomLevel = event.zoomLevel;
	
	if (event.zoomLevel < HYSMaps.zoom.minimum) {
		
		this.map.SetZoomLevel(HYSMaps.zoom.minimum);
		return;
	}
	if (event.zoomLevel > HYSMaps.zoom.maximum) {
		
		this.map.SetZoomLevel(HYSMaps.zoom.maximum);
		return;
	}

	this.plotter.onEndZoom(this);
};

HYSMaps.MicrosoftMap.prototype.onEndPan = function (event) {
	
	this.plotter.onEndPan(this);
};

HYSMaps.MicrosoftMap.prototype.unload = function () {
	
};