App.Map = Class.create({
  initialize : function(element, options){
    this.element = $(element);
    this.element.addClassName('ui-map-editor');
    
    this.options = Object.extend({
      url : '/webadmin/#{controller}/#{action}/#{id}.json',
      postBody : 'data[#{model}][#{field}]=#{value}',
      action : 'save_field',
      height : 325,
      width: 640,
      marker_image_url : '/img/site/layout/pin.png',
      marker_image_width : 20,
      marker_image_height : 34,
      model : 'Destination'
    }, options||{});
    
    this.url = new Template(this.options.url);
    this.postBody = new Template(this.options.postBody);
    
    this.requests = 0;
    this.geo = {};
    this.markers = $A();
      
    this.map = new GMap2(this.element);
    //this.map.setMapType(G_PHYSICAL_MAP)
    
    this.map.disableDragging();
    this.map.disableDoubleClickZoom();
    this.callbacks = 0;
    this.center();
  },
  center : function(){
    if(this.options.map_latitude && this.options.map_longitude && this.options.zoom){
      this.setCenter(this.getPoint(this.options.map_latitude, this.options.map_longitude), this.options.zoom);
    }else if(this.options.address){
      this.getLocation(this.options.address, this.options.id, function(point, zoom){
        this.setCenter(point, zoom);
        this.save();
      });
    }
  },
  getLocation : function(address, id, callback){    
    var c = callback.wrap(function(proceed, data){
      try{
        var bounds = this.getBoundsFromWOEID(data);    
        var point = bounds.getCenter();
        var zoom = this.map.getBoundsZoomLevel(bounds);
        proceed(point, zoom);
      }catch(e){}
    });
    getJSON('http://where.yahooapis.com/v1/places.q(%27'+escape(address)+'%27)?format=json&appid=0hAohbDV34FSLcpx6KAOLgTzV28ZrEc57SP1IiD2phqfyv9hr_3LoBYQkMjLlOn3oXg-&callback=?', c.bind(this));
  },
  getBoundsFromWOEID : function(data){
    var place = data.places.place[0];
    var southWest = place.boundingBox.southWest;
    var northEast = place.boundingBox.northEast;
    var bounds = new GLatLngBounds(new GLatLng(southWest.latitude, southWest.longitude), new GLatLng(northEast.latitude, northEast.longitude));
    return bounds;
  },
  getPoint : function(latitude, longitude){
    return new GLatLng(latitude, longitude, true);
  },
  setCenter : function(point, zoom){
    this.map.setCenter(point, parseInt(zoom, 10));
    this.setMapLatLong(point);
    this.setZoom(zoom);  
  },
  setMapLatLong : function(point){
    this.geo.map_lat = point.y;
    this.geo.map_long = point.x;
  },
  setZoom : function(zoom){
    this.geo.zoom = this.map.getZoom();
  },
  onMove : function(){
    this.setMapLatLong(this.map.getCenter());
  },
  onZoom : function(){
    this.setZoom();
  },
  save : function(){    
    new Ajax.Request(this.url.evaluate(this.options), {
      method : 'post',
      postBody : this.postBody.evaluate({
        field : 'map_latitude',
        value : this.geo.map_lat,
        model : this.options.model
      })
    });
    
    new Ajax.Request(this.url.evaluate(this.options), {
      method : 'post',
      postBody : this.postBody.evaluate({
        field : 'map_longitude',
        value : this.geo.map_long,
        model : this.options.model
      })
    });
            
    new Ajax.Request(this.url.evaluate(this.options), {
      method : 'post',
      postBody : this.postBody.evaluate({
        field : 'zoom_level',
        value : this.geo.zoom,
        model : this.options.model
      })
    });
  },
  addMarker : function(options){
    if(options.latitude && options.longitude){
      this.markers.push(new App.MapMarker(this.map, options, this.getPoint(options.latitude, options.longitude)));
    }else if(options.address){
      this.getLocation(options.address, options.id, function(p, z){
        var m = new App.MapMarker(this.map, options, p);
        m.save();
        this.markers.push(m);
      });     
    }
  },
  addPath : function(points){    
    this.points = $A();
    points = $A(points);
    var size = points.size();
    points.each(function(point, i){
      if(point.draggable){
        point.onDrag = this.updatePath.bind(this);
      }      
      point.first = i==0;
      point.last = i==size-1;
      point.order = i;
      if(point.latitude && point.longitude){
        var p = this.getPoint(point.latitude, point.longitude);
        var m = new App.MapMarker(this.map, point, p);
        this.points.push(m);
      }else{
        this.getLocation(point.address, point.id, function(p, z){  
          var m = new App.MapMarker(this.map, point, p);
          m.save();
          this.points.push(m);
          if(this.points.size()==size){
            this.plotPath();
          }
        });
      }
    }, this);  
    if(this.points.size()==size){
      this.plotPath();
    }  
  },
  plotPath : function(){  
    this.points = this.points.sortBy(function(point){
      return point.options.order;
    });
    this.updatePath();
  },
  updatePath : function(){          
    this.polyline = new GPolyline(this.points.pluck('point'), "#FF0000", 10);
    this.map.addOverlay(this.polyline);      
  }
});

App.MapMarker = Class.create({
  initialize : function(map, options, point){
    this.map = map;
    this.point = point;
    this.options = Object.extend({
      _url : '/webadmin/#{controller}/#{action}/#{id}.json',
      postBody : 'data[#{model}][#{field}]=#{value}',
      action : 'save_field',
      draggable : false,
      onDrag : Prototype.emptyFunction,
      model : 'Destination',
      marker_image_url : '/img/site/layout/pin.png',
      marker_image_width : 28,
      marker_image_height : 21
    }, options||{});
    this.geo = {};
    this.url = new Template(this.options._url);
    this.postBody = new Template(this.options.postBody);
    
    // /* This section of code is lame, need a much better way of specifying custom icons */
    // var icon = new GIcon();
    // var image = (this.options.draggable) ? 'red_marker' : 'blue_marker';
    // if(this.options.first){
    //   image+='_start';
    //   icon.size = new GSize(54, 48);
    //   icon.iconAnchor = new GPoint(27, 48);
    // }else if(this.options.last){
    //   image+='_finish';
    //   icon.size = new GSize(54, 48);
    //   icon.iconAnchor = new GPoint(27, 48);
    // }else{
    //   icon.size = new GSize(20, 34);
    //   icon.iconAnchor = new GPoint(10, 34);
    // }
    // icon.image = '/framework/themes/webadmin/images/'+image+'.png';
    // icon.infoWindowAnchor = new GPoint(0, 0);
    // /* End lame code */
    // 
    // var marker = new GMarker(point, {icon : icon, draggable : this.options.draggable});
    // this.map.addOverlay(marker);   
    
    
    var icon = new GIcon();
    icon.image = this.options.marker_image_url;
    icon.size = new GSize(this.options.marker_image_width, this.options.marker_image_height);
    icon.iconAnchor = new GPoint(2, 20);
    icon.infoWindowAnchor = new GPoint(0, 0);
    
    var marker = new LabeledMarker(point, {
      'icon' : icon,
      'labelText' : options.address,
      'labelOffset': new GSize(-16, -16)      
    });
    this.map.addOverlay(marker);
    marker.div.style.visibility = 'hidden';

    GEvent.addListener(marker, 'mouseover', function(){
      this.div.style.visibility = 'visible';
    });

    GEvent.addListener(marker, 'mouseout', function(){
      this.div.style.visibility = 'hidden';
    });

    if(options.url){
      GEvent.addListener(marker, 'click', function(){
        window.location = options.url;
      });
    }
    
    
  },
  onSave : function(){
    if(this.options.draggable){
      this.save();
    }
  },
  save : function(){
    if(this.url){
      new Ajax.Request(this.url.evaluate(this.options), {
        method : 'post',
        postBody : this.postBody.evaluate({
          field : 'latitude',
          value : this.point.y,
          model : this.options.model
        })
      });
    
      new Ajax.Request(this.url.evaluate(this.options), {
        method : 'post',
        postBody : this.postBody.evaluate({
          field : 'longitude',
          value : this.point.x,
          model : this.options.model
        })
      });
    }
  }
});