/*
#TODO: Implement clustermarker function: clusterMarkerClick to zoom in less \
       depending on how many markers were clustered
*/

function sfMap(mapObj, feedURL, isEvents) {
   sfMap.prototype.asObject = this;
   this.map = mapObj;
   this.url = feedURL;
   this.data = null;
   this.center = null;
   this.points = [];
   this.isEvents = isEvents;
   this.focusedMarker = null;
   this.mapMgr = new ClusterMarker(this.map);
   this.canvas = this.map.getContainer();
   this.loadingControl = new HtmlControl('<div class="sfMap" id="loadingControl" style="background-color:transparent; border:solid 1px black; width:200px"><div style="background-color:white; border:solid 1px grey; padding:2ex; margin:1px; font-weight:bold; font-size:14px; text-align:center">Loading...</div></div>', { visible:false, selectable:false } );
   this.noResultsControl = new HtmlControl('<div class="sfMap" id="style="background-color:transparent; border:solid 1px black; width:200px"><div style="background-color:white; border:solid 1px grey; padding:2ex; margin:1px; font-weight:bold; font-size:14px; text-align:center">No results found, please refine your query and try again.</div></div>', { visible:false, selectable:false } );

   this.map.addControl(this.loadingControl, new GControlPosition(G_ANCHOR_TOP_RIGHT)); 
   this.map.addControl(this.noResultsControl, new GControlPosition(G_ANCHOR_TOP_RIGHT)); 

   var _isBusy = false;
   this.isBusy = function() { return _isBusy }
   this.setBusy = function(val) { _isBusy = val; }

   this.mlk = null;

   GEvent.addListener(this.map, "click", this.showInfoWindow);
};

sfMap.prototype.asObject = null;

sfMap.prototype.ee = function(s) {
   return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

sfMap.prototype.eesummary = function(s) {
   return this.ee(this.truncate(s.replace(/\s+/g, " "), 300))
}

sfMap.prototype.eedetails = function(s, test) {
   s = this.ee(s);
   s = s.replace(/(https?:\/\/|www\.)([a-z0-9]([a-z0-9\-]+[a-z0-9])?\.){1,}[a-z]{2,5}(\/[a-z0-9\?&=\$\-_\.\+\!\*'\(\),%\/:]*)?/gi, function($0) {
         var url = $0;
         if (!url.match(/^https?:/i)) url = "http://" + url;
         var rurl = url.replace(/[\.\!\?]+$/, "");

         return "<a href=\"" + rurl + "\" title=\"Visit " + $0 + "\" rel=\"external\">" + $0 + "</a>";
      });
   s = s.replace(/[a-z0-9\.\-_]+@([a-z0-9]([a-z0-9\-]+[a-z0-9])?\.){1,}[a-z]{2,5}/gi, "<a href=\"mailto:$0\" rel=\"external mail\" title=\"Send an email to $0\">$0</a>");

   s = s.replace(/\r?\n/g, "<br />")
   return (test) ? s.replace(/\s+/g, " ") : s.replace("  ", "&nbsp; ");
}

sfMap.prototype.eetest = function(s) {
   return (sfMap.prototype.eesummary(s) == sfMap.prototype.eedetails(s, true));
}

/**
 * Makes JSON query to sfMapJSON URL and displays results on map
 */
sfMap.prototype.query = function(frm, isFirst) {
   // Allow only a single concurrent query
   if (this.isBusy()) return false;

   this.setBusy(true);
   
   if (typeof(frm) != "undefined") this.form = frm;

   if (isFirst) {
      //#TODO: Antyihing special on the initial automatic submition?
   } else {
      if ((frm.ids) && (frm.ids.value)) frm.ids.value = "";
   }

   var params = $(this.form).serialize();
   if (!this.url) this.url = this.form.action;

   this.noResultsControl.setVisible(false);
   this.loadingControl.setVisible(true);

   $("#searchResults .status").text("Loading results...");
   $("#searchResults div.pagItems").remove();
   $("#searchResults div.resultItems").remove();
   this.mapMgr.removeMarkers();

   var _self = this;
   $.getJSON(this.url, params, function(data) {
         _self.data = data.success;
         _self.loadResults(0, true);
         _self.getMarkers();
      });

   this.setBusy(false); // release lock
   return false;
}

sfMap.prototype.handleNoResults = function() {
   this.loadingControl.setVisible(false);
   this.noResultsControl.setVisible(true);
}

sfMap.prototype.scrollToMap = function() {
   var selectedPosX = 0;
   var selectedPosY = 0;
   var theElement = this.map.getContainer();

   while (theElement != null) {
      selectedPosX += theElement.offsetLeft;
      selectedPosY += theElement.offsetTop;
      theElement = theElement.offsetParent;
   }

   selectedPosX -= 50;
   selectedPosY -= 50;
   if (selectedPosX < 0) selectedPosX = 0;
   if (selectedPosY < 0) selectedPosY = 0;

   window.scrollTo(selectedPosX, selectedPosY);
}

sfMap.prototype.loadResults = function(offs, isinitial) {
   var data = this.data;

   if (!isinitial) {
      this.loadingControl.setVisible(true);
      this.mapMgr.removeMarkers();
      this.mapMgr.refresh();

      $("#searchResults .status").text("Loading results...");
      $("#searchResults div.pagItems").remove();
      $("#searchResults div.resultItems").remove();
   }

   if ((!data) || (data.length <= 0)) {
      this.handleNoResults();
      this.loadingControl.setVisible(false);
      $("#searchResults .status").text("No " + ((this.isEvents) ? "events" : "wineries") + " found");
      return;
   }

   if (offs <= 0) offs = 0;
   if (offs >= data.length) offs = 0;

   $("#searchResults").append("<div class=\"pagItems\"></div>");
   $("#searchResults").append("<div class=\"resultItems\"></div>");
   this.mapMgr.addMarkers(this.createMarkers(offs));
   $("#searchResults a[@rel='external']").each(function() {
         var el = this;
         $(this).click(function() {
               window.open(el.href);
               return false;
            });
      });

   this.mapMgr.refresh(true);
   this.mapMgr.fitMapToMarkers();
   this.map.checkResize();
   this.loadingControl.setVisible(false);
   if (data.length == 1) {
      $("#searchResults .status").text("A single " + ((this.isEvents) ? "event" : "winery") + " was found");
      $("#searchResults span.mapFocus a").click();
   } else {
      $("#searchResults .status").text(data.length + " " + ((this.isEvents) ? "events" : "wineries") + " found");
   }
}

sfMap.prototype.swapMarkerColor = function(marker) {
   if (!marker.sfdata) return;
   var fnd = false;
   var markers = this.getMarkers();
   for (var i=0; i < markers.length; i++) {
      var t = markers[i];
      if (!t.sfdata) continue;
      if (t.sfdata.id == marker.sfdata.id) {
         this.showInfoWindow(t);
         fnd = true;
         break;
      }
   }
   if (!fnd) window.setTimeout(this.swapMarkerColor, 10, marker);
}


sfMap.prototype.onFocusMap = function(evt) {
   if (!evt) evt = window.event;
   var el = (typeof evt.target != "undefined") ? evt.target : evt.srcElement;
   while (el.nodeName != "A") el = el.parentNode;
   if (evt.preventDefault) evt.preventDefault();
   evt.returnValue = false;
   var self = sfMap.prototype.asObject;
   var id = $(this).attr("resid");
   var fnd = -1;
   for (var x=0; x < self.points.length; x++) {
      if (self.points[x].sfdata.id == id) {
         fnd = x;
         break;
      }
   }
   if (fnd < 0) return false;
   fnd = self.points[fnd];
   self.scrollToMap();

   var markers = self.getMarkers();
   for (var i=0; i < markers.length; i++) {
      if (markers[i].sfFocused) {
         if (markers[i].sfdata.id == fnd.sfdata.id) {
            self.showInfoWindow(markers[i]);
            return false;
         } else {
            break;
         }
      }
   }

   var latlng = fnd.getLatLng();

   for (var i=0; i < markers.length; i++) {
      if ((markers[i].setImage) && (markers[i].sfdata)) {
         markers[i].setImage("http://maps.google.com/intl/en_us/mapfiles/marker.png");
         markers[i].sfFocused = false;
      }
   }

   self.map.panTo(latlng);
   self.map.setZoom(16);
   window.setTimeout(function() {
         self.map.panTo(latlng);
         self.swapMarkerColor(fnd);
      }, 10);

   return false;
}

sfMap.prototype.findResultContainer = function(prnt) {
   for (var i=0; i <prnt.childNodes.length; i++) {
      if (prnt.childNodes[i].className == "resultItems") {
         return prnt.childNodes[i];
      } else {
         var res = sfMap.prototype.findResultContainer(prnt.childNodes[i]);
         if (res) return res;
      }
   }
   return null;
}

sfMap.prototype.truncate = function(s, len) {
   if (s.length <= len) return s;
   s = s.substring(0, len - 3) + "...";
   return s;
}

sfMap.prototype.expandDetails = function(ael) {
   var ri = $(ael).parent().parent();
   ri.find(".summary").css("display", "none");
   ri.find(".details").css("display", "block");
   return false;
}

sfMap.prototype.collapseDetails = function(ael) {
   var ri = $(ael).parent().parent();
   ri.find(".details").css("display", "none");
   ri.find(".summary").css("display", "block");
   return false;
}


sfMap.prototype.createMarkers = function(offs) {
   var data = this.data;
   this.points = [];

   var pd = $("#searchResults .pagItems").get(0);
   var rd = $("#searchResults .resultItems").get(0);

   var paghtml = "";
   var pagNo = Math.floor((offs + 20) / 20);
   var pagCnt = Math.ceil(data.length / 20);
   var prev = (pagNo - 1);
   var next = (pagNo + 1);

   var pi = [];

   var classes = ["pagLink", "pagPrev"];
   if (pagNo == 1) classes.push("disabled");
   pi.push("<a class=\"" + classes.join(" ") + "\" href=\"#p" + prev + "\" page=\"" + prev + "\"><span class=\"text\">Previous</span></a>");

   for (var i=1; i <= pagCnt; i++) {
      var classes = ["pagLink", "specific"];
      if (i == 1) classes.push("first");
      if (i == pagNo) classes.push("current");
      if (i == pagCnt) classes.push("last");
      pi.push("<a class=\"" + classes.join(" ") + "\" href=\"#p" + i + "\" page=\"" + i + "\" rel=\"scripted\"><span class=\"text\">" + i + "</span></a>");
   }

   var classes = ["pagLink", "pagNext"];
   if (pagNo == pagCnt) classes.push("disabled");
   pi.push("<a class=\"" + classes.join(" ") + "\" href=\"#p" + next + "\" page=\"" + next + "\"><span class=\"text\">Next</span></a>");
   paghtml += pi.join("");

   pd.innerHTML = paghtml;
   $(pd).find("a.pagLink").each(function() {
         this.onclick = function(evt) {
               if (!evt) evt = window.event;
               var el = (typeof evt.target != "undefined") ? evt.target : evt.srcElement;
               while (el.nodeName != "A") el = el.parentNode;
               if (evt.preventDefault) evt.preventDefault();
               evt.returnValue = false;

               if (el.className.match(/(current|disabled)/)) return false;

               var pagNo = parseInt($(el).attr("page"));
               var offs = (pagNo - 1) * 20;
               sfMap.prototype.asObject.loadResults((pagNo - 1) * 20);
               return false;
            };
      });


   var html = "";



   var max = data.length - offs;
   if (max > 25) max = 20;

   for (var i=offs; i < offs + max; i++){
      var o = data[i];

      var cls = [];
      if (this.isEvents) cls.push("eventItem");
      cls.push((i % 2 == 1) ? "odd" : "even");
      if (i == 0) cls.push("first");
      if (i == data.length - 1) cls.push("last");

      var rhtml = "<div id=\"resultItem_" + o.id + "\" class=\"resultItem " + cls.join(" ") + "\">";

      if (o.lat && o.lng) {
         rhtml += "<span class=\"mapFocus\"><a rel=\"scripted\" href=\"#" + o.id + "\" resid=\"" + o.id + "\" title=\"Zoom the map to this result\">Focus on Map</a></span>";
      } else {
         rhtml += "<span class=\"mapFocus notAvailable\">Not mappable</span>";
      }

      if (this.isEvents) {
         rhtml += "<h3 class=\"title\">" + this.ee(o.name) + "</h3>";
         var s = "<span class=\"date\">" + this.ee(o.startDateFmt) + "</span> at <span class=\"time\">" + this.ee(o.startTimeFmt) + "</span>";
         if (o.duration) s += " <span class=\"durationWrapper\">(<span class=\"duration\">goes on for " + o.durationFmt + "</span>)</span>";
         rhtml += "<h4 class=\"startDate\">" + s + "</h4>";
         if (o.winery) rhtml += "<h5 class=\"winery\"><a href=\"" + this.ee(o.winery.link) + "\" class=\"title\">" + this.ee(o.winery.name) + "</a></h5>";

         var inf = [];
         if (o.location) inf.push(this.ee(o.location));
         if ((o.winery) && (o.winery.phone)) inf.push(this.ee(o.winery.phone));
         if (inf.length > 0) rhtml += "<div class=\"physical\">" + inf.join(" - ") + "</div>";

         if (o.winery) {
            var inf = [];
            if (o.winery.email) inf.push("<a href=\"mailto:" + this.ee(escape(o.winery.email)) + "\" rel=\"email\">" + this.ee(o.winery.email) + "</a>");
            if (o.winery.website) inf.push("<a href=\"http://" + this.ee(o.winery.website) + "\" rel=\"external\">" + this.ee(o.winery.website) + "</a>");
            if (inf.length > 0) rhtml += "<div class=\"internet\">" + inf.join(" - ") + "</div>";
         }

         if (o.summary) {
            var s = this.eesummary(o.summary);
            var l = this.eedetails(o.summary);
            var same = (this.eetest(o.summary));
            var ml = (same) ? "" : " &nbsp; <a href=\"#\" class=\"more\" onclick=\"return sfMap.prototype.asObject.expandDetails(this);\">more</a>";
            rhtml += "<p class=\"summary\">" + s + ml + "</p>";
            rhtml += "<p class=\"details\" style=\"display: none;\">" +
                     l +
                     "&nbsp; <a href=\"#\" class=\"less\" onclick=\"return sfMap.prototype.asObject.collapseDetails(this);\">less</a>" +
                     "</p>";
         }

      } else {
         rhtml += "<h3 class=\"title\"><a class=\"title\" href=\"" + this.ee(o.link) + "\" title=\"" + this.ee(o.name) + "\">" + this.ee(o.name) + "</a></h3>";

         var inf = [];
         if (o.address) inf.push(this.ee(o.address));
         if (o.phone) inf.push(this.ee(o.phone));
         if (inf.length > 0) rhtml += "<div class=\"physical\">" + inf.join(" - ") + "</div>";

         var inf = [];
         if (o.email) inf.push("<a href=\"mailto:" + this.ee(escape(o.email)) + "\" rel=\"email\">" + this.ee(o.email) + "</a>");
         if (o.website) inf.push("<a href=\"http://" + this.ee(o.website) + "\" rel=\"external\">" + this.ee(o.website) + "</a>");
         if (inf.length > 0) rhtml += "<div class=\"internet\">" + inf.join(" - ") + "</div>";

         if (o.about) {
            var s = this.eesummary(o.about);
            var l = this.eedetails(o.about);
            var same = (this.eetest(o.about));
            var ml = (same) ? "" : " &nbsp; <a href=\"#\" class=\"more\" onclick=\"return sfMap.prototype.asObject.expandDetails(this);\">more</a>";
            rhtml += "<p class=\"summary\">" + s + ml + "</p>";
            if (!same) {
               rhtml += "<p class=\"details\" style=\"display: none;\">" +
                        l +
                        "&nbsp; <a href=\"#\" class=\"less\" onclick=\"return sfMap.prototype.asObject.collapseDetails(this);\">less</a>" +
                        "</p>";
            }
         }
      }

      rhtml += "</div>";
      html += rhtml;
      if (i < data.length - 1) html += "<div class=\"seperator sepGrey\"><hr/></div>";

      var marker = null;
      if (o.lat && o.lng) {
         marker = new GMarker(new GLatLng(o.lat, o.lng));
         marker.sfdata = o; // extend marker so it knows where to fetch more data
         this.points.push(marker);
      }
   }
   rd.innerHTML = "<div class=\"resultsWrapper\">" + html + "</div>";
   $(rd).find("span.mapFocus a").each(function() { this.onclick = sfMap.prototype.onFocusMap; });
   return this.points;
};


sfMap.prototype.getMarkers = function() {
   if (this.mlk) return this.map[this.mlk];
   for (var k in this.map) {
      o = this.map[k];
      if (!(o instanceof Array)) continue;
      if (!o.length) continue;
      for (var i=0; i < o.length; i++) {
         if (o[i] instanceof GMarker) {
            this.mlk = k;
            return this.map[k];
         }
      }
   }
   return new Array();
}

sfMap.prototype.showInfoWindow = function(marker, point) {
   var s = sfMap.prototype.asObject;
   var map = s.map;
   var markers = s.getMarkers();
   if (map && markers && marker && marker.sfdata) {
      for (var i=0; i < markers.length; i++) {
         if ((markers[i].setImage) && (markers[i].sfdata) && (markers[i].sfdata.id != marker.sfdata.id)) {
            markers[i].sfFocused = false;
            markers[i].setImage("http://maps.google.com/intl/en_us/mapfiles/marker.png");
         }
      }
   }
   $("#searchResults .resultItems div.resultItem").removeClass("focused");

   if (!marker || !marker.sfdata) return;

   var el = null;
   $("#resultItem_" + marker.sfdata.id).each(function() {
         el = this;
         var j = $(this);
         j.addClass("focused");
         var trgt = (this.offsetTop - this.parentNode.offsetTop) - 10;
         j.parent().parent().animate({scrollTop: trgt}, "fast");
      });

   marker.sfFocused = true;
   marker.setImage("/res/img/marker_yellow.png");
   var d = marker.sfdata; // shortcut
   var newel = el.cloneNode(true);
   var jel = $(newel);
   jel.find("span.mapFocus").remove();
   jel.find("a.more").remove();
   jel.find("p.details").remove();
   jel.find("p.summary").css("display", "block");

   marker.openInfoWindowHtml(jel.html(), {maxWidth: 400});
   window.setTimeout(function() {
         $(map.getContainer()).find("a[@rel='external']").each(function() {
               var el = this;
               $(this).click(function() {
                     window.open(el.href);
                     return false;
                  });
            });
      }, 100);
};
