  var latLng;
  var mapOptions;
  var map;
  var geocoder;
  var centerChangedLast;
  var reverseGeocodedLast;
  var currentGeocodeString;
  var currentReverseGeocodeResponse;
  var mapInitialized = false;
  var marker = false;
  
  function initMap(o) {
    latLng = new google.maps.LatLng(o.lat,o.lng);
    mapOptions = {
		    zoom: 5,
		    center: latLng,
		    scrollwheel: false,
		    mapTypeControl:false,
		    mapTypeId: google.maps.MapTypeId.ROADMAP
		 };
    $.extend(true,mapOptions,o);
    
    map = new google.maps.Map(document.getElementById(o.canvasID), mapOptions);
    
    if (map.hide) {
	google.maps.event.addListener(map, 'tilesloaded', hideOnInit);
    }
    
    if (mapOptions.marker && mapOptions.marker.lat && mapOptions.marker.lng) {
	var markerLatLng = new google.maps.LatLng(mapOptions.marker.lat,mapOptions.marker.lng);
	maker = initMarker(markerLatLng,mapOptions.marker);
    }

    if (mapOptions.geocode) {
	geocoder = new google.maps.Geocoder();
	
	// setup Events
	reverseGeocodedLast = new Date();
	centerChangedLast = new Date();
    
	/*
	setInterval(function() {
	  if((new Date()).getSeconds() - centerChangedLast.getSeconds() > 1) {
	    if(reverseGeocodedLast.getTime() < centerChangedLast.getTime())
	      reverseGeocode();
	  }
	}, 1000);
	*/
	/*
	google.maps.event.addListener(map, 'zoom_changed', function() {
	  //document.getElementById("zoom_level").innerHTML = map.getZoom();
	});
    
	google.maps.event.addListener(map, 'center_changed', centerChanged);
	*/
	/* cross hear in the center
	google.maps.event.addDomListener(document.getElementById('crosshair'),'dblclick', function() {
	   map.setZoom(map.getZoom() + 1);
	});
	*/
    
	//centerChanged();
    }
  }

  function hideOnInit() {
    if (!mapInitialized) {
	mapInitialized = true;
	$(map.getDiv()).height(0);
	
    }
  }
  
  function mapResized() {
	//alert("map resize");
  }

  function getCenterLatLngText() {
    return '(' + map.getCenter().lat() +', '+ map.getCenter().lng() +')';
  }

  function centerChanged() {
    centerChangedLast = new Date();
    var latlng = getCenterLatLngText();
    //document.getElementById('latlng').innerHTML = latlng;
    //document.getElementById('formatedAddress').innerHTML = '';
    currentReverseGeocodeResponse = null;
  }

  function reverseGeocode() {
    reverseGeocodedLast = new Date();
    geocoder.geocode({latLng:map.getCenter()},reverseGeocodeResult);
  }

  function reverseGeocodeResult(results, status) {
    currentReverseGeocodeResponse = results;
    /*
    if(status == 'OK') {
      if(results.length == 0) {
        document.getElementById('formatedAddress').innerHTML = 'None';
      } else {
        document.getElementById('formatedAddress').innerHTML = results[0].formatted_address;
      }
    } else {
      document.getElementById('formatedAddress').innerHTML = 'Error';
    }
    */
  }

  function writeMakerPos(marker_pos,o) {
    $("#block2-location_lat").val(marker_pos.lat());
    $("#block2-location_lng").val(marker_pos.lng());
    //$("#block2-location_mapsettings").val(o.serialize());
    $("#block2-location_mapsettings").val(JSON.stringify(o));
    
  }

  function completeAddress(r) {
    //console.debug(r);
    //alert(r.postal_code + " " + r.locality + " " + r.country);
    // address_components : {long_name: str, types[0] == "locality" | "postal_code"}
    for ( var i in r.address_components ) {
	if ('locality' == r.address_components[i].types[0] && "" == $("#block2-location_city").val() ) {
            $("#block2-location_city").val(r.address_components[i].long_name);
	    
	}
	if ('postal_code' == r.address_components[i].types[0] && "" == $("#block2-location_zip").val() ) {
            $("#block2-location_zip").val(r.address_components[i].long_name);
	    
	}
	
    }
  }

  function geocode(e) {
    var address = $(e.data.address).val()
		  + ", " + $(e.data.zip).val() + " " + $(e.data.city).val()
		  + ", " + $(e.data.country).val();
    //console.debug(address);
    if (currentGeocodeString != address) {
	currentGeocodeString = address;
	geocoder.geocode({
	  'address': address,
	  'partialmatch': true}, geocodeResult);
    }
  }

  function geocodeResult(results, status) {
    //console.debug(results);
    //alert("status: " + status + " results.length: " + results.length);
    if (status == 'OK' && results.length > 0) {
      map.fitBounds(results[0].geometry.viewport);
      completeAddress(results[0]);
      if (marker) {
	//console.debug('updating marker position');
	//alert('updating marker position');
	marker.setPosition(map.getCenter())
      } else {
	//console.debug('creating marker');
	//alert('creating marker');
	initMarker(map.getCenter(),mapOptions.marker);
      }
      writeMakerPos(map.getCenter(),{zoom: map.getZoom()});
    }
    /* else {
      //alert("Geocode was not successful for the following reason: " + status);
    }
    */
  }

  function addMarkerAtCenter() {
    var marker = new google.maps.Marker({
        position: map.getCenter(),
        map: map
    });

    var text = 'Lat/Lng: ' + getCenterLatLngText();
    if(currentReverseGeocodeResponse) {
      var addr = '';
      if(currentReverseGeocodeResponse.size == 0) {
        addr = 'None';
      } else {
        addr = currentReverseGeocodeResponse[0].formatted_address;
      }
      text = text + '<br>' + 'address: <br>' + addr;
    }

    var infowindow = new google.maps.InfoWindow({ content: text });

    google.maps.event.addListener(marker, 'click', function() {
      infowindow.open(map,marker);
    });
  }

  function initMarker(latLng,param) {
    if (!param)
	param = {} ;
    if (!param.title)
	param.title = "Marker";
    if (!param.draggable)
	param.draggable = false;
	
    marker = new google.maps.Marker({
      position: latLng,
      title: param.title,
      map: map,
      draggable: param.draggable
    });
    
    // Update current position info.
    // updateMarkerPosition(latLng);
    // geocodePosition(latLng);
    
    // Add dragging event listeners.
    /*
    google.maps.event.addListener(marker, 'dragstart', function() {
      updateMarkerAddress('Dragging...');
    });
    */
    
    /* not needed either
    google.maps.event.addListener(marker, 'drag', function() {
      //updateMarkerStatus('Dragging...');
      //updateMarkerPosition(marker.get_position());
    });
    */
    
    google.maps.event.addListener(marker, 'dragend', function() {
      //updateMarkerStatus('Drag ended');
      // => to adapt, save latLng; geocodePosition(marker.get_position());
      writeMakerPos(marker.getPosition(),{zoom : map.getZoom()});
    });
  }
  