/*


h1. Google Maps Wrapper

*Overview:*

This class is used to plot latitude and longitude points onto an interactive Google Map

*Dependencies:*

* This class requires "Prototype 1.6":/http://prototypejs.org/ (http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js) to run.
* The Google Maps javascript API file and key must be in the head of your HTML document. Refer to the Google Maps API documentation. Generate your own key: "http://code.google.com/apis/maps/signup.html":http://code.google.com/apis/maps/signup.html

*Usage:*

The class will execute automatically on the 'dom:loaded' event. If you do not want this to happen,
you can either set the 'embedEvent' to false or specify a custom event.


*/

var GoogleMap = Class.create({
  
  initialize: function(id, options){
    this.id = id;
    this.mapId = id;
    this.defaultOptions = {
      height: 400,
      width: 400,
      zoom: 10,
      setCenterLatitude: true,
      setCenterLongitude: true,
      controls: [new GLargeMapControl(), new GMapTypeControl()],
      infoWindowTemplate: false,
      markerOptions: [],
      data: {},
      embedMethod: ['update'],
      embedEvent: 'dom:loaded'
    };
    this.options = $H(this.defaultOptions).merge(options);
    this.divStyles = {
      height: this.options.get('height') + 'px',
      width:  this.options.get('width')  + 'px'
    };
    this.markerOptions = this.options.get('markerOptions');
    this.data = $H(this.options.get('data'));
    this.infoWindowTemplate = this.options.get('infoWindowTemplate');
    if (this.options.get('embedMethod').last())
      this.insertPosition = this.options.get('embedMethod').last();
        
    if (this.options.get('embedEvent')) {
      document.observe(this.options.get('embedEvent'), this.setup.bind(this));
    } else {
      this.setup();
    }
  },
  
  setup: function(){
    this.root = $(this.id);
    if (!this.root) return;
    this.tryToInsertElement();
    this.addMap();
    this.addControls();
    this.addPoints();
    Event.observe(window, 'unload', GUnload);
  },
  
  tryToInsertElement: function(){
    // if inserting a new element, insert it
    if (this.options.get('embedMethod').first() != 'insert') return;
    this.mapsDiv = new Element('div', { id: 'GoogleMapsCreatedDiv' }).setStyle(this.divStyles);
    var div = new Object();
    div[this.insertPosition] = this.mapsDiv;
    this.root.insert(div);
    this.mapId = 'GoogleMapsCreatedDiv';
  },
  
  addMap: function(){
    if (!GBrowserIsCompatible()) return;
    this.map = new GMap2($(this.mapId));
    this.determineCenter();
    this.map.setCenter(new GLatLng(this.setCenterLatitude, this.setCenterLongitude), this.options.get('zoom'));
  },
  
  determineCenter: function(){
    // if supplied coordinates, use them
    if (Object.isNumber(this.options.get('setCenterLatitude')) && Object.isNumber(this.options.get('setCenterLongitude'))) {
      this.setCenterLatitude  = this.options.get('setCenterLatitude');
      this.setCenterLongitude = this.options.get('setCenterLongitude');
    } else {
      function getPropertyAsNumber(property, point){
        if (point.value[property]) return Number(point.value[property]);
      };
      var latitudes  = this.data.collect(getPropertyAsNumber.curry('latitude'));
      var longitudes = this.data.collect(getPropertyAsNumber.curry('longitude'));
      this.setCenterLatitude  = (latitudes.max() + latitudes.min()) / 2;
      this.setCenterLongitude = (longitudes.max() + longitudes.min()) / 2;
    }
  },
  
  addControls: function(){
    this.options.get('controls').each(function(control, i){
      this.map.addControl(control);
    }.bind(this));
  },
  
  addPoints: function(){
    this.data.each(function(point, i){
      if (point.value.latitude && point.value.longitude) {
        var p = new GLatLng(Number(point.value.latitude), Number(point.value.longitude));
        var markerOptions = point.value.iconIndex ? this.markerOptions[Number(point.value.iconIndex)] : {};
        var marker = new GMarker(p, markerOptions);
        if (this.infoWindowTemplate) {
          var template = this.infoWindowTemplate.evaluate(point.value);
          GEvent.addListener(marker, 'click', marker.openInfoWindowHtml.curry(template));
        }
        this.map.addOverlay(marker);
      }
    }.bind(this));
  },
  
  updatePoints: function(json) {
    this.map.clearOverlays();
    json.each(function(point, i){
      if (point.latitude && point.longitude) {
        var p = new GLatLng(Number(point.latitude), Number(point.longitude));
        var marker = new GMarker(p);
        if (this.infoWindowTemplate) {
          var template = this.infoWindowTemplate.evaluate(point);
          GEvent.addListener(marker, 'click', marker.openInfoWindowHtml.curry(template));
        }
        this.map.addOverlay(marker);
      }
    }.bind(this));
  },
  
  clearPoints: function(){
    this.map.clearOverlays();
  },
  
  sum: function(acc, n){ return acc + n; }
  
});

/*


This class has an additional url parameter that is used to request the JSON option data

  Example Returned Data from Request:
    
    {
    'Pensacola': {
    'name':'Pensacola',
    'latitude':'30.477379',
    'longitude':'-87.252216',
    'fax':'555-555-5555'
    },

    'Cordova Branch': {
    'name':'Cordova Branch',
    'latitude':'30.471466',
    'longitude':'-87.208577',
    'fax':'555-555-5555'
    }
    }
  
*/
var AjaxGoogleMap = Class.create(GoogleMap, {
  
  initialize: function($super, url, id, options){
    this.$super = $super;
    this.url = url;
    this.id = id;
    this.forcedOptions = {
      embedEvent: false
    };
    this.options = $H(options).update(this.forcedOptions);
    document.observe('dom:loaded', function() {
      this.request = new Ajax.Request(this.url, {
        method: 'get',
        onComplete: this.processRequestAsJSON.bind(this)
      });
    }.bind(this));
  },
  
  processRequestAsJSON: function(response){
    this.text = new String(response.responseText).gsub(/\n/, '').gsub('},}', '}}'); // try to clean up malformed JSON
    this.json = this.text.evalJSON();
    this.blah = this.$super(this.id, this.options.update({ data: this.json }));
  },
  
  updateWithJSON: function(json) {    
  }
  
});


