/**
 * Google Maps abstraction
 */
HYSMaps.GoogleMap = 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.GoogleMap.prototype.initialise = function (element, type) {
	
	// error checking
	if (GMap2 == undefined || !GBrowserIsCompatible()) {
		
		//TODO: display error message
		return false;
	}
	
	// instantiate Google map
	this.map = new GMap2(element);
	this.map.HYSEvents = {}; // custom events structure
	this.setCentre();
	
	// event control
	this.events = {
		
		onEndZoom: this.onEndZoom.bind(this),
		onDragEnd: this.onDragEnd.bind(this),
		onMouseUp: this.onMouseUp.bind(this)
	};
	
	GEvent.addListener(this.map, "click", this.events.onMouseUp);
	GEvent.addListener(this.map, "zoomend", this.events.onEndZoom);
	GEvent.addListener(this.map, "dragend", this.events.onDragEnd);
	
	// set map type
	switch (HYSMaps[type.toLowerCase() + 'Map']) {
		
		default:
		case HYSMaps.type.road:
			type = G_NORMAL_MAP;
			break;
		case HYSMaps.type.aerial:
			type = G_SATELLITE_MAP;
			break;
		case HYSMaps.type.hybrid:
			type = G_HYBRID_MAP;
			break;
		case HYSMaps.type.terrain:
			type = G_PHYSICAL_MAP;
			break;
	}
	this.map.setMapType(type);
	
	// add map controls
	this.map.addControl(new GSmallMapControl(), new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(5, 40)));
	
	// set min/max zoom
	var mapTypes = this.map.getMapTypes();
	for (var i = 0; i < mapTypes.length; ++i) { 
		mapTypes[i].getMinimumResolution = function () { return HYSMaps.zoom.minimum; };
		mapTypes[i].getMaximumResolution = function () { return HYSMaps.zoom.maximum; };
	}
	
	return this;
};

HYSMaps.GoogleMap.prototype.setCentre = function (geo, zoom) 
{
	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 bounds = new GLatLngBounds();
		for (i in this.markers) {
			
			bounds.extend(this.markers[i].getLatLng());
		}

		var center = bounds.getCenter();
		
		geo = new HYSMaps.GeoCoords(center.lat(), center.lng());
		zoom = this.map.getBoundsZoomLevel(bounds);
	}
	else 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.getZoom() + parseInt(zoom)
	}
	
	this.map.setCenter(new GLatLng(geo.latitude, geo.longitude), zoom);
	
};

HYSMaps.GoogleMap.prototype.makeMarker = function (coords, icon, draggable) {

	var mapIcon = new GIcon();
	mapIcon.image = icon.image;
	mapIcon.iconSize = new GSize(icon.imageSize.x, icon.imageSize.y);
	mapIcon.shadow = icon.shadow;
	mapIcon.shadowSize = new GSize(icon.shadowSize.x, icon.shadowSize.y);
	mapIcon.iconAnchor = new GPoint(icon.iconAnchor.x, icon.iconAnchor.y);
	
	var googleCoords = new GLatLng(coords.latitude, coords.longitude);
	var markerOptions = { icon: mapIcon, clickable: true, draggable: draggable };
	var marker = new GMarker(googleCoords, markerOptions);
	marker.HYSEvents = { draggable: draggable }; // custom events structure
	
	return marker;
};

/**
 * Add marker to map (must return marker)
 */
 
HYSMaps.GoogleMap.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;
		
		this.markers[coords] = marker;
		this.activeMarkers[coords] = marker;
		this.bounds.extend(coords);
		
		this.map.addOverlay(marker);
		marker.hide();
		
		return marker;
	}
	else {
		
		this.activeMarkers[coords] = this.markers[coords];
	}
};


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

	for (var i in this.activeMarkers) {
		
		this.activeMarkers[i].hide();
		
		if (!(i in this.markers)) {
			this.map.removeOverlay(this.activeMarkers[i]);
			delete this.activeMarkers[i];
		}
	}
	
	this.activeMarkers = {};
};


HYSMaps.GoogleMap.prototype.redraw = function () {
	
	// get map size
	var geoBounds = this.map.getBounds();
	var size = this.map.getSize();
	var southWest = geoBounds.getSouthWest();
  	var northEast = geoBounds.getNorthEast();
  	var fLonSpan = Math.abs(southWest.lng() - northEast.lng());
  	var fPixelsPerDegree = (size.width / fLonSpan);
  	var topLeft = new HYSMaps.GeoCoords(northEast.lat(), southWest.lng());
	var bottomRight = new HYSMaps.GeoCoords(southWest.lat(), northEast.lng());
	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)) {
			this.map.addOverlay(markers[i]);
			this.activeMarkers[i] = markers[i];
		}
	}
};

HYSMaps.GoogleMap.prototype.getMarkerCoords = function (marker) {

	var latlng = marker.getLatLng();
	var pixel = this.map.fromLatLngToContainerPixel(latlng);
	return { latitude: latlng.lat(), longitude: latlng.lng(), x: pixel.x, y: pixel.y };
};

HYSMaps.GoogleMap.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.GoogleMap.prototype.onMouseUp = function (overlay, latlng, overlaylatlng) {
	
	var marker = overlay;
	if (marker && marker.HYSEvents.onmouseup) {
		var newEvent = { latitude: overlaylatlng.lat(), longitude: overlaylatlng.lng(), wasMoving: false, movedMarker: false };
		marker.HYSEvents.onmouseup(this, newEvent);
	}
	else if (this.map.HYSEvents.onmouseup) {
		var newEvent = { latitude: latlng.lat(), longitude: latlng.lng(), wasMoving: false, movedMarker: false };
		this.map.HYSEvents.onmouseup(this, newEvent);
	}
};

HYSMaps.GoogleMap.prototype.onEndZoom = function (oldLevel, newLevel) {
	
	this.zoomLevel = newLevel;
	this.plotter.onEndZoom(this);
};

HYSMaps.GoogleMap.prototype.onDragEnd = function () {
	
	this.plotter.onEndPan(this);
};

HYSMaps.GoogleMap.prototype.unload = function () {
	
	GUnload();
};