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

/**
 * Initialise function must return the class (or false)
 */
HYSMaps.MicrosoftMap.prototype.initialise = function (element, type, showControls) {
	
	// 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);
	
	this.commentLayer = new VEShapeLayer();
	this.commentLayer.SetTitle("Comment Layer");
	this.map.AddShapeLayer(this.commentLayer);

	this.panelLayer = new VEShapeLayer();
	this.panelLayer.SetTitle("Panel Layer");
	this.map.AddShapeLayer(this.panelLayer);
            
    // set map type
	switch (HYSMaps.type[type.toLowerCase()]) {
		
		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;
	        });
	    }
	})();

	if (!showControls) {
		this.map.HideDashboard();
	}
	
	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.setZoom = function(zoom) {
	this.map.SetZoomLevel(zoom);
};

HYSMaps.MicrosoftMap.prototype.setCenter = function(latitude, longitude) {
	this.map.SetCenter(new VELatLong(latitude, longitude));
	// Below hack ensures that any panels in the area will appear when the abve methods is called
	this.map.StartContinuousPan(1,0); 
	this.map.EndContinuousPan();
};

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);
		// No need to display 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 && 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 && 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 () {
	
};


// The following properties/methods of HYSMaps.MicrosoftMap are used by HYSMaps.AutoPanelSizer

HYSMaps.MicrosoftMap.prototype.OUTERMOST_ZOOM_LEVEL = 0;

HYSMaps.MicrosoftMap.prototype.getZoomRatio = function() {
	var a = this.map.PixelToLatLong(new VEPixel( 0,  0)),
	    b = this.map.PixelToLatLong(new VEPixel(25, 25));

	return {
		x: HYSMaps.AutoPanelSizer.wrapValue(a.Latitude - b.Latitude, 180),
		y: a.Longitude - b.Longitude
	};
};

HYSMaps.MicrosoftMap.prototype.addZoomListener = function(callback, context) {
	this.map.AttachEvent('onendzoom', function(event) {
		callback.call(context, event.zoomLevel);
	});

	return callback;
};

HYSMaps.MicrosoftMap.prototype.addPanel = function(latitude, longitude, panelObj) {
	var root,
	    map   = this.map,
	    id    = 'poor-ve-' + Math.random(),
	    shape = new VEShape(VEShapeType.Pushpin, new VELatLong(latitude, longitude));

	/* Alignment Reference
	var shape2 = new VEShape(VEShapeType.Pushpin, new VELatLong(latitude, longitude));
	map.AddShape(shape2);
	/**/

	if (panelObj.panelType != "comment") {
		this.panelLayer.AddShape(shape);
	}
	else {
		this.commentLayer.AddShape(shape);
	}
	map.AttachEvent('onendpan', update);
	map.AttachEvent('onendzoom', update);
	shape.SetCustomIcon('<div id="' + id + '" class="panel-buffer"><div class="panel-anchor"></div></div>');

	root = document.getElementById(id) || createPanelRoot();

	return { mapPanelRef: shape, domEl: root.getElementsByTagName('div')[0] };

	function update() {
		setTimeout(restore, 0); // what an amazing hack this is
	}

	function restore() {
		var test = document.getElementById(id);

		if (test && test !== root) {
			test.parentNode.replaceChild(root, test);
		}
	}

	function createPanelRoot() {
		var div = document.createElement('div');
		div.id = id;
		div.className = 'panel-buffer';
		div.appendChild(document.createElement('div')).className = 'panel-anchor';
		return div;
	}
};

HYSMaps.MicrosoftMap.prototype.removePanel = function(dataObj) {
	var panel = dataObj.panel;
	var panelType = dataObj.panelType;
	if (panelType != "comment") {
		this.panelLayer.DeleteShape(panel);
	}
	else {
		this.commentLayer.DeleteShape(panel);
	}
}