function GoogleMap(mapID){
	this.gmap = null;
	this.mapID = mapID;
	this.homePos = new GLatLng(1, 1);
	this.homeZoom = 5;
	var _this = this;
	
	this.setHomePos = function(lat, lng){
		this.homePos = new GLatLng(lat, lng);
	}	
	
	this.initialize = function(){
		if (GBrowserIsCompatible()) {
			this.gmap = new GMap2(document.getElementById(this.mapID));
			this.gmap.setCenter(this.homePos, this.homeZoom);
			this.gmap.enableScrollWheelZoom();

			return true;
		}
		return false;
	}
	
	this.onNewPosition = function(callBack){
		
		// listen for new positions
		GEvent.addListener(this.gmap, "moveend", function() {			
			callBack();
		}); 		
		GEvent.addListener(this.gmap, "zoom", function() {
			callBack();
		}); 
	
	}
	
	this.getZoomLevel = function(){
		return this.gmap.getZoom();
	};
	
	this.getCenterPos = function(){
		return this.gmap.getCenter();
	};
	
	this.moveTo = function(lat, lng, zoomLevel){
		if(zoomLevel)
			this.gmap.setZoom(parseInt(zoomLevel));
		this.gmap.panTo(new GLatLng(lat, lng));
	};
	
	this.up = function(){
		this.moveTo(this.getCenterPos().lat()+0.01*(16-this.getZoomLevel()), this.getCenterPos().lng(), this.getZoomLevel());
	}
	
	this.down = function(){
		this.moveTo(this.getCenterPos().lat()-0.01*(16-this.getZoomLevel()), this.getCenterPos().lng(), this.getZoomLevel());
	}
	
	this.right = function(){
		this.moveTo(this.getCenterPos().lat(), this.getCenterPos().lng()+0.02*(16-this.getZoomLevel()), this.getZoomLevel());
	}
	
	this.left = function(){
		this.moveTo(this.getCenterPos().lat(), this.getCenterPos().lng()-0.02*(16-this.getZoomLevel()), this.getZoomLevel());
	}
	
	// Guesses the area to move to
	this.moveToArea = function(maxlat, maxlon, minlat, minlon){
		if(minlat>maxlat){
			var t = minlat;
			minlat = maxlat;
			maxlat = t;
		}			
		if(minlon>maxlon){
			var d = minlon;
			minlon = maxlon;
			maxlon = d;
		}
		var zoomlat		= minlat+(maxlat-minlat)/2;
		var zoomlon		= minlon+(maxlon-minlon)/2;
		var zoomlevel = 11;
		this.moveTo(zoomlon, zoomlat,zoomlevel);
	}

	this.zoomIn = function(){
		this.gmap.setZoom(this.gmap.getZoom()+1);
	};
	
	this.zoomOut = function(){
		this.gmap.setZoom(this.gmap.getZoom()-1);
	};
	
	this.goHome = function(){
		this.moveTo(this.homePos.lat(), this.homePos.lng(), this.homeZoom);
	};
	
	// Marker Management
	
	this.markerManagers = [];
	
	this.addManager = function(name){
		var m = new GmapsMarkerManager();
		m.name = name;
		//m.markerManager = new MarkerManager(this.gmap);
		this.markerManagers.push(m);
	}
	
	this.getManager = function(name){
		for(var i=0; i<this.markerManagers.length; i++)
			if(this.markerManagers[i].name === name)
				return this.markerManagers[i];
		return null;
	}
	
	this.hideManager = function(name){
		this.getManager(name).hide();
	}
	
	this.showManager = function(name){
		this.getManager(name).show();
	}
	
	this.toggleManager = function(name){
		var m = this.getManager(name);
		if(m.isVisible)
			m.hide();
		else
			m.show();		
	}
	
	this.addMarker = function(pos, zoomLvl, callbackFunction, markerManagerName, draggable, icon, tooltip){
		
		if(draggable == null)
			draggable = false;
			
		var marker = new GMarker(this.getUniquePoint(pos), {draggable: draggable, icon: icon});

		if(callbackFunction!=null && callbackFunction!=undefined){
			GEvent.addListener(marker, "click", callbackFunction);
		}
		
		if(tooltip != undefined && tooltip != null){
			GEvent.addListener(marker,"mouseover", function() {
				ViewController.showTooltip(tooltip, PageController.mouseX, PageController.mouseY);
				//this.setImage("/themes/images/gmaps.icon.shadow.png");
			});        
			GEvent.addListener(marker,"mouseout", function() {
				ViewController.hideTooltip();
			});   

		}
			
		if(markerManagerName){
			var m = this.getManager(markerManagerName);
			m.addMarker(marker, zoomLvl);
		} else 
			this.gmap.addOverlay(marker);
		
		
		return marker;
	}
	
	this.addPolyline = function(points, callbackFunction, color, tooltip, markerManagerName, zoom){
	
		if(color == undefined)
			color = "#000"; //Util.getRandomColor();
	
		var polyline = new GPolyline(points, color, 3, 0.6, {});
		
		if(callbackFunction!=null && callbackFunction!=undefined){
			GEvent.addListener(polyline, 'click', callbackFunction);
		}
		
		if(tooltip != undefined && tooltip != null){
			GEvent.addListener(polyline,"mouseover", function() {
				ViewController.showTooltip(tooltip, PageController.mouseX, PageController.mouseY);

        			_this.gmap.getDragObject().setDraggableCursor("pointer"); 
				this.setStrokeStyle({
					opacity: 1,
					weight: 4
				});
			});        
			GEvent.addListener(polyline,"mouseout", function() {
				ViewController.hideTooltip();
				_this.gmap.getDragObject().setDraggableCursor("url(http://maps.google.com/intl/en_us/mapfiles/openhand.cur),default"); 
				this.setStrokeStyle({
					opacity: 0.8,
					weight: 3
				});
			});   

		}
		
		if(markerManagerName != undefined && markerManagerName != null){
			var m = this.getManager(markerManagerName);
			m.addPolyline(polyline, zoom);
		} else {
			this.gmap.addOverlay(polyline);
		}
	}
	
	this.positionMarker = null;
	this.activatePositionPicker = function(latElementID, lngElementID, clickCallback){
		GEvent.addListener(this.gmap, 'click', function(overlay,point) { 
			if(point == undefined)
				return;
				
			if(this.positionMarker)
				this.positionMarker.hide();
				
			this.positionMarker = MapController.map.addMarker(point, null, null, null, true);
			$("#" + latElementID).val(roundNumber(this.positionMarker.getLatLng().lat(),4));
			$("#" + lngElementID).val(roundNumber(this.positionMarker.getLatLng().lng(),4));
			if(clickCallback!=undefined) clickCallback();
			var posMarker = this.positionMarker;
			GEvent.addListener(this.positionMarker, "dragend", function() {
				$("#" + latElementID).val(roundNumber(posMarker.getLatLng().lat(),4));
				$("#" + lngElementID).val(roundNumber(posMarker.getLatLng().lng(),4));
				if(clickCallback!=undefined) clickCallback();
			});

		});
		
		$("#"+latElementID+", #" + lngElementID).keyup(function(){
			
			GEvent.trigger(MapController.map.gmap, 'click', null, new GLatLng ($("#" + latElementID).val(), $("#" + lngElementID).val())); 
			MapController.map.moveTo($("#" + latElementID).val(), $("#" + lngElementID).val());
		});
		
		// show controls
		this.gmap.setUIToDefault();
		
		if($("#" + latElementID).val().length>0 && $("#" + lngElementID).val().length>0){
			GEvent.trigger(MapController.map.gmap, 'click', null, new GLatLng ($("#" + latElementID).val(), $("#" + lngElementID).val()));
			MapController.map.moveTo($("#" + latElementID).val(), $("#" + lngElementID).val());
		} else // trigger click on homepos
			GEvent.trigger(MapController.map.gmap, 'click', null, this.homePos); 
	}
	
	this.selectedMap = null;
	this.setMap = function(name){
		if(name==undefined || name==null)
			name = UserController.getSetting('map');
			
		switch(name)
		{
			case "ga-physical":
				this.setGMapType(0);
				break;
			case "ga-normal":
				this.setGMapType(1);
				break;
			case "ga-satellite":
				this.setGMapType(2);
				break;
			case "ga-hybrid":
				this.setGMapType(3);
				break;
			case "ga-default":
				this.setGMapType(4);
				break;
			case "sk-topo2":
				this.statensKartverk(0);
				break;
			case "sk-kartdata2":
				this.statensKartverk(1);
				break;
			case "sk-sjohovedkart":
				this.statensKartverk(2);
				break;
			case "sk-topo2graatoner":
				this.statensKartverk(3);
				break;
			case "sk-toporaster2":
				this.statensKartverk(4);
				break;
			case "sk-europa":
				this.statensKartverk(5);
				break;
			default:
				this.setGMapType(0);
		}
		
		this.selectedMap = name;
		UserController.setSetting('map', name);
	}
	
	// Google maps - map type
	this.setGMapType = function(num){
	
		// remove possible other maps
		this.removeStatensKartverk();
		
		var mapType = G_PHYSICAL_MAP;
		if(num == 1)
			mapType = G_NORMAL_MAP;
		else if(num == 2)
			mapType = G_SATELLITE_MAP;
		else if(num == 3)
			mapType = G_HYBRID_MAP;
		else if(num == 4)
			mapType = G_DEFAULT_MAP_TYPES;
		
		Util.log("Adding map: google - " + mapType);
			
		this.gmap.setMapType(mapType);
	}
	
	this.removeGMap = function(){
		this.gmap.removeMapType(this.gmap.getCurrentMapType());
	}
	
	// Statens Kartverk integration
	
	this.statensKartverkOverlay = null;
	
	this.statensKartverk = function(num){
	
		// remove possible other maps
		this.removeStatensKartverk();
		this.removeGMap();
		
		var mapType = "topo2";
		if(num == 1)
			mapType = "kartdata2";
		if(num == 2)
			mapType = "sjo_hovedkart2";
		if(num == 3)
			mapType = "topo2graatone";
		if(num == 4)
			mapType = "toporaster2";
		if(num == 5)
			mapType = "europa";
		
		Util.log("Adding map: statens kartverk - " + mapType);
		
		var tilelayer = new GTileLayer(null, null, null,
			 {tileUrlTemplate: 'http://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=' + mapType + '&zoom={Z}&x={X}&y={Y}',
			 isPng:true,
			 opacity:1.0 }
		);
		this.statensKartverkOverlay = new GTileLayerOverlay(tilelayer);
		this.gmap.addOverlay(this.statensKartverkOverlay);
	}
	
	this.removeStatensKartverk = function(){
		if(this.statensKartverkOverlay != null){
			Util.log("Removing map: statens kartverk");
			this.gmap.removeOverlay(this.statensKartverkOverlay);
		} 
		this.statensKartverkOverlay = null;
	}
	
	//
	// Div util
	//
	
	var addedPoints = [];
	this.getUniquePoint = function(point){
        return point;
        if(addedPoints.indexOf(point.lat() + "_" + point.lng())<0){
            addedPoints.push(point.lat() + "_" + point.lng());
            return point;
        }
        addedPoints.push((point.lat() + 0.0002) + "_" + (point.lng() + 0.0004));
        return new GLatLng((point.lat() + 0.0002), (point.lng() + 0.0004));
	}
}

function GmapsMarkerManager(){
	this.name = "";
	this.markerManager = new MarkerManager(MapController.map.gmap);
	this.markers = [];
	this.polylines = [];
	this.isVisible = true;
	var _this = this;
	
	this.addMarker = function(marker, zoom){
		this.markers.push(marker);
		this.markerManager.addMarker(marker, zoom);
		
		if(!this.isVisible)
			marker.hide();
	}
	
	this.addPolyline = function(polyline, zoom){
		this.polylines.push({polyline: polyline, zoom: zoom});
		MapController.map.gmap.addOverlay(polyline);
		
		if(zoom > MapController.map.getZoomLevel() || !this.isVisible)
			polyline.hide();
	}
	
	this.update = function(){
		if(!_this.isVisible){
			return;
		}
			
		for(var i=0; i<_this.polylines.length; i++){
			if(_this.polylines[i].zoom > MapController.map.getZoomLevel())
				_this.polylines[i].polyline.hide();
			else
				_this.polylines[i].polyline.show();
		}
	};
	
	GEvent.addListener(MapController.map.gmap, "zoomend", function() {
		_this.update();
	});
	
	this.hide = function(){
		for(var i=0; i<this.markers.length; i++){
			this.markers[i].hide();
		}
		for(var i=0; i<this.polylines.length; i++){
			this.polylines[i].polyline.hide();
		}
		this.isVisible = false;
	}
	
	this.show = function(){
		for(var i=0; i<this.markers.length; i++){
			this.markers[i].show();
		}
		for(var i=0; i<this.polylines.length; i++){
			if(this.polylines[i].zoom <= MapController.map.getZoomLevel())
				this.polylines[i].polyline.show();
		}
		this.isVisible = true;
	}
}

var MapUtil = new function(){

	this.distance = function(point1, point2){
		return point1.distanceFrom(point2);
	}
}

//
// well, welcome to teh hacks department
//

// Saving the position of the marker and then place it outside the map
GMarker.prototype.hide = function() {
  if (this.getPoint().lat() < 90) {
    //try {
        this.savePoint = this.getPoint();
        this.setPoint(new GLatLng(90, 0));
    //} catch (e) { }
  }
}

// Restoring the position of the marker
GMarker.prototype.show = function() {
  if (this.getPoint().lat() == 90) {
    if (this.savePoint) {
      //try {
        this.setPoint(this.savePoint);
        this.savePoint = null;
      //} catch (e) { }
    }
  }
}
