var LARGE_ZOOM = 15;
var MEDIUM_ZOOM = 13;
var POPULAR_ZOOM = 10;
//alert('classes');
// Class MapLabel
function MapLabel(id, content, coords) {
  this.id = id;
  this.content = content;
  this.map = null;
  this.coords = coords;
  this.width = this.height = 0;
  this.opacity = 80;
  this.setOpacity = function(o) { this.opacity = o; }
  this.insertToMap = function (aMap)
  {
     this.map = aMap;
     var obj = document.createElement("span");
     obj.id = this.id;
     obj.innerHTML = this.content;
     $$("body")[0].appendChild(obj);
     with (obj.style) {
       position='absolute';
       zIndex=1;
       filter = 'alpha(opacity:'+this.opacity+')';
       opacity = (this.opacity / 100);
	   if (this.map.getZoom() < POPULAR_ZOOM) display = 'none';
     }
     this.width = $(id).offsetWidth;
     this.height = $(id).offsetHeight;
     mpane = this.map.getPane(G_MAP_MAP_PANE);
	 mpane.appendChild(obj);
     //if(!moffset){moffset=new GSize(0,0);}
     this.setPos();
     GEvent.bind(this.map, "zoomend", this, this.setPos);
     GEvent.bind(this.map, "moveend", this, this.setPos);
  }
  this.setPos = function(a)
  {
    if (this.map.getZoom() < POPULAR_ZOOM) {
      //$(this.id).style.display = 'none';
	  //return;
	}
	$(this.id).style.display = '';
    var p = this.map.fromLatLngToDivPixel(this.coords);
    var x = parseInt(p.x);
    var y = parseInt(p.y);
    x -= Math.floor(this.width / 2);
    y -= Math.floor(this.height / 2);
    with ($(this.id).style){
      left = x+'px';
	  top = y+'px';
    }
  }
}

GMap2.prototype.addMapLabel = function(label){
  label.insertToMap(this);
}
GMap2.prototype.removeMapLabel=function(label){
  var obj = $(label.id);
  obj.parentNode.removeChild(obj);
  delete(obj);
  delete(label);
}


// class Area - Area Area data
function Area(aName, aVertices, aLat, aLng, aInfo) {
  this.name = aName;
  this.info = typeof(aInfo) != "undefined" ? aInfo : null;
  this.vertices = [];
  for (var i = 0; i<aVertices.length; i++) {
    this.vertices[i] = new GLatLng(aVertices[i][0], aVertices[i][1]);
  }
  if (typeof(aLat) != "undefined") {
  	this.center = new GLatLng(aLat,aLng);
  } else {
    this.center = this.vertices[0];
  }
}

// Class AreasOverlay (GMap overlay), areas id Area's array
function AreasOverlay(areas) {
  GOverlay.call(this);
  this.map = null;
  if (typeof(areas) != "undefined") this.setAreas(areas);
  this.labels = false;
  this.curZoom = -1;
}
AreasOverlay.prototype.setAreas = function(areas) {
  // search global area bounds
  this.areas = areas;
  var t = this.areas[0].vertices[0];
  var gMinLat = gMaxLat = t.lat();
  var gMinLng = gMaxLng = t.lng();

  for (var i = 0; i<this.areas.length; i++) {
    // search area bounds
    t = this.areas[i].vertices[0];
    var aMinLat = aMaxLat = t.lat();
    var aMinLng = aMaxLng = t.lng();
    for (var j = 1; j<this.areas[i].vertices.length; j++) {
      t = this.areas[i].vertices[j];
	  if (t.lat() == 0 && t.lng() == 0) continue; // skip special codes
      if (t.lat() < aMinLat) aMinLat = t.lat();
      if (t.lat() > aMaxLat) aMaxLat = t.lat();
      if (t.lng() < aMinLng) aMinLng = t.lng();
      if (t.lng() > aMaxLng) aMaxLng = t.lng();
	}
	this.areas[i].bounds = new GLatLngBounds(new GLatLng(aMinLat,aMinLng), new GLatLng(aMaxLat,aMaxLng));
	if (aMinLat < gMinLat) gMinLat = aMinLat;
	if (aMaxLat > gMaxLat) gMaxLat = aMaxLat;
	if (aMinLng < gMinLng) gMinLng = aMinLng;
	if (aMaxLng > gMaxLng) gMaxLng = aMaxLng;
  }
  // set global bounds
  this.bounds = new GLatLngBounds(new GLatLng(gMinLat, gMinLng), new GLatLng(gMaxLat, gMaxLng));
  //alert(this.bounds);
}

AreasOverlay.prototype.initialize = function(map) {
  this.map = map;
  var cn = document.createElement("canvas");
  cn.id = "nCanvas";
  //cn.style.position = "auto";
  this.map.getPane(G_MAP_MAP_PANE).appendChild(cn);
  this.canvas = $("nCanvas");

  //alert(this.canvasNode);
  // we need to init this for IE
  if (typeof G_vmlCanvasManager != "undefined") {
    this.canvas = G_vmlCanvasManager.initElement(this.canvas);
  }
}

AreasOverlay.prototype.remove = function() {
  window.console.log("TODO: removing");
}

AreasOverlay.prototype.copy = function() {
  window.console.log("TODO: copying");
  return this;
}

AreasOverlay.prototype.redraw = function(force) {
  if (!force) return;
  if (this.curZoom != this.map.getZoom()) {
    this.curZoom = this.map.getZoom();
  } else {
    return;
  }
  //if (this.map.getZoom() > LARGE_ZOOM || this.map.getZoom() < POPULAR_ZOOM) return;
  var start = new Date().getTime();
  // Create map Labels as "Neighborhood name"
  if (this.labels) {
    for(var i=0, cnt, lbl; i < this.areas.length; i++) {
      cnt = '<div style="color:black; background-color:white; padding: 2px; font-size: 0.9em;"><nobr>'+this.areas[i].name+'</nobr></div>';
      lbl = new MapLabel('ml'+i, cnt, this.areas[i].center);
      this.map.addMapLabel(lbl);
	}
	this.labels = false;
  }
  this.clear();
  var swPix = this.map.fromLatLngToDivPixel(this.bounds.getSouthWest());
  var nePix = this.map.fromLatLngToDivPixel(this.bounds.getNorthEast());

  var w = Math.abs(swPix.x - nePix.x);
  var h = Math.abs(swPix.y - nePix.y);

  this.canvas.width = w;
  this.canvas.height = h;
  this.canvas.style.position = 'absolute';
  this.canvas.style.width = w + "px";
  this.canvas.style.height = h + "px";
  this.canvas.style.top = nePix.y + "px";
  this.canvas.style.left = swPix.x + "px";
  var ctx = this.canvas.getContext("2d");
  ctx.strokeStyle = "rgba(0,0,255,0.4)";
  ctx.globalCompositeOperation = "copy";
  if (/MSIE/.test(navigator.userAgent)) { ctx.clearShape("debug_rect"); }
  // debug rect
  ctx.beginPath("debug_rect");
  ctx.rect(0,0,w,h);
  ctx.stroke();
  ctx.closePath();
  /*test*/
  for (var i = 0; i < this.areas.length; i++) {
    this.drawArea(i,0)
  }
  /**/

  var end = new Date().getTime();
  window.console.log("REDRAW: w=" + w + ", h=" + h + ", x=" + swPix.x+" y=" + nePix.y +
                     " Rendering Time=" + (end - start) + "ms");
}
AreasOverlay.prototype.geo2pix = function(lat_lng) {
  t = this.map.fromLatLngToDivPixel(lat_lng);
  t.x -= parseInt(this.canvas.style.left);
  t.y -= parseInt(this.canvas.style.top);
  return t;
}
AreasOverlay.prototype.drawArea = function(index, nStyle, stroke) {
  if (this.map.getZoom() > LARGE_ZOOM || this.map.getZoom() < POPULAR_ZOOM) {
    //return;
  }
  if (typeof(stroke) == "undefined") stroke = true;
  var ctx = this.canvas.getContext("2d");
  var a = this.areas[index];
  // FIX IE
  if (/MSIE/.test(navigator.userAgent)) {
    ctx.clearShape("a"+index);
	for (var i = 0; ctx.clearShape("a"+index+"s"+i); i++) {/*sergey good man*/}
  }
  ctx.lineWidth = 1;
  ctx.strokeStyle = "rgba(0,0,255,0.4)";
  if (nStyle == 0) ctx.fillStyle = "rgba(255,255,255,0)"; // normal
  if (nStyle == 1) ctx.fillStyle = "rgba(0,0,255,0.4)"; // over
  if (nStyle == 2) ctx.fillStyle = "rgba(255,0,0,0.4)"; // selected
  ctx.globalCompositeOperation = "copy";
  ctx.beginPath("a"+index);
  var sub_idx = 0, sub_start = false,sub_start_point = this.geo2pix(a.vertices[0]);
  for (var i = 0,p,t; p = a.vertices[i]; i++) {
    t = this.geo2pix(p);
	if (p.lat() == 0 && p.lng() == 0) {
      ctx.lineTo(sub_start_point.x, sub_start_point.y);
      //ctx.closePath();
      //ctx.stroke(); ctx.fill();
      //ctx.beginPath("a"+index+"s"+sub_idx);
      sub_idx++;
	  sub_start = true;
	  continue;
	}
	if (i == 0 || sub_start) {
      ctx.moveTo(t.x, t.y);
	  if (sub_start) {
        sub_start = false;
		sub_start_point = t;
	  }
	} else ctx.lineTo(t.x, t.y);
  }
  if (sub_idx > 0) ctx.lineTo(sub_start_point.x, sub_start_point.y);
  //if (stroke) ctx.stroke(); else ctx.fill();
  ctx.closePath();
  if (/MSIE/.test(navigator.userAgent)) {
    ctx.strokefill();
  } else {
    ctx.stroke();
    ctx.fill();
  }
}

AreasOverlay.prototype.clear = function()
{
  this.canvas.getContext("2d").clearRect(0,0,this.canvas.width,this.canvas.height);
}

// To "subclass" the GControl, we set the prototype object to
// an instance of the GControl object
AddressBox = new GControl();

AddressBox.initialize = function(map) {
  var container = document.createElement("DIV");
  container.id = 'divABox';
  var inp = document.createElement("INPUT");
  inp.id = "abox";
  inp.type = "text";
  inp.onkeyup = function (e) {
	if (window.event) {
		e = window.event;
    	key = window.event.keyCode;
	} else if (e) {
    	key = e.which;
	} else {
	   	return true;
	}
	AddressBox.white();
	if (key == 13) {
		this.onblur();
		return false;
	} 
  }
  inp.onblur = AddressBox.doSearchStreet;

  AddressBox.setInputStyle(inp);
  container.appendChild(inp);
  map.getContainer().appendChild(container);
  AddressBox.setStreet("Address?");
  return container;
}
// By default, the control will appear in the top left corner of the
// map with 10 pixels of padding.
AddressBox.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 10));
}

// Sets the proper CSS for the given button element.
AddressBox.setInputStyle = function(el) {
  el.style.width = "100px";
  el.style.height = "12px";
  el.style.color = "#0000cc";
  el.style.backgroundColor = "white";
  el.style.backgroundImage = "none";
  el.style.fontSize = "7pt";
  el.style.border = "1px solid black";
  el.style.padding = "2px";
  el.style.margin = "0px";
}
AddressBox.setStreet = function(s) {
  AddressBox.initialStreet = s;
  $('abox').value = s;
}
AddressBox.doSearchStreet = function() {
  var s = $('abox').value;
  if (s.length == 0 || s == 'Address?') return;
  if (s != AddressBox.initialStreet) {
    AddressBox.initialStreet = s;
	$('address').value = s;	
	copyAdv2Simple();
	searchAddress();	
  }
}
// must be overrided
AddressBox.onSearchStreet = function(s) { alert(s); }

AddressBox.show = function() {
  $('divABox').style.display = '';
}
AddressBox.hide = function() {
  $('divABox').style.display = 'none';
}
AddressBox.green = function() {
  $('abox').style.color = 'white';
  $('abox').style.backgroundColor = '#00CC99';
}
AddressBox.red = function() {
  $('abox').style.color = 'white';
  $('abox').style.backgroundColor = '#FF3300';
}
AddressBox.white = function() {
  $('abox').style.color = 'black';
  $('abox').style.backgroundColor = 'white';
}


/*
function QuoteSet(location, lat, lng, neighborhoodIndex, quotes) {
  this.location = location;
  this.lat = lat;
  this.lng = lng;
  this.neighborhood = neighborhoodIndex != -1
                      ? neighborhoods[neighborhoodIndex]
                      : null;
  this.quotes = quotes;
  totalQuoteCount += quotes.length;

  this.point = new GLatLng(this.lat, this.lng);
  this.iconNode = null;
  this.infoWindowNode = null;
}

QuoteSet.prototype.getPoint = function() {
  return this.point;
}

QuoteSet.prototype.getIconNode = function() {
  if (!this.iconNode) {
    var quoteCount = this.quotes.length;
    this.iconNode = newNode("div");

    var className = "marker";

    if (quoteCount >= POPULAR_THRESHOLD) {
      className += " marker-popular";
    }

    if (this.neighborhood) {
      className += " marker-in-neighborhood";
    }

    this.iconNode.className = className;
    this.iconNode.quoteSet = this;
    this.iconNode.title = this.location;

    this.iconNode.innerHTML = "<span>" + quoteCount + "</span>";
  }

  return this.iconNode;
}

QuoteSet.prototype.getIconNodeBounds = function() {
  var tl = new GPoint(this.iconNode.offsetLeft, this.iconNode.offsetTop);
  var br = new GPoint(tl.x + this.iconNode.offsetWidth, tl.y + this.iconNode.offsetHeight);

  return new GBounds([tl, br]);
}

QuoteSet.prototype.getQuotes = function() {
  if (this.matchedSearch) {
    return this.matchingQuotes;
  } else {
    return this.quotes;
  }
}

QuoteSet.prototype.showInfoWindow = function() {
  map.closeInfoWindow();
  map.openInfoWindow(this.point,
                     this.getInfoWindowNode());
}

QuoteSet.prototype.getInfoWindowNode = function() {
  if (!this.infoWindowNode) {
    this.infoWindowNode = newNode("div");
    this.infoWindowNode.className = "quote-info-window";

    var headerNode = newNode("h3");
    this.linkNode = newNode("a");
    this.linkNode.target = "_blank";
    headerNode.appendChild(this.linkNode);
    this.titleNode = newNode("span");
    this.linkNode.appendChild(this.titleNode);
    this.infoWindowNode.appendChild(headerNode);

    this.bodyNode = newNode("div");
    this.bodyNode.className = "body";
    this.infoWindowNode.appendChild(this.bodyNode);

    var controllerNode = newNode("div");
    controllerNode.className = "controller";
    if (this.quotes.length > 1) {
      var self = this;

      var prevNode = newNode("span");
      prevNode.className = "button";
      prevNode.innerHTML = "&laquo;";
      prevNode.onclick = function() {
        self.displayQuote(self.currentQuoteIndex - 1);

        return false;
      }
      controllerNode.appendChild(prevNode);

      this.statusNode = newNode("span");
      this.statusNode.className = "status";
      controllerNode.appendChild(this.statusNode);

      var nextNode = newNode("span");
      nextNode.className = "button";
      nextNode.innerHTML = "&raquo;";
      nextNode.onclick = function() {
        self.displayQuote(self.currentQuoteIndex + 1);

        return false;
      }
      controllerNode.appendChild(nextNode);

    } else {
      controllerNode.style.display = "none";
    }
    this.infoWindowNode.appendChild(controllerNode);

    var locationNode = newNode("address");
    locationNode.innerHTML = this.location;
    this.infoWindowNode.appendChild(locationNode);
  }

  this.displayQuote(Math.floor(Math.random() * this.getQuotes().length));

  return this.infoWindowNode;
}

QuoteSet.prototype.displayQuote = function(index) {
  var quotes = this.getQuotes();
  index = (index + quotes.length) % quotes.length;

  if (index == this.currentQuoteIndex) return;

  this.currentQuoteIndex = index;

  var quote = quotes[index];

  this.linkNode.href =
    "http://www.overheardinnewyork.com/archives/" + quote.id + ".html";
  this.titleNode.innerHTML = quote.title;
  this.bodyNode.innerHTML = quote.quote;

  if (this.quotes.length > 1) {
    this.statusNode.innerHTML = (index + 1) + " of " + quotes.length;
  }

  if (!map.getInfoWindow().isHidden()) {
      this.infoWindowNode.style.display = "none";
      map.openInfoWindow(this.point,
                         this.infoWindowNode);
      this.infoWindowNode.style.display = "";
   }
}

QuoteSet.prototype.filterForSearch = function(terms) {
  function containsTerms(s) {
    var tokens = tokenize(s);

    for (var i = 0, term; term = terms[i]; i++) {
      if (!(term in tokens)) {
        return false;
      }
    }

    return true;
  }

  this.resetSearch();

  var matchingQuotes = [];

  if (containsTerms(this.location)) {
    matchingQuotes = this.quotes;
  } else {
    for (var i = 0, quote; quote = this.quotes[i]; i++) {
      if (containsTerms(quote.quote)) {
        matchingQuotes.push(quote);
      }
    }
  }

  this.matchedSearch = matchingQuotes.length > 0;

  if (this.matchedSearch) {
    // Make sure we've initted the icon node
    var iconNode = this.getIconNode();

    addClass(iconNode, "hit");
    iconNode.firstChild.innerHTML = matchingQuotes.length;

    this.matchingQuotes = matchingQuotes;
  }

  return matchingQuotes.length;
}

QuoteSet.prototype.resetSearch = function() {
  if (this.matchedSearch) {
    this.iconNode.firstChild.innerHTML = this.quotes.length;
    removeClass(this.iconNode, "hit");
    this.matchedSearch = false;

    this.matchingQuotes = null;
  }
}

function Quote(id, title, quote, source, timestamp) {
  this.id = id;
  this.title = title;
  this.quote = quote;
  this.source = source;
  this.timestamp = timestamp;

  if (timestamp > latestTimestamp) {
    latestTimestamp = timestamp;
  }
}

function QuotesOverlay(quotes) {
  GOverlay.call(this);

  this.map = null;
  this.quotes = quotes;
  this.visibleQuotes = {};
}

QuotesOverlay.prototype.initialize = function(map) {
  this.map = map;
  this.parentNode = map.getPane(G_MAP_MARKER_PANE);

  // To handle clicks in the shadow, adding a simple handler to
  // G_MAP_MARKER_MOUSE_TARGET_PANE doesn't seem to work, and we don't want
  // to have to add DOM nodes there too. So instead we add a global click
  // handler to the map that looks for clicked markers if the regular
  // handler doesn't trigger
  GEvent.bindDom(this.parentNode, "click", this, this.handleDomClick);
  GEvent.bind(map, "click", this, this.handleMapClick);


  GEvent.bind(map, "dragstart", this, this.beginMapDrag);
  GEvent.bind(map, "dragend", this, this.endMapDrag);
}

QuotesOverlay.prototype.beginMapDrag = function() {
  // Don't count short drags, so that they still trigger marker clicks
  var self = this;
  this.beginDragTimeout = window.setTimeout(function() {
    self.beginDragTimeout = null;
    self.inDrag = true;
  }, 250);
}

QuotesOverlay.prototype.endMapDrag = function() {
  if (this.beginDragTimeout) {
    window.clearTimeout(this.beginDragTimeout);
  } else {
    // We reset the drag state in a timeout because we want the click event
    // (if any) to be processed first
    var self = this;
    window.setTimeout(function() {
      self.inDrag = false;
    }, 0);
  }
}

QuotesOverlay.prototype.handleMapClick = function(marker, point) {
  var self = this;
  this.handledClick = false;
  window.setTimeout(function() {
    if (self.handledClick) return;

    var domPoint = map.fromLatLngToDivPixel(point);
    var domBounds = new GBounds([domPoint]);

    for (var location in self.visibleQuotes) {
      var quote = self.visibleQuotes[location];

      if (quote.getIconNodeBounds().containsBounds(domBounds)) {
        self.handleDomClick({target: quote.getIconNode()});
        break;
      }
    }
  }, 0);
}

QuotesOverlay.prototype.handleDomClick = function(event) {
  this.handledClick = true;

  if (this.inDrag) {
    return;
  }

  for (var node = event.target; node; node = node.parentNode) {
    if (node.quoteSet) {
      node.quoteSet.showInfoWindow();
      break;
    }
  }
}

QuotesOverlay.prototype.remove = function() {
  window.console.log("TODO: removing");
}

QuotesOverlay.prototype.copy = function() {
  window.console.log("TODO: copying");

  return this;
}

QuotesOverlay.prototype.redraw = function(force) {
  if (force) {
    var zoom = map.getZoom();

    if (zoom >= LARGE_ZOOM_THRESHOLD &&
        !hasClass(containerNode, "quotes")) {
      addClass(containerNode, "quotes");
      removeClass(containerNode, "neighborhoods");
    } else if (zoom < LARGE_ZOOM_THRESHOLD &&
               !hasClass(containerNode, "neighborhoods")) {
      removeClass(containerNode, "quotes");
      addClass(containerNode, "neighborhoods");
    }

    this.resetVisibleQuotes();
  } else {
    if (this.updateVisibleQuotesTimeout) {
      window.clearTimeout(this.updateVisibleQuotesTimeout);
    }
    var self = this;
    this.updateVisibleQuotesTimeout = window.setTimeout(function() {
      self.updateVisibleQuotesTimeout = null;
      self.updateVisibleQuotes();
    }, 100);
  }
}

QuotesOverlay.prototype.resetVisibleQuotes = function() {
  for (var location in this.visibleQuotes) {
    var quote = this.visibleQuotes[location];
    this.parentNode.removeChild(quote.getIconNode());
  }

  this.visibleQuotes = {};

  this.updateVisibleQuotes();
}

QuotesOverlay.prototype.updateVisibleQuotes = function() {
  var start = new Date().getTime();

  var quotesToRemove = [];
  var quotesToAdd = [];
  var newVisibleQuotes = {};
  var mapBounds = this.map.getBounds();

  // Enlarge bounds a bit so points at the edges don't flicker in and out
  var sw = mapBounds.getSouthWest();
  var ne = mapBounds.getNorthEast();

  sw = new GLatLng(sw.lat() - 0.001, sw.lng() - 0.001);
  ne = new GLatLng(ne.lat() + 0.001, ne.lng() + 0.001);

  mapBounds = new GLatLngBounds(sw, ne);

  zoom = map.getZoom();

  visibleCount = 0;
  for (var i = 0, quoteSet; quoteSet = this.quotes[i]; i++) {
    if (zoom < LARGE_ZOOM_THRESHOLD &&
        quoteSet.quotes.length < POPULAR_THRESHOLD) {
      continue;
    }

    var alreadyVisible = quoteSet.location in this.visibleQuotes;

    if (mapBounds.contains(quoteSet.getPoint())) {
      visibleCount++;
      newVisibleQuotes[quoteSet.location] = quoteSet;
      if (!alreadyVisible) {
        quotesToAdd.push(quoteSet);
      }
    } else if (alreadyVisible) {
      quotesToRemove.push(quoteSet);
    }
  }

  var removeStart = new Date().getTime();

  for (var i = 0, quoteSet; quoteSet = quotesToRemove[i]; i++) {
    this.parentNode.removeChild(quoteSet.getIconNode());
  }

  this.visibleQuotes = newVisibleQuotes;

  var addStart = new Date().getTime();

  for (var i = 0, quoteSet; quoteSet = quotesToAdd[i]; i++) {
    var iconNode = quoteSet.getIconNode();
    var iconPosition = this.map.fromLatLngToDivPixel(quoteSet.getPoint());

    iconNode.style.left = iconPosition.x + "px";
    iconNode.style.top = iconPosition.y + "px";

    this.parentNode.appendChild(iconNode);
  }

  var end = new Date().getTime()

  window.console.log(
    "showing " + visibleCount + "/" + quotes.length +
        " (" + (removeStart - start) + "ms)" +
    " added " + quotesToAdd.length +
        " (" + (end - addStart) + "ms)" +
    " removed " + quotesToRemove.length +
        " (" + (addStart - removeStart) + "ms)");
}
*/
