/* Tribalogic extensions to the jmaps system. */

(function($){
  $.jmap.markers = [];
  $.jmap.tooltip = null;

  $.jmap.initCallback = function(el, options)
  {
    var map = $(el);
    map.jmap("displayMarkers");

    if (options.showSearch)
    {
      // Slightly hacky for now :)
      var search = $('<a href="#">Try our new search interface</a>').click(function(){map.jmap("showSearch"); return false;});
      map.before(search);
      map.parent().height(map.parent().height()+30);
    }


    /*$(el).jmap("addScreenOverlay",
      {
        imageUrl: "/frontend/default/images/icons/find.png",
        screenXY:[-64,0],
        overlayXY:[0,0],
        size:[64,64]
      },
      function(myoverlay)
      {
        console.log(myoverlay);
        GEvent.addListener($.jmap.GMap2, "click",
          function(overlay, latlng)
          {
            var proj = $.jmap.GMap2.getCurrentMapType().getProjection();
            var point = proj.fromLatLngToPixel(latlng, $.jmap.GMap2.getZoom());
            console.log("Clicked");
            console.log(overlay);
            console.log(point);
            console.log(myoverlay);
          });
      });*/
  };

  $.extend(
    $.jmap.JMarkerDefaults,
    {
      pointToolTip: "",
      pointLink: "",
      pointInfoWindowHtml: "",
      toolTipHoverDomId: "",
      pointIconName: ""
    }
  );

  $.jmap.pointerIcons = {};

  $.jmap.createMarker = function(options, callback)
  {
    try
    {
      var options = $.extend({}, $.jmap.JMarkerDefaults, options);
      var markerOptions = {};

      if (typeof options.pointIcon == 'object' && options.pointIcon)
      {
        $.extend(markerOptions, {icon: options.pointIcon});
      }
      else if (options.pointIconName)
      {
        if (!$.jmap.pointerIcons[options.pointIconName])
        {
          var icon = new GIcon();
          icon.image = options.pointIconName;
          icon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
          icon.iconSize = new GSize(12, 20);
          icon.shadowSize = new GSize(22, 20);
          icon.iconAnchor = new GPoint(6, 20);
          icon.infoWindowAnchor = new GPoint(5, 1);
          $.jmap.pointerIcons[options.pointIconName] = icon;
        }
        $.extend(markerOptions, {icon: $.jmap.pointerIcons[options.pointIconName]});
      }

      if (options.pointIsDraggable)
        $.extend(markerOptions, {draggable: options.pointIsDraggable});

      /* Create the marker */
      var marker = new GMarker(new GLatLng(options.pointLatLng[0], options.pointLatLng[1]), markerOptions);

      // Add our own tooltip code.
      if (options.pointToolTip)
      {
        if (!$.jmap.tooltip)
        {
          $.jmap.tooltip = $('<div/>')[0];
          $.jmap.GMap2.getPane(G_MAP_FLOAT_PANE).appendChild($.jmap.tooltip);
        }
        var map = $.jmap.GMap2;
        var tooltip = '<div class="tooltip">' + options.pointToolTip + '</div>';
        GEvent.addListener(marker, "mouseover",
          function()
          {
            $.jmap.tooltip.innerHTML = tooltip;
            var point = map.getCurrentMapType().getProjection().fromLatLngToPixel(map.fromDivPixelToLatLng(new GPoint(0,0),true),map.getZoom());
            var offset = map.getCurrentMapType().getProjection().fromLatLngToPixel(marker.getPoint(),map.getZoom());
            var anchor = marker.getIcon().iconAnchor;
            var width = marker.getIcon().iconSize.width;
            var height = $.jmap.tooltip.clientHeight;
            var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(offset.x - point.x - anchor.x + width, offset.y - point.y -anchor.y -height));
            pos.apply($.jmap.tooltip);
            $.jmap.tooltip.style.visibility="visible";
            //marker.openInfoWindowHtml(tooltip, {maxContent: options.pointMaxContent, maxTitle: options.pointMaxTitle});
          }
        );
        GEvent.addListener(marker, "mouseout",
          function()
          {
            $.jmap.tooltip.style.visibility="hidden";
          }
        );
      }

      // Do we link through?
      if (options.pointLink)
      {
        GEvent.addListener(marker, "click",
          function()
          {
            window.location.href=options.pointLink;
          }
        );
      }

      if (options.pointInfoWindowHtml)
        marker.jMapInfoWindow = options.pointInfoWindowHtml;

      if (options.toolTipHoverDomId)
        marker.jMapToolTipHoverDomId = options.toolTipHoverDomId;

      if (callback)
      {
        callback(marker);
      }
    } catch(er) { }
  };

  $.jmap.registerMarker = function(options, callback)
  {
      $.jmap.createMarker(
        options,
        function(marker)
        {
          // Add the marker to the array.
          $.jmap.markers.push(marker);
          if (callback)
          {
            callback(marker);
          }
        }
      );

  };


  $.jmap.getMarker = function(options, callback)
  {
    try
    {
      var options = $.extend({markerId: 0 }, options);
      return $.jmap.markers[options.markerId];
    }
    catch(er) { }
  };


  $.jmap.displayMarkers = function()
  {
    try
    {
      if (1 == $.jmap.markers.length)
      {
        var marker = $.jmap.markers[0];
        $.jmap.GMap2.addOverlay(marker);
        $.jmap.GMap2.setCenter(
          marker.getLatLng(),
          $.jmap.JOptions.mapZoom
        );
        if (marker.jMapInfoWindow) {
          GEvent.addListener(marker, "click",
            function()
            {
              marker.openInfoWindowHtml(marker.jMapInfoWindow);
            }
          );
          // Triggering the event immediately doesn't seem to work
          // and the window is oftent obscured by the map type icons.
          window.setTimeout(
            function()
            {
              GEvent.trigger(marker, "click");
            },
            250
          );
        }
      }
      else if ($.jmap.markers.length > 1)
      {
        // Calculate the bounding box
        var bounds = new GLatLngBounds();

        for (var i=0; i < $.jmap.markers.length; ++i)
        {
          marker = $.jmap.markers[i];
          bounds.extend(marker.getLatLng());
          $.jmap.GMap2.addOverlay(marker);

          if (marker.jMapToolTipHoverDomId)
          {
            /* Need a fresh namespace for the event listeners */
            (
              function()
              {
                var mkr = marker;
                var el = $("#" + mkr.jMapToolTipHoverDomId)[0];
                GEvent.addDomListener(el, "mouseover", function(){GEvent.trigger(mkr, "mouseover");});
                GEvent.addDomListener(el, "mouseout",  function(){GEvent.trigger(mkr, "mouseout");});
              }
            )();
          }
        }

        // Set Center and Zoom
        $.jmap.GMap2.setCenter(
          bounds.getCenter(),
          $.jmap.GMap2.getBoundsZoomLevel(bounds)
        );
      }
    }
    catch(er) { }
  };

  $.jmap.setCenter = function(newCenter, callback)
  {
    $.jmap.GMap2.setCenter(newCenter);
    $.jmap.variables.mapCenter = newCenter;
    if (typeof callback == 'function') return callback(center);
  };



  /* Some extra options for the search interface */
  /* TODO: Separate this out from the main options. */
  $.extend(
    $.jmap.JDefaults,
    {
      showSearch: false,
      searchAreaPadding: 7,
      searchAreaBorder: 1,
      searchAreaBackground: "#fff",
      searchAreaBorderStyle: "solid #ccc",
      searchMapPercentage: 65,
      searchAjaxUri: "",
      searchRadarUri: "",
      searchResultsUri: "",
      searchUnit: "km",
      searchRadii: [ 20, 50, 100, 150, 200 ],
      searchInitialRadius: 20,
      searchRingColor: "0000c0",
      searchBusyIcon: "",
      searchCloseIcon: "",
      searchUseTagCloud: false,
      searchRestrictCountry: true
    }
  );

  $.jmap.showSearch = function(options, callback)
  {
    try
    {
      var options = $.extend({}, $.jmap.JOptions, options);
      var map = $($.jmap.GMap2.getContainer());
      map.jqm({modal: true});
      map.jqmShow();
      var map_orig_width = map.width();

      var search = $('<div class="tlMapSearch" />');
      var offset = map.offset();

      var margin = (map.width() * options.searchMapPercentage / 100);
      var padding = options.searchAreaPadding;
      var border = options.searchAreaBorder;
      search.css("position", "absolute");
      search.css("margin-left", margin);
      search.css("padding", padding);
      search.css("border", border + "px " + options.searchAreaBorderStyle);
      search.css("background", options.searchAreaBackground);
      search.width(map_orig_width - margin - ((padding+border) * 2));
      search.height(map.height() - ((padding+border) * 2));
      search.css("left", 0);
      search.css("top", 0);
      map.prepend(search);


      /* Close Icon */
      var close = false;
      if (options.searchCloseIcon)
      {
        close = $('<img alt="close" />');
        close.attr("src", options.searchCloseIcon);
        close.css("float", "right");
        search.append(close);
      }

      /* Create a small area for tips/advice */
      var tips = $('<div class="tlMapSearchTips" />');
      tips.css("display", "none");
      tips.css("position", "absolute");
      tips.css("padding", padding);
      tips.css("border", border + "px " + options.searchAreaBorderStyle);
      tips.css("background", options.searchAreaBackground);
      tips.css("opacity", "0.85");
      tips.css("left", 0);
      tips.css("top", map.height());
      tips.width(map_orig_width - ((padding+border) * 2));
      map.prepend(tips);

      tips.append($('<h3>Tips</h3>'));
      var tip = $('<div />');
      tips.append(tip);
      var show_tip =
        function(tipText)
        {
          if ("none" == tips.css("display"))
          {
            tip.html(tipText);
            tips.slideDown(500);
          }
          else
          {
            tip.fadeOut(
              200,
              function()
              {
                tip.html(tipText);
                tip.fadeIn(200);
              }
            );
          }
        };
      var hide_tips =
        function()
        {
          tips.slideUp(1500);
        };


      // For some reason IE prefers having a div here.
      var div = $('<div/>');
      search.append(div);
      var loctext = 'Enter location...';
      var loc = $('<input type="text" size="20" />').attr('value', loctext);
      loc.blur(
        function()
        {
          if (''==loc.val())
            loc.val(loctext);
          hide_tips();
        }
      );
      loc.focus(
        function()
        {
          if (loctext==loc.val())
            loc.val('');
          else
            loc[0].select();

          show_tip(
            "Enter your search terms here. After a short delay your matches will be shown below.<br/>" +
            "When you see a matching location, simply click on the name to recentre the map.<br/>" +
            "You can then choose your desired search radius and display a list of matching results."
          );
        }
      );
      div.append(loc);
      var busy = $('<img alt="Busy"/>');
      if (options.searchBusyIcon)
      {
        busy.attr("src", options.searchBusyIcon);
        busy.css("visibility", "hidden");
        div.append(busy);
      }

      var curloc = $('<p class="tlMapCurLoc">lat/lng: ?/?</p>');
      curloc.css("display", "none"); /* Keep it hidden - useful for debug */
      div.append(curloc);


      var content = $('<div/>');
      content.css("overflow", "auto");
      search.append(content);

      var tools = $('<p/>');
      var button = $('<input type="button" value="Show results"/>');
      var radius = $('<select/>');
      for (var i = 0; i < options.searchRadii.length; ++i)
      {
        // IE needs to have the value option...
        radius.append(
          $('<option/>')
          .attr("value", options.searchRadii[i])
          .append(options.searchRadii[i] + options.searchUnit)
        );
      }
      search.append(tools.append(button).append('&nbsp;within&nbsp;').append(radius));

      var named_location = false;
      button.click(
        function()
        {
          var latlng = $.jmap.GMap2.getCenter();
          var uri = options.searchResultsUri;
          uri += ((uri.indexOf('?') >= 0) ? "&" : "?");
          uri += "posrad=" + latlng.lat() + "," + latlng.lng() + "," + radius.val();
          if (named_location)
            uri += "," + escape(named_location);
          window.location = uri;
        }
      );

      var map_events = Array();
      if (close)
      {
        close.click(
          function()
          {
            for (var i=0; i < map_events.length; ++i)
              GEvent.removeListener(map_events[i]);

            $.jmap.GMap2.clearOverlays();
            tips.remove();
            search.remove();
            map.animate(
              { width: map_orig_width },
              1500,
              "swing",
              function()
              {
                map.jqmHide();
                map.css("display", "block");
                $.jmap.GMap2.checkResize();
                $.jmap.GMap2.returnToSavedPosition();
                $.jmap.displayMarkers();
              }
            );
          }
        );
      }


      $.jmap.GMap2.savePosition();
      $.jmap.GMap2.clearOverlays();
      map.animate(
        {
          width: options.searchMapPercentage + "%"
        },
        1500,
        'swing',
        function()
        {

          show_tip(
            "Please use the map above to target the area you are interested in. You can drag the map to pinpoint exactly where you want to search.<br/>" +
            "When you move the map, nearby locations will be displayed; clicking on a location name will recenter the map for you." +
            (options.searchUseTagCloud ? " Larger/more populated areas will be displayed in bigger type." : "") + "<br/>" +
            "If you want to search by place name, please enter the name above to get a list of potential matches."
          );

          //window.setTimeout(function(){show_tip("You are still a wanker");}, 1500);
          //window.setTimeout(function(){hide_tips();}, 3000);
          /* Put the tools in the right place */
          tools.css("position", "absolute");
          tools.css("top", search.innerHeight() - tools.height());
          content.height(tools.offset().top - content.offset().top - 5);

          $.jmap.GMap2.checkResize();
          $.jmap.GMap2.returnToSavedPosition();

          var proj = G_NORMAL_MAP.getProjection();
          var circle = false;
          var getMaxZoom =
            function()
            {
              var zoom = $.jmap.GMap2.getZoom();
              var center = $.jmap.GMap2.getCenter();

              km = radius.val();
              // Is this in miles?
              if ('km' != options.searchUnit)
                km *= 1.609344;

              // Avg. Latitude length for 1 deg == 111km
              var rad = new GLatLng(center.lat() + (km/111), center.lng());

              var cpt = proj.fromLatLngToPixel(center, zoom);
              var rpt = proj.fromLatLngToPixel(rad, zoom);

              var bounds = new GLatLngBounds();
              with (Math)
              {
                radval = floor(sqrt(pow((cpt.x-rpt.x),2) + pow((cpt.y-rpt.y),2)));
                var piover180 = (PI/180);
                var ar, x, y, p;
                // 5 steps is enough.
                for (var a = 0; a <= 360; a+= 72)
                {
                  ar = a * piover180;
                  y = cpt.y + radval * sin(ar)
                  x = cpt.x + radval * cos(ar)
                  bounds.extend(proj.fromPixelToLatLng(new GPoint(x, y), zoom));
                }
              }
              return $.jmap.GMap2.getBoundsZoomLevel(bounds);
            }
          var drawcircle =
            function(zoomMode)
            {
              if ("initial" == zoomMode)
              {
                // Here we calculate the zoom level
                var bounds = $.jmap.GMap2.getBounds();
                var sw = bounds.getSouthWest();
                var ne = bounds.getNorthEast();
                var latdelta = Math.abs(sw.lat() - ne.lat()) * 111 / 2;
                // Convert our lat delta into the same units as our drop down.
                if ('km' != options.searchUnit)
                  latdelta /= 1.609344;

                // Find the biggest value of radius that fits!
                var rad = false;
                // We assume assending order
                radius.children('option').each(
                  function()
                  {
                    var val = $(this).val();
                    if (val > latdelta)
                      return;

                    rad = val;
                  }
                );
                // If we get a false value, it means all sizes are smaller
                // therefore we need to zoom out to see our circle.
                if (false === rad)
                  zoomMode = "radius";
                else if ("auto" == options.searchInitialRadius)
                  radius.val(rad);
                else
                  radius.val(options.searchInitialRadius);
              }
              // Note that we may sometimes zoom to the bounds of our circle
              // and in this case the projection calculations may be a little out.
              // In practice this does not seem to be much of a problem.
              var zoom = $.jmap.GMap2.getZoom();
              var center = $.jmap.GMap2.getCenter();

              km = radius.val();
              // Is this in miles?
              if ('km' != options.searchUnit)
                km *= 1.609344;

              // Avg. Latitude length for 1 deg == 111km
              var rad = new GLatLng(center.lat() + (km/111), center.lng());

              var cpt, rpt;
              if ("radius" == zoomMode)
              {
                $.jmap.GMap2.setZoom(getMaxZoom());
                zoom = $.jmap.GMap2.getZoom();
                cpt = proj.fromLatLngToPixel(center, zoom);
                rpt = proj.fromLatLngToPixel(rad, zoom);
              }
              else
              {
                cpt = proj.fromLatLngToPixel(center, zoom);
                rpt = proj.fromLatLngToPixel(rad, zoom);
              }
              var pxrad = cpt.y - rpt.y;

              if (!circle)
              {
                circle = { "radius": pxrad, "overlay": false };
                var copts = {
                  radius: pxrad,
                  padding: 3,
                  color: options.searchRingColor + "00",
                  updateUrl: options.searchRadarUri
                };
                circle.overlay = new tlJmapCrossHair(copts);
                $.jmap.GMap2.addOverlay(circle.overlay);
              }
              else if (circle.radius != pxrad)
              {
                circle.overlay.setRadius(pxrad);
                circle.radius = pxrad;
              }
            };

          /* A small utilitity function to scale text appropriately */
          var cloudify =
            function(o, val, limit)
            {
              if (!options.searchUseTagCloud)
                return;
              var segment = limit / 4;
              if (val < (segment))
                o.css('font-size', '150%');
              else if (val < (segment * 2))
                o.css('font-size', '120%');
              else if (val < (segment * 3))
                o.css('font-size', '100%');
              else
                o.css('font-size', '90%');
            };
          var update_search =
            function()
            {
              busy.css("visibility", "hidden");
              locval = loc.val();
              if ('' == locval || loctext == locval)
              {
                busy.css("visibility", "hidden");
                return;
              }
              var limit = 25;
              var restrict_country = "";
              if (options.searchRestrictCountry)
              {
                var latlng = $.jmap.GMap2.getCenter();
                restrict_country = "&restrict_country=" + latlng.lat() + "," + latlng.lng();
              }
              $.getJSON(
                options.searchAjaxUri + "?view=name&name=" + escape(loc.val()) + "&limit=" + limit + restrict_country,
                function(data)
                {
                  busy.css("visibility", "hidden");
                  show_tip(
                    "Your search results have been updated with a list of matches. Please click on one of the places shown to recentre the map.<br />" +
                    (options.searchUseTagCloud ? "Larger/more populated places are shown in bigger type." : "")
                  );
                  content.text("");
                  content.append($("<h5>Matches:</h5>"));
                  var resultcount = 0;
                  $.each(
                    data.results,
                    function(i,item)
                    {
                      var li = $("<span/>").append(item.name);
                      cloudify(li, item.population_order, data.resultCount);
                      li.click(
                        function()
                        {
                          $.jmap.GMap2.setCenter(new GLatLng(item.latitude, item.longitude));
                          named_location = item.name;
                          curloc.text(item.name);
                          loc.val(item.name);
                        }
                      );
                      if (i > 0)
                        content.append(", ");
                      content.append(li);
                      resultcount++;
                    }
                  );
                  if (!resultcount)
                    content.append($("<p>Sorry, but no matching terms were found. Please try again.</p>"));
                }
              );
            };
          var markers = Array();
          var update_markers =
            function()
            {
              // Clear existing markers.
              for (var i = 0; i<markers.length; ++i)
                $.jmap.GMap2.removeOverlay(markers[i]);
              markers = Array();

              var latlng = $.jmap.GMap2.getCenter();
              var limit = 20;
              $.getJSON(
                options.searchAjaxUri + "?view=markers&posrad=" + latlng.lat() + "," + latlng.lng() + "," + radius.val() + "&limit=" + limit,
                function(data)
                {
                  $.each(
                    data.results,
                    function(i,item)
                    {
                      $.jmap.createMarker(
                        item,
                        function(marker)
                        {
                          $.jmap.GMap2.addOverlay(marker);
                          markers.push(marker);
                        }
                      );
                    }
                  );
                }
              );
            };
          var first_nearby = true;
          var update_nearby =
            function()
            {
              var latlng = $.jmap.GMap2.getCenter();
              var limit = 25;
              $.getJSON(
                options.searchAjaxUri + "?view=nearby&posrad=" + latlng.lat() + "," + latlng.lng() + "," + radius.val() + "&limit=" + limit,
                function(data)
                {
                  update_markers();
                  busy.css("visibility", "hidden");
                  if (!first_nearby)
                  {
                    show_tip(
                      "The list of nearby locations has been updated. Please click on one of the places shown to recentre the map.<br />" +
                      (options.searchUseTagCloud ? "Larger/more populated places are shown in bigger type. " : "") +
                      "They are ordered by their distance from the centre point of the map."
                    );
                  }
                  first_nearby = false;
                  content.text("");
                  content.append($("<h5>Nearby:</h5>"));
                  var resultcount = 0;
                  $.each(
                    data.results,
                    function(i,item)
                    {
                      var li = $("<span/>").append(item.name);
                      cloudify(li, item.population_order, data.resultCount);
                      li.click(
                        function()
                        {
                          $.jmap.GMap2.setCenter(new GLatLng(item.latitude, item.longitude));
                          named_location = item.name;
                          curloc.text(item.name);
                          loc.val(item.name);
                        }
                      );
                      if (i > 0)
                        content.append(", ");
                      content.append(li);
                      resultcount++;
                    }
                  );
                  if (!resultcount)
                    content.append($("<p>Sorry, but I cannot find any nearby locations.</p>"));
                }
              );
            };
          /* Wire things up */
          var timeout = false;
          var dp3 =
            function(num)
            {
              return Math.round(num * 1000) / 1000;
            };
          var update =
            function(zoomMode)
            {
              named_location = false;
              loc.val(loctext);
              var center = $.jmap.GMap2.getCenter();
              curloc.text("lat/lng: " + dp3(center.lat()) + "/" + dp3(center.lng()));
              drawcircle(zoomMode);
              if (timeout)
                window.clearTimeout(timeout);
              busy.css("visibility", "visible");
              timeout = window.setTimeout(update_nearby, 500);
            };

          loc.keyup(
            function()
            {
              if (timeout)
                window.clearTimeout(timeout);
              busy.css("visibility", "visible");

              locval = loc.val();
              if ('' == locval || loctext == locval)
                return;
              timeout = window.setTimeout(update_search, 1000);
            }
          );
          update("initial");
          radius.change(
            function()
            {
              var save_name = named_location;
              update("radius");
              if (save_name)
              {
                named_location = save_name;
                curloc.text(named_location);
                loc.val(named_location);
              }
            }
          );
          map_events.push(GEvent.addListener($.jmap.GMap2, "move", update));
          map_events.push(GEvent.addListener($.jmap.GMap2, "moveend", update));
          map_events.push(GEvent.addListener($.jmap.GMap2, "click", function(overlay, point){$.jmap.GMap2.setCenter(point);}));
        }
      );

    }
    catch(er) { }
  };


})(jQuery);
