function GReverseGeocoder(map) {
  this.map=map;
  this.gdirections = new GDirections();
  this.geocoder = new GClientGeocoder();
  this.lastpoint=null;
  this.closestonroad=null;
  this.experimental=false;
  this.ad="";
  this.step=10;
  this.start=1;
  this.gdirectionsrefine = new GDirections();  
  GEvent.bind(this.gdirections, "error", this, this.handleError);
  GEvent.bind(this.gdirections, "load", this, this.processDirection);
  GEvent.bind(this.gdirectionsrefine, "error", this, this.handleError);
  GEvent.bind(this.gdirectionsrefine, "load", this, this.processDirectionRefine);
}

GReverseGeocoder.prototype.reverseGeocode = function(point){
  this.lastpoint = point;
  this.closestonroad = null;
  this.gdirections.clear();
  this.gdirections.loadFromWaypoints([point.toUrlValue(6),point.toUrlValue(6)],{getSteps: true, getPolyline:true});
}

GReverseGeocoder.prototype.getStatus = function(){
  return this.gdirections.getStatus();
}

GReverseGeocoder.prototype.handleError = function(){
  GEvent.trigger(this, "error");
}

GReverseGeocoder.prototype.processDirection = function(){
  var source = this;
  if (this.gdirections.getPolyline() != null) {
    this.closestonroad=this.gdirections.getPolyline().getVertex(0);
  }
  
  if (this.gdirections.getNumRoutes() != 0 ){
    var street = this.getStreet(this.gdirections.getGeocode(0).address);
    var sw = new GLatLng(Number(this.lastpoint.lat()) - 0.01, Number(this.lastpoint.lng()) - 0.01);
    var ne = new GLatLng(Number(this.lastpoint.lat()) + 0.01, Number(this.lastpoint.lng()) + 0.01);
    var bounds = new GLatLngBounds(sw, ne);
    this.geocoder.setViewport(bounds);
    
    this.geocoder.getLocations(street,
      function(response){
        var placemark = source.getBestMatchingPlacemark(response);
        if(placemark != null){
          if(source.experimental){
            source.ad = placemark.address;
            source.step=10;
            source.start=1;
            source.houseNumberSearch();
          }
          else{
            GEvent.trigger(source, "load", placemark);
          }
        }
        else{
          source.handleError();
        }
      }
    );
    
  }
}

GReverseGeocoder.prototype.getBestMatchingPlacemark = function(response){
  if (!response || response.Status.code != 200) return null;
  var j = -1;
  var mindist = 1000000;
  for (var i = 0; i < response.Placemark.length; i++){
    var place = response.Placemark[i];
    var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
    var temp = this.lastpoint.distanceFrom(point);
    if (temp < mindist) {
      j = i;
      mindist = temp;
    }
  }
  if(j < 0 ) return null;
  response.Placemark[j].RequestPoint = {
    "coordinates":[this.lastpoint.lng(),this.lastpoint.lat()]
  }
  response.Placemark[j].PointOnRoad = {
      "coordinates":[this.closestonroad.lng(),this.closestonroad.lat()]
  }
  response.Placemark[j].Distance=mindist;
  response.Placemark[j].DistanceOnRoad=this.closestonroad.distanceFrom(new GLatLng(response.Placemark[j].Point.coordinates[1], response.Placemark[j].Point.coordinates[0]));
  return response.Placemark[j];
}

GReverseGeocoder.prototype.processDirectionRefine = function(){
  var nrgeocodes = this.gdirectionsrefine.getNumGeocodes();
  var j = -1;
  var mindist = 100;
  for (var i = 1; i < nrgeocodes; i++){
    var place = this.gdirectionsrefine.getGeocode(i);
    var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
    if (place.AddressDetails.Accuracy == 8){
      var temp = this.lastpoint.distanceFrom(point);
      if (temp < mindist) {
        j = i;
        mindist = temp;
      }	
    }
  }
  if(j < 0 ) {
    if ( this.start + (24 * this.step) < 2000){
    	this.start = this.start + (25 * this.step);
    	this.houseNumberSearch();
    }
    else {

    	this.handleError();
    }	
  }
  else {
    if (this.step == 1){
        var placemark = this.gdirectionsrefine.getGeocode(j);
        placemark.RequestPoint = {
          "coordinates":[this.lastpoint.lng(),this.lastpoint.lat()]
        }
        placemark.PointOnRoad = {
          "coordinates":[this.closestonroad.lng(),this.closestonroad.lat()]
        }
               
        placemark.Distance=mindist;
        placemark.DistanceOnRoad=this.closestonroad.distanceFrom(new GLatLng(placemark.Point.coordinates[1], placemark.Point.coordinates[0]));
     	GEvent.trigger(this, "load", placemark);
    }
    else{
    	var place = this.gdirectionsrefine.getGeocode(j);
    	var nr = this.start + (j * this.step);    	
	this.start = nr - 10;
	this.step = 1;
	this.houseNumberSearch();
    }
  }
}

GReverseGeocoder.prototype.houseNumberSearch = function(){
  this.gdirectionsrefine.clear();
  this.gdirectionsrefine.loadFromWaypoints([
  	("" + (this.start + (0 * this.step))  + " ") + this.ad,
  	("" + (this.start + (1 * this.step))  + " ") + this.ad,
  	("" + (this.start + (2 * this.step))  + " ") + this.ad,
  	("" + (this.start + (3 * this.step))  + " ") + this.ad,
  	("" + (this.start + (4 * this.step))  + " ") + this.ad,
  	("" + (this.start + (5 * this.step))  + " ") + this.ad,
  	("" + (this.start + (6 * this.step))  + " ") + this.ad,
  	("" + (this.start + (7 * this.step))  + " ") + this.ad,
  	("" + (this.start + (8 * this.step))  + " ") + this.ad,
  	("" + (this.start + (9 * this.step))  + " ") + this.ad,
  	("" + (this.start + (10 * this.step))  + " ") + this.ad,
  	("" + (this.start + (11 * this.step))  + " ") + this.ad,
  	("" + (this.start + (12 * this.step))  + " ") + this.ad,
  	("" + (this.start + (13 * this.step))  + " ") + this.ad,
  	("" + (this.start + (14 * this.step))  + " ") + this.ad,
  	("" + (this.start + (15 * this.step))  + " ") + this.ad,
  	("" + (this.start + (16 * this.step))  + " ") + this.ad,
  	("" + (this.start + (17 * this.step))  + " ") + this.ad,
  	("" + (this.start + (18 * this.step))  + " ") + this.ad,
  	("" + (this.start + (19 * this.step))  + " ") + this.ad,
  	("" + (this.start + (20 * this.step))  + " ") + this.ad,
  	("" + (this.start + (21 * this.step))  + " ") + this.ad,
  	("" + (this.start + (22 * this.step))  + " ") + this.ad,
  	("" + (this.start + (23 * this.step))  + " ") + this.ad,
  	("" + (this.start + (24 * this.step))  + " ") + this.ad
  ],{getSteps: true});
}


GReverseGeocoder.prototype.getStreet = function(street){
  if(street == null) return null;
  if((street != null) && (street.indexOf("/") > 0)) {
    street = street.split("/");
    if (isNaN(street[0].charAt(1)) ){
      street = street[0];
    }
    else{
      street = street[1];
    }
  }
  return street;
}

GReverseGeocoder.prototype.setExperimentalHouseNumber = function (setting){
  this.experimental = setting;
}

GReverseGeocoder.prototype.getPlacemarkProperty = function (placemark,propertyname){
  for (var property in placemark) {
    if((property == propertyname)) {
      return String(placemark[property]);
    } else if (typeof(placemark[property]) == 'object') {
      var r = this.getPlacemarkProperty(placemark[property], propertyname);
      if (r != null) return r;
    }
  }
  return null;
}