GSmapSearchControl.ACTIVE_MAP_ZOOM = 14;
GSmapSearchControl.IDLE_MAP_ZOOM = 11;
GSmapSearchControl.MAP_TYPE_ENABLE_ALL = 1;
GSmapSearchControl.MAP_TYPE_ENABLE_ACTIVE = 2;
function GSmapSearchControl(container, mapCenter, opt_options) {

  this.enableIdleMapType = false;
  this.enableActiveMapType = false;
  this.root = container;
  this.buildContainerGuts();

  this.activeMapZoom = GSmapSearchControl.ACTIVE_MAP_ZOOM;
  this.idleMapZoom = GSmapSearchControl.IDLE_MAP_ZOOM;

  // result scroller
  this.currentResultIndex = 0;
  this.markers = new Array();
  this.idle = true;

  // bind up the controls
  this.searchForm.input.onclick = GSmapsc_methodClosure(this, GSmapSearchControl.prototype.onPreActive, []);
  this.searchForm.input.onfocus = GSmapsc_methodClosure(this, GSmapSearchControl.prototype.onPreActive, []);
  this.searchForm.setOnSubmitCallback(this,GSmapSearchControl.prototype.formSubmit);

  this.cancel.onclick = GSmapsc_methodClosure(this, GSmapSearchControl.prototype.goIdle, []);
  this.next.onclick = GSmapsc_methodClosure(this, GSmapSearchControl.prototype.onNext, []);
  this.prev.onclick = GSmapsc_methodClosure(this, GSmapSearchControl.prototype.onPrev, []);

  // build the icons
  this.selectedIcon = new GIcon();
  this.selectedIcon.image = "http://labs.google.com/ridefinder/images/mm_20_red.png";
  this.selectedIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
  this.selectedIcon.iconSize = new GSize(12, 20);
  this.selectedIcon.shadowSize = new GSize(22, 20);
  this.selectedIcon.iconAnchor = new GPoint(6, 20);
  this.selectedIcon.infoWindowAnchor = new GPoint(5, 1);

  this.unselectedIcon = new GIcon(this.selectedIcon);
  this.unselectedIcon.image = "http://labs.google.com/ridefinder/images/mm_20_red.png";

  this.centerIcon = new GIcon(this.selectedIcon);
  this.centerIcon.image = "http://labs.google.com/ridefinder/images/mm_20_green.png";

  this.mapCenterString = null;
  this.titleOverride = null;

  // handle title and url
  if (opt_options) {
    var title = null;
    var url = null;
    if (opt_options.title) {
      title = opt_options.title;
    }
    if (opt_options.url) {
      url = opt_options.url;
    }
    if (url && title) {
      var div = GSmapsc_createDiv(null,"gsmsc-user-title");
      var link = GSmapsc_createLink(url, title, null, "gsmsc-user-title");
      div.appendChild(link);
      this.titleOverride = div;
    }

    if (opt_options.hotspots) {
      // whip through the hotspots and bind away...
      for (var i=0; i < opt_options.hotspots.length; i++) {
        var hs = opt_options.hotspots[i];
        hs.element.onclick = GSmapsc_methodClosure(this, GSmapSearchControl.prototype.newSearch, [hs.query]);
      }
    }

    // allow custom center marker
    if (opt_options.centerIcon) {
      this.centerIcon = opt_options.centerIcon;
    }

    // allow custom selected marker
    if (opt_options.selectedIcon) {
      this.selectedIcon = opt_options.selectedIcon;
    }

    // allow custom unselected marker
    if (opt_options.unselectedIcon) {
      this.unselectedIcon = opt_options.unselectedIcon;
    }

    // allow zoom level specification
    if (opt_options.idleMapZoom) {
      this.idleMapZoom = opt_options.idleMapZoom;
    }

    if (opt_options.activeMapZoom) {
      this.activeMapZoom = opt_options.activeMapZoom;
    }

    if (opt_options.mapTypeControl) {
      if (opt_options.mapTypeControl == GSmapSearchControl.MAP_TYPE_ENABLE_ALL) {
        this.enableIdleMapType = true;
        this.enableActiveMapType = true;
      } else if (opt_options.mapTypeControl == GSmapSearchControl.MAP_TYPE_ENABLE_ACTIVE) {
        this.enableIdleMapType = false;
        this.enableActiveMapType = true;
      } else {
        this.enableIdleMapType = false;
        this.enableActiveMapType = false;
      }
    }
  }

  // be kind, since typically a lot is going on at load...
  this.startupTimer = setTimeout(GSmapsc_methodClosure(this, GSmapSearchControl.prototype.deferLoad, [mapCenter]), 100);
}

GSmapSearchControl.prototype.deferLoad = function(mapCenter) {
  // allow mapCenter to be a string or a GLatLng
  if ( mapCenter && mapCenter.lat && mapCenter.lng ) {
    this.mapCenterString = "lat: " + mapCenter.lat + " lng: " + mapCenter.lng;
    this.mapCenter = mapCenter;
    this.bootComplete(null);
  } else {
    // use ajax search based geocode
    this.mapCenter = null;
    this.mapCenterString = mapCenter;
    var gsLookup = new GlocalSearch();
    gsLookup.setSearchCompleteCallback(this, GSmapSearchControl.prototype.bootComplete, [gsLookup]);
    gsLookup.execute(mapCenter);
  }
}

GSmapSearchControl.prototype.buildContainerGuts = function() {
  // build out the map divs and search form

  GSmapsc_removeChildren(this.root);
  this.appContainer = GSmapsc_createDiv(null, "gsmsc-appContainer");

  // active map div
  this.mapDiv = GSmapsc_createDiv(null, "gsmsc-mapDiv");
  this.appContainer.appendChild(this.mapDiv);

  this.attributionDiv = GSmapsc_createDiv(null, "gsmsc-attributionDiv");
  this.appContainer.appendChild(this.attributionDiv);

  // idle map div
  this.idleMapDiv = GSmapsc_createDiv(null, "gsmsc-idleMapDiv");
  this.appContainer.appendChild(this.idleMapDiv);

  var div = GSmapsc_createDiv(null, "gsmsc-controls");
  this.searchForm = new GSearchForm(false, div);

  // controls
  this.prevNext = GSmapsc_createDiv(null, "gsmsc-prevNext");
  this.prev = GSmapsc_createDiv(null, "gsmsc-prev");
  this.cancel = GSmapsc_createDiv(null, "gsmsc-cancel");
  this.next = GSmapsc_createDiv(null, "gsmsc-next");
  this.tooltip = GSmapsc_createDiv("検索結果をスクロール", "gsmsc-tooltip");
  this.prev.innerHTML = "&nbsp;";
  this.cancel.innerHTML = "&nbsp;";
  this.next.innerHTML = "&nbsp;";
  this.prev.title = "前の検索結果";
  this.cancel.title = "検索結果をリセット";
  this.next.title = "次の検索結果";
  this.prevNext.appendChild(this.prev);
  this.prevNext.appendChild(this.cancel);
  this.prevNext.appendChild(this.next);
  this.prevNext.appendChild(this.tooltip);
  this.searchForm.userDefinedCell.appendChild(this.prevNext);

  this.appContainer.appendChild(div);
  this.root.appendChild(this.appContainer);
}

// finish boot process by creating and centering our map(s)
GSmapSearchControl.prototype.bootComplete = function(gs) {
  if ( gs && gs.results && gs.results.length > 0 ) {
    // extract map center from first search result
    this.mapCenter = new GLatLng(parseFloat(gs.results[0].lat),
                                 parseFloat(gs.results[0].lng));
    var resHtml = gs.results[0].html.cloneNode(true);
    if (this.titleOverride) {
      var div = GSmapsc_createDiv(null, "gsmsc-user-title");
      div.appendChild(this.titleOverride);
      div.appendChild(resHtml);
      this.mapCenterHtml = div;
    } else {
      this.mapCenterHtml = resHtml;
    }

  } else if (this.mapCenter == null) {
    this.mapCenter = new GLatLng(37.421947, -122.084391);
    this.mapCenterString = "lat: " + this.mapCenter.lat + " lng: " + this.mapCenter.lng;
    if (this.titleOverride) {
      this.mapCenterHtml = this.titleOverride;
    } else {
      this.mapCenterHtml = GSmapsc_createDiv(this.mapCenterString, "gsmsc-map-center");
    }
  }
  this.gmap = new GMap2(this.mapDiv);
  this.gmap.addControl(new GSmallMapControl());
  if (this.enableActiveMapType) {
    this.gmap.addControl(new GMapTypeControl());
  }
  this.gmap.setCenter(this.mapCenter, this.activeMapZoom);

  this.idleGmap = new GMap2(this.idleMapDiv);
  if (this.enableIdleMapType) {
    this.idleGmap.addControl(new GMapTypeControl());
  }
  this.idleGmap.setCenter(this.mapCenter, this.idleMapZoom);

  // create a searcher and bind to the map
  // note: address lookup mode has been disabled, search results ONLY
  this.gs = new GlocalSearch();
  this.gs.setResultSetSize(GSearch.LARGE_RESULTSET);
  this.gs.setCenterPoint(this.mapCenter);
  this.gs.setSearchCompleteCallback(this, GSmapSearchControl.prototype.searchComplete, [null]);

  this.idleMarker = new GMarker(this.mapCenter, this.centerIcon);
  this.mapCenterMarker = new GMarker(this.mapCenter, this.centerIcon);
  GEvent.bind(this.mapCenterMarker, "click", this, this.onCenterClick);
  GEvent.bind(this.idleMarker, "click", this, this.onIdleCenterClick);
  this.gmap.addOverlay(this.mapCenterMarker);
  this.idleGmap.addOverlay(this.idleMarker);

  GEvent.bind(this.gmap, "click", this, this.onMapClick);

  this.goIdle();
}

// show the center of the map
GSmapSearchControl.prototype.onCenterClick = function() {
  this.mapCenterMarker.openInfoWindow(this.mapCenterHtml, {maxWidth:200});
}

// show the center of the map
GSmapSearchControl.prototype.onIdleCenterClick = function() {

  // transition to semi-active state and open the center info window
  this.clearMarkers();
  GSmapsc_cssSetClass(this.prevNext, "gsmc-prevNext gsmsc-prev-next-active");
  GSmapsc_cssSetClass(this.appContainer, "gsmsc-appContainer gsmsc-active");
  GSmapsc_cssSetClass(this.prev, "gsmsc-prev gsmsc-prev-idle");
  GSmapsc_cssSetClass(this.next, "gsmsc-next gsmsc-next-idle");
  this.searchForm.input.value = "";
  this.gmap.checkResize();
  this.idle = false;

  this.mapCenterMarker.openInfoWindow(this.mapCenterHtml, {maxWidth:200});
}

// clear the old markers off of the map
GSmapSearchControl.prototype.clearMarkers = function() {
  GSmapsc_cssSetClass(this.prevNext, "gsmc-prevNext gsmsc-prev-next-idle");

  this.gmap.closeInfoWindow();
  for (var i=0; i < this.markers.length; i++) {
    this.gmap.removeOverlay(this.markers[i].marker);
  }

  // result scroller
  this.currentResultIndex = 0;
  this.markers = new Array();
}

// drop the new markers on the map
GSmapSearchControl.prototype.setMarkers = function() {

  // result scroller
  this.currentResultIndex = 0;
  this.markers = new Array();

  if ( this.gs.results && this.gs.results.length > 0) {
    for (var i = 0; i < this.gs.results.length; i++) {
      var result = this.gs.results[i];
      this.markers.push(new GSmapscLocalResult(this, result, this.unselectedIcon, i));
    }
    GSmapsc_cssSetClass(this.prevNext, "gsmc-prevNext gsmsc-prev-next-active");
    GSmapsc_cssSetClass(this.appContainer, "gsmsc-appContainer gsmsc-active");
    this.gmap.checkResize();
    this.idle = false;
    this.selectMarker(0);
  }
}

// light up the selected marker
GSmapSearchControl.prototype.selectMarker = function(index) {

  // clear info window and reset icon on current marker
  this.gmap.closeInfoWindow();
  this.markers[this.currentResultIndex].setMarker(this.unselectedIcon);

  // snap to current
  this.currentResultIndex = index;

  // light up current
  var result = this.markers[this.currentResultIndex];

  result.setMarker(this.selectedIcon);
  result.marker.openInfoWindow(result.getHtml(), {maxWidth:200});

  // set scroller
  if (index == 0) {
    GSmapsc_cssSetClass(this.prev, "gsmsc-prev gsmsc-prev-idle");
  } else {
    GSmapsc_cssSetClass(this.prev, "gsmsc-prev gsmsc-prev-active");
  }

  if (index == this.markers.length - 1) {
    GSmapsc_cssSetClass(this.next, "gsmsc-next gsmsc-next-idle");
  } else {
    GSmapsc_cssSetClass(this.next, "gsmsc-next gsmsc-next-active");
  }
}

// clear current markers and start a new search
GSmapSearchControl.prototype.formSubmit = function(form) {
  if (form.input.value) {
    this.newSearch(form.input.value);
  }
  return false;
}

// clear current markers and start a new search
GSmapSearchControl.prototype.newSearch = function(opt_query) {
  if (opt_query) {
    this.searchForm.input.value  = opt_query;
  }
  if (this.searchForm.input.value) {

    // clear markers, set prev/next
    this.clearMarkers();
    GSmapsc_removeChildren(this.attributionDiv);
    this.gs.execute(this.searchForm.input.value);
  }
  return false;
}

GSmapSearchControl.prototype.searchComplete = function() {
  var attribution = this.gs.getAttribution();
  if (attribution) {
    this.attributionDiv.appendChild(attribution);
  }
  this.setMarkers();
}

// forwards through the search results
GSmapSearchControl.prototype.onNext = function() {
  if (this.currentResultIndex < this.markers.length - 1) {
    this.selectMarker(this.currentResultIndex+1);
  }
}

// backwards through the search results
GSmapSearchControl.prototype.onPrev = function() {
  if (this.currentResultIndex > 0) {
    this.selectMarker(this.currentResultIndex-1);
  }
}

// called onboot complete, and on cancel click
GSmapSearchControl.prototype.goIdle = function() {
  this.searchForm.input.value = "マップ検索";
  this.gmap.setCenter(this.mapCenter, this.activeMapZoom);
  this.idleGmap.setCenter(this.gmap.getCenter(), this.idleMapZoom);
  GSmapsc_cssSetClass(this.appContainer, "gsmsc-appContainer gsmsc-idle");
  GSmapsc_cssSetClass(this.prevNext, "gsmc-prevNext gsmsc-prev-next-idle");
  this.idleGmap.checkResize();
  this.idle = true;
}

// call onfocus/onclick for search input cell
GSmapSearchControl.prototype.onPreActive = function() {
  if (this.idle) {
    this.searchForm.input.value = "";
  }
}

GSmapSearchControl.prototype.onMapClick = function(marker, point) {
  if (marker && marker.__ls__) {
    var localResult = marker.__ls__;
    localResult.onClick();
  }
}

// A class representing a single Local Search result returned by the
// Google AJAX Search API.
function GSmapscLocalResult(gsmsc, result, icon, index) {
  this.gsmsc = gsmsc;
  this.result = result;
  this.latLng = new GLatLng(parseFloat(result.lat), parseFloat(result.lng));
  this.index = index;
  this.setMarker(icon);
}

GSmapscLocalResult.prototype.getHtml = function() {
  return this.result.html.cloneNode(true);
}

GSmapscLocalResult.prototype.setMarker = function(icon) {
  if (this.marker) {
    this.gsmsc.gmap.removeOverlay(this.marker);
    this.marker.__ls__ = null;
    var marker = this.marker;
    this.marker = null;
    delete(marker);
  }
  this.marker = new GMarker(this.latLng, icon);
  this.marker.__ls__ = this;
  this.gsmsc.gmap.addOverlay(this.marker);
}

GSmapscLocalResult.prototype.onClick = function() {
  this.gsmsc.selectMarker(this.index);
}


/**
 * Various Static DOM Wrappers.
*/
function GSmapsc_methodClosure(object, method, opt_argArray) {
  return function() {
    return method.apply(object, opt_argArray);
  }
}

function GSmapsc_createDiv(opt_text, opt_className) {
  var el = document.createElement("div");
  if (opt_text) {
    el.innerHTML = opt_text;
  }
  if (opt_className) { el.className = opt_className; }
  return el;
}

function GSmapsc_removeChildren(parent) {
  while (parent.firstChild) {
    parent.removeChild(parent.firstChild);
  }
}

function GSmapsc_cssSetClass(el, className) {
  el.className = className;
}


function GSmapsc_createForm(opt_className) {
  var el = document.createElement("form");
  if (opt_className) { el.className = opt_className; }
  return el;
}

function GSmapsc_createTable(opt_className) {
  var el = document.createElement("table");
  if (opt_className) { el.className = opt_className; }
  return el;
}

function GSmapsc_createTableRow(table) {
  var tr = table.insertRow(-1);
  return tr;
}

function GSmapsc_createTableCell(tr, opt_className) {
  var td = tr.insertCell(-1);
  if (opt_className) { td.className = opt_className; }
  return td;
}

function GSmapsc_createTextInput(opt_className) {
  var el = document.createElement("input");
  el.type = "text";
  if (opt_className) { el.className = opt_className; }
  return el;
}

function GSmapsc_createLink(href, text, opt_target, opt_className) {
  var el = document.createElement("a");
  el.href = href;
  el.appendChild(document.createTextNode(text));
  if (opt_className) {
    el.className = opt_className;
  }
  if (opt_target) {
    el.target = opt_target;
  }
  return el;
}
