/*


Google Maps Javascript Class

  *Overview:*
  
  This class is used to plot latitude and longitude points onto an interactive Google Map
  
  *Dependencies:*
  
  This class requires Prototype 1.6 (http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/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.

  *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 GoogleMaps = 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()],
      infoWindows: false,
      infoWindowTemplate: false,
      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.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') {
      this.mapsDiv = new Element('div', { id: 'GoogleMapsCreatedDiv' }).setStyle(this.divStyles);
      if (this.insertPosition == 'top')    this.root.insert({ top:    this.mapsDiv });
      if (this.insertPosition == 'bottom') this.root.insert({ bottom: this.mapsDiv });
      if (this.insertPosition == 'before') this.root.insert({ before: this.mapsDiv });
      if (this.insertPosition == 'after')  this.root.insert({ after:  this.mapsDiv });
      this.mapId = 'GoogleMapsCreatedDiv';
    }
  },
  
  addMap: function(){
    if (GBrowserIsCompatible()) {
      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 {
      var getPropertyAsNumber = function(property, point, i){
        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 marker = new GMarker(p);
        if (this.infoWindowTemplate) {
          var template = this.infoWindowTemplate.evaluate(point.value);
          GEvent.addListener(marker, 'click', marker.openInfoWindowHtml.curry(template));
        }
        this.map.addOverlay(marker);
      }
    }.bind(this));
  },
  
  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':'850-477-1803'
    },

    'Cordova Branch': {
    'name':'Cordova Branch',
    'latitude':'30.471466',
    'longitude':'-87.208577',
    'fax':'850-478-1578'
    },
    }
  
*/
var AjaxGoogleMaps = Class.create(GoogleMaps, {
  
  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);
    this.request = new Ajax.Request(this.url, {
      method: 'get',
      onComplete: this.processRequestAsJSON.bind(this)
    });
  },
  
  processRequestAsJSON: function(response){
    this.text = new String(response.responseText).gsub(/\n/, '').gsub('},}', '}}');
    // cl(this.text);
    this.json = this.text.evalJSON();
    this.$super(this.id, this.options.update({ data: this.json }));
  }
  
});

