/**
 * Creates a new MapWidget.
 *
 *
 * Does not load positions/contents if there is no currentPositionedContent.
 * So either the content already has a position, and contents in proximity are shown.
 * Or the user places it anew.
 *
 * TODO Create semi-transparent overlay with explanation if no position, yet.
 * FIXME Due to new map loading mechanism, onLogout/onLogin events are broadcasted before mapWidget is inited.
 */
function MapWidget($element){
	MapWidget.baseConstructor.call(this, $element);
	
	this.icon = MapWidget.createIcon(MACEConstants.widgetsURL + "/map/img/marker-mace.png");
	this.iconSelected = MapWidget.createIcon(MACEConstants.widgetsURL + "/map/img/marker-selected.png");
	this.iconExternal = MapWidget.createIcon(MACEConstants.widgetsURL + "/map/img/marker-external.png");
	this.iconEditable = MapWidget.createIcon(MACEConstants.widgetsURL + "/map/img/marker-move.png");
	this.iconCluster = MapWidget.createIcon(MACEConstants.widgetsURL + "/map/img/marker-cluster.png", 54, 63, 26, 26);
	
	
	// All MaceLocations shown on map (key: positionedContent.id, value: location)
	this.locations = new HashMap2();
	// The current positioned content.
	this.currentPositionedContent = null;
	
	// Options
	this.options = {
		"useCluster": true,
		"showPositionsInProximity": false,
		"showExternalPositionsInProximity": false,
		"searchOnlyAndAlways": false
	};
	this.resultCount = MapWidget.DEFAULT_COUNT;
	this.resultOffset = 0;
	
	// The Google map
	this.map = this.initGMaps();
	// Geocoder to search freeform places (via Google)
	this.geocoder = new GClientGeocoder();
	// Utility to cluster locations
	var clusterMarkerIconFunction = function(number){
		var icon = new GIcon();
		icon.image = MACEConstants.widgetsURL + "/map/img/marker-cluster-" + ((number < 20) ? number : "n") + ".png";
		icon.iconSize = new GSize(54, 63);
		icon.iconAnchor = new GPoint(26, 26);
		icon.infoWindowAnchor = new GPoint(20, 5);
		return icon;
	}
	// option: clusterMarkerIcon: this.iconCluster, 
	this.clusterMarker = new ClusterMarker(this.map, {
		intersectPadding: -2,
		clusterMarkerIconFunction: clusterMarkerIconFunction
	});
	
	this.positionsLoadedEvent = new YAHOO.util.CustomEvent("positionsLoaded", this);
	
	
	
	// Components
	this.messageBox = new MessageBox($("#messageBox", this.$element));
	this.tracker = new MapTracker();
	
	// jQuery and DOM elements
	var _this = this;
	this.$searchForm = $("#mapSearchForm", this.$element);
	this.$searchForm.submit(function(){
		_this.search(this.mapSearchTextField.value);
		return false;
	})
	this.$setButton = $("#setButton", this.$element);
	this.$cancelButton = $("#cancelButton", this.$element);
	this.$saveButton = $("#saveButton", this.$element);
	
	this.setEditMode(false);
	
	// toolBars are two divs: button bar and search bar
	this.$editToolbar = $("#editToolbar", this.$element);
	this.$editToolbar.hide();
	this.$searchToolbar = $("#searchToolbar", this.$element);
	this.$searchToolbar.hide();
	
	console.log("MapWidget created.");
}

MapWidget.extend(Widget);


// Constants -------------------------------

// Visual center of (Western) Europe
MapWidget.EUROPE_CENTER_LAT_LNG = new GLatLng(55.57, 13.54);
MapWidget.START_ZOOM_LEVEL = 3;

MapWidget.DEFAULT_COUNT = 12;
MapWidget.DEFAULT_COUNT_STEP = 12;

// Service URLs
//MapWidget.getPositionURL = MACEConstants.rootURL + "php/mock.php?callback=?&success=false";
MapWidget.getPositionURL = MACEConstants.widgetsURL + "map/php/getPosition.php?callback=?";
MapWidget.savePositionURL = MACEConstants.widgetsURL + "map/php/savePosition.php?callback=?";
//MapWidget.savePositionURL = MACEConstants.rootURL + "php/mock.php?success=true";

//MapWidget.getContentPositionsInAreaURL = MACEConstants.rootURL + "php/mock.php?callback=?&success=false";
MapWidget.getContentPositionsInAreaURL = MACEConstants.widgetsURL + "map/php/getContentPositionsInArea.php";

MapWidget.showContentURL = MACEConstants.detailsBaseSrc;


// Event handling -------------------------------

MapWidget.prototype.getSubscriptionEvents = function(){
	return [new SubscriptionEvent("login", this.onLogin), new SubscriptionEvent("logout", this.onLogout), new SubscriptionEvent("lomLoaded", this.onLomLoaded)];
}

MapWidget.prototype.getBroadcastEvents = function(){
	console.log("MapWidget broadcasts: positionsLoaded");
	return [this.positionsLoadedEvent];
}

MapWidget.prototype.onLogin = function(type, args){
	console.log("MapWidget.onLogin");
	this.setLoginState(true);
}

MapWidget.prototype.onLogout = function(type, args){
	console.log("MapWidget.onLogout");
	this.setLoginState(false);
}

MapWidget.prototype.setLoginState = function(loggedIn){
	if (loggedIn) {
		if (!this.options.searchOnlyAndAlways) {
			this.$editToolbar.slideDown();
		}
		this.$searchToolbar.slideDown();
	}
	else {
		this.$editToolbar.slideUp();
		if (!this.options.searchOnlyAndAlways) {
			this.$searchToolbar.slideUp();
		}
		this.cancelCurrentPoint();
	}
}

MapWidget.prototype.onLomLoaded = function(type, args){
	var lom = args[0];
	console.log("MapWidget.onLomLoaded: lom.id=" + lom.id);
	this.loadAndCreatePositionedContent(lom.id, lom.title);
}

// Map methods ----------------------------------


MapWidget.prototype.initGMaps = function(){
	var map = null;
	if (GBrowserIsCompatible()) {
		map = new GMap2(document.getElementById("map"));
		map.addControl(new GSmallZoomControl());
		map.addControl(new GMapTypeControl(true));
		map.setCenter(MapWidget.EUROPE_CENTER_LAT_LNG, MapWidget.START_ZOOM_LEVEL);
		
		// Listener to get lat/long after zoom or moving the map
		// Used to query positions for new area
		var _this = this;
		GEvent.addListener(map, "moveend", function(){
			_this.updatePositionsInProximity();
		});
		GEvent.addListener(map, "zoomend", function(oldLevel, newLevel){
			_this.tracker.trackZoom(oldLevel, newLevel);
			
			// REVISIT zoomend always also fires moveend, so don't use it for now.		
			/*
			 console.log("zoomend: oldLevel=" + oldLevel + ", newLevel=" + newLevel);
			 if (newLevel < oldLevel) {
			 console.log("\t Updating..");
			 _this.updatePositionsInProximity();
			 }
			 else {
			 console.log("\t Nothing");
			 }
			 */
		});
	}
	return map;
}

MapWidget.prototype.setPosition = function(lat, lng, zoom){
	console.log("MapWidget.setPosition " + lat + ", " + lng + ", " + zoom);
	var center = new GLatLng(lat, lng);
	var zoomNr = parseInt(zoom);
	this.map.setCenter(center, zoomNr);
}


/**
 * Resets the view of the map. Displays an overview of Europe.
 */
MapWidget.prototype.resetView = function(){
	this.map.setCenter(MapWidget.EUROPE_CENTER_LAT_LNG, MapWidget.START_ZOOM_LEVEL);
}



// ----------------------------------------------

/**
 * Updates (internal and external) positions in proximity.
 *
 * Updates only after some time of non-interaction (move ... update. move, move ... update)
 */
MapWidget.prototype.updatePositionsInProximity = function(){
	if (this.currentPositionedContent != null || this.options.showPositionsInProximity) {
		console.log("Updating positions in proximity in a moment ...");
		$.timer.remove(this, "updatePiP");
		
		var _this = this;
		$.timer.add(this, "1000ms", "updatePiP", _this._updatePositionsInProximity, 1);
	}
	else {
		console.log("Not updating positions in proximity. Neither currentPositionedContent nor option showPositionsInProximity.");
	}
}

MapWidget.prototype._updatePositionsInProximity = function(){
	console.log("Updating positions in proximity now.");
	
	this.loadAndCreatePositionsInCurrentArea();
	
	if (this.options.showExternalPositionsInProximity) {
		loadAndCreateFreebaseLocationsCurrentArea();
	}
}

MapWidget.prototype.refreshMarkers = function(){
	if (this.options.useCluster) {
		//this.clusterMarker.fitMapToMarkers();
		this.clusterMarker.refresh();
	}
}


// Loading -------------------------------

MapWidget.prototype.loadMorePositions = function(countStep){
	//	if ( countStep )
	this.resultCount += MapWidget.DEFAULT_COUNT_STEP;
	this.loadAndCreatePositionsInCurrentArea();
}

/**
 * Loads the position of given contentId, and creates a PositionedContent.
 *
 * A content without position is not shown on the map, directly.
 *
 * @param {String} contentId The id of the content to load positions for.
 * @param {String} title The title to display.
 */
MapWidget.prototype.loadAndCreatePositionedContent = function(contentId, title){
	this.removeLocations();
	var _this = this;
	console.log("load Position from " + MapWidget.getPositionURL + "?contentId=" + contentId);
	$.getJSON(MapWidget.getPositionURL, {
		"contentId": contentId
	}, function(result){
		if (typeof(result.success) == "undefined" || result.success) {
			console.log("Found position '" + result["positionId"] + "'");
			var geo = new MaceGeo(result["latitude"], result["longitude"]);
			var posContent = new PositionedContent(contentId, result["positionId"], title, null, geo, result["description"], null, null, PositionedContentType.MACE_SELECTED);
			_this.setCurrentPositionedContent(posContent);
			
			var maceLocation = new MaceLocation(posContent, _this);
			_this.addLocation(maceLocation);
			_this.centerPosition(maceLocation);
		}
		else {
			console.warn("Loading position failed: " + result.message);
			
			_this.resetView();
			
			var posContent = new PositionedContent(contentId, null, title, null, null, null, null, null, PositionedContentType.MACE_SELECTED);
			_this.setCurrentPositionedContent(posContent);
		}
	});
}

MapWidget.prototype.loadAndCreatePositionsInCurrentArea = function(){
	// Attention: Mapping from Google's (ne, sw) to ContextService's (nw, se)
	var mgBounds = new MaceGeoBoundary(this.map.getBounds())
	console.log("loadAndCreatePositionsInCurrentArea: " + mgBounds);
	this.tracker.trackViewArea(mgBounds);
	
	var _this = this;
	$.getJSON(MapWidget.getContentPositionsInAreaURL, {
		"nwLat": mgBounds.nwLat,
		"nwLng": mgBounds.nwLng,
		"seLat": mgBounds.seLat,
		"seLng": mgBounds.seLng,
		"count": this.resultCount,
		"offset": this.resultOffset
	}, function(result){
		if (result != null && result.success) {
			var contents = result.contents;
			_this.removeOtherLocations();
			if (contents != null) {
				console.log("Found " + contents.length + " positions");
				_this.createAndAddLocations(contents);			
			}

			// Fire listener for other widgets to use the positions
			_this.positionsLoadedEvent.fire(_this.locations, result.numResults, _this.map.getCenter(), _this.map.getZoom());
		}
		else {
			console.warn("Loading positions failed: " + result.message);
		}
	
	});
}

/**
 *
 */
MapWidget.prototype.createAndAddLocations = function(contentShortInfos){
	for (var i = 0; i < contentShortInfos.length; i++) {
		var csi = contentShortInfos[i];
		var title = (csi.title == null || csi.title.isEmpty()) ? "Open detail for '" + csi["contentId"] + "'" : csi.title;
		var description = csi.description;
		
		for (var j = 0; j < csi.relevantPositions.length; j++) {
			var position = csi.relevantPositions[j];
			var geo = new MaceGeo(position["latitude"], position["longitude"]);
			var type = position["auto_generated"] ? PositionedContentType.EXTERNAL : PositionedContentType.MACE;
			var posContent = new PositionedContent(position["contentId"], position["positionId"], title, null, geo, description, null, null, type);
			var maceLocation = new MaceLocation(posContent, this);
			this.addLocation(maceLocation);
		}
		console.log("createAndAddLocations " + i);
	}
	
	this.refreshMarkers();
}


/**
 * Centers map to given location and zooms in.
 *
 * @param {MaceLocation} maceLocation The location to center the map around.
 */
MapWidget.prototype.centerPosition = function(maceLocation){
	this.map.setCenter(maceLocation.getCurrentPosition(), 12);
}

/**
 * Centers map to given location.
 *
 * @param {Object} locationId The id of the location to center.
 */
MapWidget.prototype.centerPositionById = function(locationId){
	var maceLocation = this.locations.get(locationId);
	this.centerPosition(maceLocation);
}

/**
 * Sets the current PositionedContent for editing.
 * @param {PositionedContent} positionedContent
 */
MapWidget.prototype.setCurrentPositionedContent = function(positionedContent){
	this.currentPositionedContent = positionedContent;
	
	if (this.currentPositionedContent) {
		this.setPositioningMode(true);
		this.messageBox.reset();
		$("#mapSearchTextField").val("");
	}
	else {
		this.setPositioningMode(false);
	}
}

/**
 * Places the current PositionedContent in the center on the map, and makes it editable.
 * If the PositionedContent has no location yet, a new one will be created.
 */
MapWidget.prototype.placeCurrentPositionedContent = function(){
	if (!this.currentPositionedContent) {
		console.warn("No current positionedContent.");
		return;
	}
	
	var maceLocation = this.locations.get(this.currentPositionedContent.id);
	if (!maceLocation) {
		console.log("Creating new Location for PositionedContent");
		var newGLatLng = new GLatLng(this.map.getCenter().lat(), this.map.getCenter().lng());
		maceLocation = new MaceLocation(this.currentPositionedContent, this, newGLatLng);
		this.addLocation(maceLocation);
	}
	else {
		var newGLatLng = new GLatLng(this.map.getCenter().lat(), this.map.getCenter().lng());
		maceLocation.newGLatLng = newGLatLng;
		maceLocation.updatePosition();
	}
	
	this.tracker.trackSetPosition(maceLocation);
	
	console.log("Enable editing of Location");
	this.makeEditable(this.currentPositionedContent.id);
}

/**
 * Adds location to the locations list, and display its marker on the map.
 *
 * @param {MaceLocation} location The MaceLocation.
 */
MapWidget.prototype.addLocation = function(maceLocation){
	var id = maceLocation.positionedContent.id;
	
	var oldMaceLocation = this.locations.get(id);
	// Don't add a location twice (mainly to prevent duplicate of the current selected one)
	if (!oldMaceLocation) {
		this.locations.put(id, maceLocation);
		
		if (this.options.useCluster && !maceLocation.positionedContent.isSelected()) {
			this.clusterMarker.addMarkers([maceLocation.marker]);
		}
		else {
			this.map.addOverlay(maceLocation.marker);
		}
		console.log("Added location '" + id + "' at " + maceLocation.originalGLatLng);
	}
	else {
		console.log("Already in '" + id + "'");
	}
}

/**
 * Removes all locations from the widget.
 * Additionally, the currentPositionedContent is set to null.
 */
MapWidget.prototype.removeLocations = function(){
	this.setCurrentPositionedContent(null);
	this.removeOtherLocations();
	this.setPositioningMode(false);
	this.setEditMode(false);
}

/**
 * Removes all locations from the widget, but the currentPositionedContent one.
 */
MapWidget.prototype.removeOtherLocations = function(){
	// Store currentLocation
	var currentLocation = null;
	if (this.currentPositionedContent != null) {
		currentLocation = this.locations.get(this.currentPositionedContent.id);
	}
	
	this.map.clearOverlays();
	this.locations.clear();
	this.clusterMarker.removeMarkers();
	
	// Add again, and show as editable if it was before
	if (currentLocation) {
		this.addLocation(currentLocation);
		if (currentLocation.hasNewPosition()) {
			currentLocation.makeEditable();
		}
	}
}


/**
 * Searches position and places it on the map. If none found,
 * a small error message is displayed, if ambigious values were searched for, only best match is shown.
 *
 * @param {String} query
 */
MapWidget.prototype.search = function(query){
	this.messageBox.showWorking("Searching");
	
	this.tracker.trackSearch(query);
	
	var t = this;
	this.geocoder.getLocations(query, function(response){
		if (!response || response.Status.code != 200) {
			// Update widget message area
			t.messageBox.showError("No place found");
			
		}
		else {
			if (response.Placemark.length > 1) {
				console.warn("Found " + response.Placemark.length + " Placemarks");
			}
			var place = response.Placemark[0];
			var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
			console.info("Found position for \"" + query + "\": " + point + ", " + place.AddressDetails.Accuracy + "");
			
			// Update widget message area
			t.messageBox.showOk(place.address);
			
			// Center to position, and zoom according to accuracy 
			var zoomLevel = t.getZoomLevelFromAccuracy(place.AddressDetails.Accuracy);
			t.map.setCenter(point, zoomLevel);
		}
	});
}


/**
 * Maps Google address accuracy to best fitting zoom level.
 *
 * See http://code.google.com/apis/maps/documentation/reference.html#GGeoAddressAccuracy
 *
 * @param {int} accuracy
 */
MapWidget.prototype.getZoomLevelFromAccuracy = function(accuracy){
	var zoomLevel;
	switch (accuracy) {
		case 1:
			zoomLevel = 5;
			break;
		case 2:
			zoomLevel = 7;
			break;
		case 3:
			zoomLevel = 9;
			break;
		case 4:
			zoomLevel = 12;
			break;
		case 5:
			zoomLevel = 12;
			break;
		case 6:
			zoomLevel = 13;
			break;
		case 7:
			zoomLevel = 14;
			break;
		case 8:
			zoomLevel = 15;
			break;
		case 9:
			zoomLevel = 16;
			break;
			
		default:
			zoomLevel = 10;
	}
	return zoomLevel;
}

/**
 * Cancels edit mode for specific content.
 * Sets the location back to original position if existing, otherwise removes it.
 *
 * @param {String} id ID of the content.
 */
MapWidget.prototype.cancelPoint = function(id){
	console.log("Cancel " + id);
	var location = this.locations.get(id);
	this.makeNonEditable(id);
	
	if (location.hasOriginalPosition()) {
		console.log("Back to original position");
		location.newGLatLng = null;
		location.updatePosition();
	}
	else {
		console.log("Remove marker from map");
		this.map.removeOverlay(location.marker);
		this.locations.remove(location.positionedContent.id);
		console.log("Successfully removed.");
	}
}

/**
 * Cancels edit mode for currentPositionedContent.
 */
MapWidget.prototype.cancelCurrentPoint = function(){
	if (this.currentPositionedContent) {
		this.cancelPoint(this.currentPositionedContent.id);
	}
}

/**
 * Saves the new position of a location.
 * Either saves a new or updates an existing position.
 *
 * @param id The ID of the location.
 */
MapWidget.prototype.savePoint = function(id){
	var location = this.locations.get(id);
	if (!location.hasNewPosition()) {
		console.log("No new position. Saving not needed.");
		this.makeNonEditable(id);
		return;
	}
	
	var contentId = location.positionedContent.id;
	var oldPositionId = location.positionedContent.positionId;
	var lat = location.newGLatLng.lat();
	var lng = location.newGLatLng.lng();
	var title = location.positionedContent.title;
	var description = location.positionedContent.description;
	console.log("MapWidget. OldPositionId=" + oldPositionId);
	
	var params = {
		"contentId": contentId,
		"latitude": lat,
		"longitude": lng,
		"title": title,
		"description": description
	};
	if (oldPositionId) {
		// Add old positionId as param to change/update position.
		params["positionId"] = oldPositionId;
	}
	
	var _this = this;
	$.getJSON(MapWidget.savePositionURL, params, function(result){
		if (typeof(result.success) == "undefined" || result.success) {
			console.log("Saved " + (oldPositionId ? "(updated)" : "(new)") + " position sucessfully.");
			_this.tracker.trackSavePosition(location);
			
			location.originalGLatLng = location.newGLatLng;
			location.newGLatLng = null;
			location.positionedContent.positionId = result.newPositionId;
			location.positionedContent.geo = new MaceGeo(lat, lng);
			
			_this.makeNonEditable(contentId);
		}
		else {
			console.error("Did NOT save position: " + result.message);
			_this.messageBox.showError("Saving failed. " + result.message);
		}
	});
}


MapWidget.prototype.showSearchToolbar = function(){
	this.$searchToolbar.show();
}

/**
 * Show detail tab of the current info window
 */
MapWidget.prototype.showDetail = function(){
	// NB: There always only can be one info window.
	this.map.getInfoWindow().selectTab(1);
}

/**
 * Makes the location with given id editable.
 * Additionally, this shows all the edit-buttons.
 *
 * @param {String} id The id of the content.
 * @param {boolean} refine
 */
MapWidget.prototype.makeEditable = function(id, refine){
	var location = this.locations.get(id);
	location.makeEditable(refine);
	
	this.setEditMode(true);
}

/**
 * Switches the edit mode. Changes toolbar buttons state.
 *
 * @param {boolean} editMode Whether to set to edit or to non-edit.
 */
MapWidget.prototype.setEditMode = function(editMode){
	if (editMode) {
		this.$cancelButton.removeClass("disabled");
		this.$saveButton.removeClass("disabled");
		
		var _this = this;
		this.$cancelButton.click(function(){
			_this.cancelPoint(_this.currentPositionedContent.id);
		});
		this.$saveButton.click(function(){
			_this.savePoint(_this.currentPositionedContent.id);
		});
	}
	else {
		this.$cancelButton.addClass("disabled");
		this.$saveButton.addClass("disabled");
		
		this.$cancelButton.unbind("click");
		this.$saveButton.unbind("click");
	}
}

/**
 * Switches the positioning mode. Sets the edit button in toolbar.
 * @param {Object} positioningMode Whether to set to positioning, or not.
 */
MapWidget.prototype.setPositioningMode = function(positioningMode){
	if (positioningMode) {
		this.$setButton.removeClass("disabled");
		var _this = this;
		this.$setButton.click(function(){
			_this.placeCurrentPositionedContent();
		});
	}
	else {
		this.$setButton.removeClass("disabled");
		this.$setButton.unbind("click");
	}
}


/**
 * Makes the location with given id non-editable.
 * Additionally, this hides all the edit-buttons.
 *
 * @param {String} id The id of the content.
 */
MapWidget.prototype.makeNonEditable = function(id){
	var location = this.locations.get(id);
	location.makeNonEditable();
	
	this.setEditMode(false);
}

/**
 * Opens the content of the given id in mace.
 * @param {String} id The id of the content
 */
MapWidget.prototype.openInMACE = function(id){
	var location = this.locations.get(id);
	window.open(MapWidget.showContentURL + location.positionedContent.id);
}

/**
 * Opens the location of the given id in a new browser window.
 * @param {String} id The id of the content
 */
MapWidget.prototype.openContent = function(id){
	var location = this.locations.get(id);
	window.open(location.positionedContent.url);
}


// Helper methods -------------------------------

MapWidget.prototype.logInfo = function(id){
	var location = this.locations.get(id);
	console.log("Location: " + location);
}

MapWidget.prototype.logCurrentArea = function(){
	var center = this.map.getCenter();
	console.log("center=" + center);
	
	var maceGeoBoundary = new MaceGeoBoundary(this.map.getBounds())
	console.log("bounds=" + maceGeoBoundary);
}

/**
 * Creates an GIcon with given image.
 *
 * @param {String} image The URL to the image.
 * @param {int} width The width of the icon (optional)
 * @param {int} height The height of the icon (optional)
 * @param {int} anchorX The x position of the anchor point (optional)
 * @param {int} anchorY The y position of the anchor point (optional)
 *
 * @return {GIcon} The created icon.
 */
MapWidget.createIcon = function(image, width, height, anchorX, anchorY){
	var myWidth = width || 50;
	var myHeight = height || 43;
	var myAnchorX = anchorX || 22;
	var myAnchorY = anchorY || 33;
	
	var icon = new GIcon();
	icon.image = image;
	icon.iconSize = new GSize(myWidth, myHeight);
	icon.iconAnchor = new GPoint(myAnchorX, myAnchorY);
	icon.infoWindowAnchor = new GPoint(20, 5);
	return icon;
}

/**
 * Creates new MaceGeoBoundary.
 * Describes the area between the two points northWest and southEast.
 *
 * @args googleBounds Will be mapped from (northEast, southWest) to (northWest, southEast).
 * (or)
 * @args nwLat The northWest latitude
 * @args nwLon The northWest longitude
 * @args seLat The southEast latitude
 * @args seLon The southEast longitude
 */
function MaceGeoBoundary(){
	if (arguments.length == 1) {
		var googleBounds = arguments[0];
		this.nwLat = googleBounds.getNorthEast().lat();
		this.nwLng = googleBounds.getSouthWest().lng();
		this.seLat = googleBounds.getSouthWest().lat();
		this.seLng = googleBounds.getNorthEast().lng();
	}
	else 
		if (arguments.length == 4) {
			this.nwLat = arguments[0];
			this.nwLng = arguments[1];
			this.seLat = arguments[2];
			this.seLng = arguments[3];
		}
}

MaceGeoBoundary.prototype.toString = function(){
	return "(" + this.nwLat + "," + this.nwLng + ";" + this.seLat + "," + this.seLng + ")";
}


