var Map = Class.create(
  {
    initialize: function(canvasDiv) 
    {
      this.map = new GMap2($(canvasDiv));
      this.bounds = new GLatLngBounds();
      this.map.addControl(new GSmallMapControl());
      this.addressPoints = new Array();
      this.addressPointIdCounter = 1;
      this.midpointMarker = null;
      this.setupDefaultMap();
    },

    setupDefaultMap: function()
    {
      var clientLocation = google.loader.ClientLocation;

      if (clientLocation == null)
      {
        // If google doesn't know the client's location
        // give it these defaults
        var latitude = 41.52;
        var longitude = -81.68;
      } else {
        // Otherwise, center around the client's location
        var latitude = clientLocation.latitude;
        var longitude = clientLocation.longitude;
      }

      this.setCenter(latitude,longitude, 10);
    },

    // Calculates the midpoint between all the address points
    // stored in TheMap.
    // It also checks to make sure we're near civilization
    // and plots the midpoint.
    calculateMidpoint: function()
    {
      var latAverage = 0;
      var lonAverage = 0;

      this.addressPoints.each(function(coordinate) 
      {
        latAverage += coordinate.latitude;
        lonAverage += coordinate.longitude;
      });

      var latitude = latAverage / this.addressPoints.length;
      var longitude = lonAverage / this.addressPoints.length;

      this.setMidpoint(latitude,longitude);

      // Adjust midpoint to make sure we're near civilzation.
      // Look for a pharmacy nearby the midpoint.
      this.adjustMidpointForCivilization();
    },

    // Adjusts and plots the midpoint by looking
    // for civilization
    adjustMidpointForCivilization: function()
    {
      var s = new Search();
      s.search('e', function(searcher) 
      {
        if (searcher.results.length != 0) {
          var finalLatitude = searcher.results[0].lat;
          var finalLongitude = searcher.results[0].lng;

          TheMap.setMidpoint(finalLatitude,finalLongitude);
        }
        TheMap.plotMidpoint();
      });
    },

    // Sets the center of the map to the given lat, lng
    setCenter: function(latitude, longitude, zoom)
    {
      var midpoint = new GLatLng(latitude, longitude);
      this.map.setCenter(midpoint, zoom); 
    },

    // Sets the centerpoint of the map to the midpoint
    centerOnMidpoint: function()
    {
      this.setCenter(this.midpoint.lat(), this.midpoint.lng());
    },

    // Assigns the midpoint instance variable to the given
    // latitude and longitude
    setMidpoint: function(latitude, longitude)
    {
      this.midpoint = new GLatLng(latitude, longitude);
    },

    getMidpoint: function()
    {
      return this.midpoint;
    },

    // Assigns the meetingplace instance variable to the
    // given latitude, longitude and search query
    setMeetingPlace: function(latitude, longitude, search_query)
    {
      this.meeting_place = {
        loc: new GLatLng(latitude, longitude),
        description: unescape(search_query)
      };
    },
    
    // Adds an address point into the instance variable array,
    // and returns the address point hash
    addAddressPoint: function(latitude, longitude, description, record_id)
    {
      var addressPoint = {
        latitude: latitude,
        longitude: longitude,
        description: description,
        id: this.addressPointIdCounter++,
        record_id: record_id
      };
      
      this.addressPoints.push(addressPoint);

      document.fire("map:addresspoint:add", addressPoint);
      
      return addressPoint;
    },

    // Returns the address point object specified by
    // the id
    getAddressPointById: function(id)
    {
      var retval = null;
      this.addressPoints.each(function(elm) {
        if (elm.id == id) {
          retval = elm;
        }
      });
      return retval;
    },

    // Adds and displays a marker on the map,
    // adjusts bounds, and returns the marker object
    addMarker: function(latitude, longitude, markerOptions)
    {
      var point = new GLatLng(latitude, longitude);
      var marker = new GMarker(point, markerOptions);

      this.map.addOverlay(marker);
      this.bounds.extend(point);

      return marker;
    },

    // Adds and displays a marker on the map with a custom icon,
    // adjusts bounds, and returns the marker object
    addIconMarker: function(latitude, longitude, iconURL, width, height, markerOptions)
    {
      // check for icon url, if we have one, use that instead of a marker
      var icon = new GIcon(G_DEFAULT_ICON);
      var markerOptions = $H({icon:icon}).merge(markerOptions).toObject();

      icon.image = iconURL;      
      icon.iconSize = new GSize(width,height);

      var point = new GLatLng(latitude, longitude);
      var marker = new GMarker(point, markerOptions);

      this.map.addOverlay(marker);
      this.bounds.extend(point);

      return marker;
    },

    // Plots the midpoint from the instance variable on the map
    // It DOES NOT remove an old midpoint
    plotMidpoint: function()
    {
      if (TheMap.midpointMarker != null) {
        this.map.removeOverlay(this.midpointMarker);
        this.midpointMarker = null;
      }
      
      var markerOptions = {
        draggable: true
      };

      var midpointMarker = this.addIconMarker(this.midpoint.lat(),this.midpoint.lng(), ROOT_URL+'images/midpointpin.png', 17, 30, markerOptions);
      TheMap.midpointMarker = midpointMarker;
      
      GEvent.addListener(midpointMarker, "dragstart", function() {
        TheMap.map.closeInfoWindow();
      });
      
      GEvent.addListener(midpointMarker, "dragend", function() {
        TheMap.midpoint = midpointMarker.getLatLng();
        document.fire("map:midpoint:dragend");
      });
      
      midpointMarker.bindInfoWindowHtml("<h3>Suggested Midpoint</h3><p>Select a category above for<br /> suggestions of places to meet.</p>");
      return midpointMarker;
    },
    
    // Removes the midpoint marker from the map
    // and setups the midpoint to null
    removeMidpoint: function()
    {
      if (this.midpointMarker) {
        this.removeOverlay(this.midpointMarker);
        this.midpointMarker = null;
      }
    },

    // Plots a meeting place if there is one
    plotMeetingPlace: function()
    {
      if (this.meeting_place == undefined || !this.meeting_place) return;
      var loc = this.meeting_place.loc;
      var qry = this.meeting_place.description + " @ " + loc.lat() + ", " + loc.lng();
      var marker = this.addIconMarker(loc.lat(), loc.lng(), ROOT_URL+'images/destpointpin.png', 17, 30);
      
      marker.bindInfoWindowHtml(this.meeting_place.description);
      return marker;
    },

    // Removes ALL markers from the map
    // and resets the bounds
    removeMarkers: function()
    {
      this.map.clearOverlays();
      this.clearBounds();
    },

    // Clears the map bounds
    clearBounds: function()
    {
      this.bounds = new GLatLngBounds();
    },

    // Draws all the address points and sets
    // the address point marker variables
    plotAddressPoints: function()
    {
      this.addressPoints.each(function(a) {
        a.marker = TheMap.addMarker(a.latitude, a.longitude);
        a.marker.bindInfoWindowHtml("<p><img src=\"/images/participant.png\" width=\"32\" height=\"29\" alt=\"Participant\" /></img> "+a.description+"</p><p style='text-align: center'><a href='javascript:TheMap.removeParticipant("+a.id+")' class='remove_participant_link'>Remove</a></p>");
      });
    },
    
    removeParticipant: function(id)
    {
      var addressPoint = this.getAddressPointById(id);
      this.map.removeOverlay(addressPoint.marker);
      var newArray = new Array();
      this.addressPoints.each(function(elm) {
        if (elm.id != id) newArray.push(elm);
      });
      
      this.addressPoints = newArray;

      document.fire("map:addresspoint:remove", addressPoint);
    },

    // Geocodes the given address
    // if lat/lon found, we call the callback with a GLatLng object
    geocodeAddress: function(address, callback)
    {
      geoCoder = new GClientGeocoder();
      geoCoder.getLatLng(address, callback);
    },

    zoomBounds: function(zoomOffset)
    {
      newzoom = this.map.getBoundsZoomLevel(this.bounds);

      // Don't let you zoom in too far, especially on a single point
      if (newzoom > 14) {
        newzoom = 14;
      }
      else {
        newzoom = newzoom - 1;
      }

      if (zoomOffset) {
        newzoom = newzoom - zoomOffset;
      }

      newcenter = this.bounds.getCenter();
      this.setCenter(newcenter.lat(), newcenter.lng(), newzoom);
    }

  }
);

var TheMap;

document.observe("dom:loaded", function() {
  
  if (GBrowserIsCompatible() && $('MapCanvas')) {
    TheMap = new Map('MapCanvas');
  }
  else {
    // TODO: Uh oh!  The users browser sucks too much to work.
  }
});
