QYPE.MapExplorer = function(url, searchTerm, placeId, catIds, params) {
  this.url = url;
  this.searchTerm = searchTerm;
  this.placeId = placeId;
  this.catIds = catIds;

  this.numMarkers = 10;
  this.locator = '';

  this.searchToggle = null;

  // Request queue for this.loadNextPoints()
  this.requestQueue = [];
  this.lastRequest = 0;

  this.controlType = (params && params.type == 'category') ? 'category' : '';
  this.placesListing = (this.controlType == 'category') ? Selector.query('.category-listing', null, true) : Dom.get('result_list');

  if (this.controlType == 'category') {
    // On the category page, just use the existing map instance
    GMapDetail = QypeMap;
  } else {
    // Map explorer page - init the map
    GMapDetail.init('Big');
  }

  GMapDetail.clear();

  this.spinner = new QYPE.layout.OverlaySpinner(this.placesListing);

  this.searchButton = Selector.query('#the_map .search_part button', null, true);

  this.setListeners();
};

QYPE.MapExplorer.prototype = {

  setListeners: function() {
    var links = Selector.query('a.PerPageLink');

    for (var i=0; i<links.length; i++) {
      Event.addListener(links[i], 'click', function(e) {
        Event.preventDefault(e);

        var link = Event.getTarget(e);
        for (var j=0; j<links.length; j++) {
          Dom.removeClass(links[j], 'strong');
        }
        Dom.addClass(link, 'strong');
        this.setMarkersPerPage(link.title);
        this.setSearchTerm(Dom.get('map_explore_search_term').value);
        this.loadNextPoints(link.title);
        link = null;
      }, null, this);
    }

    Event.addListener(this.searchButton, 'click', function(e) {
      Event.preventDefault(e);
      this.performSearch();
    }, null, this);
  },

  performSearch: function(params) {
    this.setSearchTerm(Dom.get('map_explore_search_term').value);
    if (Dom.get('map_explore_locator')) {
      // value attribute for button tag in IE is replaced with innerHTML
      // using path attribute instead
      var path = (window.MapExplorerData && window.MapExplorerData.locator_domain_id) ?
                 '/' + window.MapExplorerData.locator_domain_id + '/explore/show_async' :
                 this.searchButton.getAttribute('path');
      var url = path + '?search_term=' + this.searchTerm + '&locator=' + Dom.get('map_explore_locator').value;
      this.clear({mapOnly: true});
      this.spinner.show();
      this.disableMapListeners();

      var self = this;
      YAHOO.util.Connect.initHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
      YAHOO.util.Connect.asyncRequest('GET', url, {
        success: function(response) {
          self.spinner.hide();
          eval(response.responseText);
          self.areaExplorer.getArea();
          if (!params || (params && params.centerAndZoom === true)) {
            GMapDetail.centerAndZoom();
          }
          self.enableMapListeners();
        },
        failure: function(response) {
          self.spinner.hide();
        }
      });
    } else {
      this.loadNextPoints(this.getMarkersPerPage());
    }
  },

  afterInit: function() {
    // Trigger the Find button click when Enter is pressed
    var locators = Selector.query('#map_explore_search_term, #map_explore_locator');

    for (var i=0; i<locators.length; i++) {
      Event.addListener(locators[i], 'keyup', function(e) {
        if (e.keyCode == 10 || e.keyCode == 13)
          this.searchButton.click();
      }, null, this);
    }

    this.searchToggle = Dom.get('map_explore_follow');

    this.searchToggle && Event.addListener(this.searchToggle, 'click', function() {
      this.toggleMapListeners();
    }, null, this);

    this.toggleMapListeners();

    this.areaExplorer = new QYPE.AreaExplorer(this);
  },

  clear: function(opts) {
    GMapDetail.clear();
    if (opts && opts.mapOnly === false || !opts) this.placesListing.innerHTML = "&nbsp;";
  },

  // XXX this and the following one are essentially the same
  centerAndZoom: function() {
    GMapDetail.centerAndZoom();
  },

  toggleMapListeners: function() {
    if (this.searchToggle) {
      if (this.searchToggle.checked) {
        this.enableMapListeners();
      } else {
        this.disableMapListeners();
      }
    } else {
      this.enableMapListeners();
    }
  },

  enableMapListeners: function() {
    var self = this;

    this.drag_listener = google.maps.event.addListener(GMapDetail.map(), "dragend", function() {
      if (self.controlType != 'category') {
        self.loadNextPoints(self.getMarkersPerPage());
      }
    });

    this.tiles_loaded_listener = google.maps.event.addListener(GMapDetail.map(), "tilesloaded", function() {
      self.zoom_changed_listener = google.maps.event.addListener(GMapDetail.map(), "zoom_changed", function() {
        if (self.controlType != 'category') {
          self.loadNextPoints(self.getMarkersPerPage());
        }
      });
    });
  },

  disableMapListeners: function() {
    this.drag_listener && google.maps.event.removeListener(this.drag_listener);
    this.tiles_loaded_listener && google.maps.event.removeListener(this.tiles_loaded_listener);
    this.zoom_changed_listener && google.maps.event.removeListener(this.zoom_changed_listener);
  },

  loadNextPoints: function(markersPerPage, additionalParams, callback) {
    // Simple request queue - in case of repeating drag-stop interaction,
    // only execute the first and the last request from the queue.
    this.requestQueue.push(markersPerPage);

    if (this.requestQueue.length > 1) return;

    this.clear();
    this.spinner.show();

    var swlat = (this.areaExplorer.area_bounds) ? this.areaExplorer.area_bounds.getSouthWest().lat() : GMapDetail.map().getBounds().getSouthWest().lat();
    var swlng = (this.areaExplorer.area_bounds) ? this.areaExplorer.area_bounds.getSouthWest().lng() : GMapDetail.map().getBounds().getSouthWest().lng();
    var nelat = (this.areaExplorer.area_bounds) ? this.areaExplorer.area_bounds.getNorthEast().lat() : GMapDetail.map().getBounds().getNorthEast().lat();
    var nelng = (this.areaExplorer.area_bounds) ? this.areaExplorer.area_bounds.getNorthEast().lng() : GMapDetail.map().getBounds().getNorthEast().lng();

    var params = [];

    params.push('swlat=' + swlat);
    params.push('swlng=' + swlng);
    params.push('nelat=' + nelat);
    params.push('nelng=' + nelng);
    params.push('per_page=' + markersPerPage);

    if (this.searchTerm)
      params.push('search_term=' + this.searchTerm);
    if (this.placeId)
      params.push('place_id=' + this.placeId);
    if (this.catIds)
      params.push('cat_ids=' + this.catIds);

    var self = this;
    YAHOO.util.Connect.initHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
    YAHOO.util.Connect.asyncRequest('POST', this.url, {
      success: function(response) {
        self.spinner.hide();
        eval(response.responseText);
        self.areaExplorer.getArea();
        callback && callback();
        // Manage the request queue
        self.lastRequest++;
        if (self.requestQueue.length > self.lastRequest) {
          self.requestQueue = [];
          self.lastRequest = 0;
          self.loadNextPoints(self.requestQueue[self.requestQueue.length - 1]);
        } else {
          self.requestQueue = [];
        }
      },
      failure: function() {
        self.spinner.hide();
      }
    }, params.join("&") + ((additionalParams) ? "&" + additionalParams : ''));
  },

  getMarkersPerPage: function() {
    return this.numMarkers;
  },

  setMarkersPerPage: function(num) {
    this.numMarkers = num;
  },

  setSearchTerm: function(term) {
    this.searchTerm = term;
  }
};

// This class extends the class QypeMap
var GMapDetail = {};

// Populate GMapDetail with QypeMap properties
for (var property in QypeMap) {
  GMapDetail[property] = QypeMap[property];
}

var GMapDetailMethods = {
  markersByPlaceId: [],
  max_zoom: 17,
  highlighted_marker: null,
  active_marker: null,

  setMapComplex: function(marker, map) {
    marker.setMap(map);
    marker.icon.setMap(map);
  },

  addPlace: function(html, url, latitude, longitude, place_id, grayed, image, place_id_short) {
    var marker;
    var point = new google.maps.LatLng(latitude, longitude);

    var isFriendReviewedPlace = false;
    var friendReviewedPlaces = (window.friend_reviewed_place_ids) ? window.friend_reviewed_place_ids.split(',') : [];

    for (var i = 0; i<friendReviewedPlaces.length; i++) {
      if (friendReviewedPlaces[i] == place_id_short) {
        isFriendReviewedPlace = true;
      }
    }

    if (grayed == 'true') {
      marker = this.createGrayedMarker(point, html, url, isFriendReviewedPlace);
    } else if (image != '') {
      marker = this.createBrandedMarker(point, html, url, image);
    } else {
      marker = this.createMarker(point, html, url, isFriendReviewedPlace);
    }

    this.markersCollection.push(marker);
    this.markersByPlaceId[place_id + ''] = marker;
    this.bounds().extend(point);
  },

  highlightPlace: function(place_id) {
    var marker = this.markersByPlaceId[place_id + ''];

    if (marker) {
      this.highlighted_marker = this.createJumper(marker.point, '', '');
      this.setMapComplex(this.highlighted_marker, this.map());
      this.active_marker = marker;
      this.setMapComplex(marker, null);
    }
  },

  unhighlightPlace: function() {
    if (this.highlighted_marker) {
      this.setMapComplex(this.highlighted_marker, null);
      this.highlighted_marker = null;
    }

    if (this.active_marker) {
      this.setMapComplex(this.active_marker, this.map());
      this.active_marker = null;
    }
  },

  createJumper: function(point, html, url) {
    var marker = new google.maps.Marker({
      position: point,
      map: this.map(),
      icon: QYPE.gmap.GOOGLE_JUMPER
    });

    var infoOverlay = new GxMarker(point, marker, html, this.map());

    this.createMarkerListeners(marker, infoOverlay);

    if (url)
      google.maps.event.addListener(marker, "click", function() {
        window.location = url;
      });

    return infoOverlay;
  },

  createGreenIcon: function() {
    return new google.maps.Marker({
      position: point,
      map: this.map(),
      icon: QYPE.gmap.GOOGLE_MARKER_PNG
    });
  },

  createGreyIcon: function() {
    return new google.maps.Marker({
      position: point,
      map: this.map(),
      icon: QYPE.gmap.GOOGLE_GREYED_MARKER_PNG
    });
  },

  clear: function() {
    this.clearOverlays();
    this._bounds = null;
    this.markersByPlaceId = [];
  }
};

// Add new methods to GMapDetail
for (var method in GMapDetailMethods) {
  GMapDetail[method] = GMapDetailMethods[method];
}
