unit uMapMarkerJs;

interface

procedure RegisterMapMarkerJs;

implementation

procedure RegisterMapMarkerJs;
begin
  asm
    if (window.emiMapMarkerJsRegistered) {
      return;
    }

    window.emiMapMarkerJsRegistered = true;

    function htmlEncode(value) {
      return String(value || '')
        .replace(new RegExp('&', 'g'), '&amp;')
        .replace(new RegExp('<', 'g'), '&lt;')
        .replace(new RegExp('>', 'g'), '&gt;')
        .replace(new RegExp(String.fromCharCode(34), 'g'), '&quot;');
    }

    function readMarkerMeta(popupHtml) {
      var result = {
        markerType: "",
        unitBadgeText: "",
        complaintBadgeText: "",
        complaintStatus: "",
        pickerLabel: ""
      };

      try {
        var temp = document.createElement("div");
        temp.innerHTML = popupHtml || "";

        var meta = temp.querySelector(".emi-marker-meta");

        if (meta) {
          result.markerType = meta.getAttribute("data-marker-type") || "";
          result.unitBadgeText = meta.getAttribute("data-unit-badge") || "";
          result.complaintBadgeText = meta.getAttribute("data-priority-badge") || "";
          result.complaintStatus = meta.getAttribute("data-complaint-status") || "";
          result.pickerLabel = meta.getAttribute("data-picker-label") || "";
        }
      } catch(e) {}

      return result;
    }

    function getUnitBadgeColor(iconUrl) {
      if (iconUrl && iconUrl.indexOf("car_fire") >= 0) {
        return "#B05454";
      }

      if (iconUrl && iconUrl.indexOf("car_ems") >= 0) {
        return "#5D9270";
      }

      if (iconUrl && iconUrl.indexOf("car_police") >= 0) {
        return "#637AAE";
      }

      return "#637AAE";
    }

    function getComplaintBadgeColors(complaintStatus) {
      if (complaintStatus === "onscene") {
        return {
          background: "#22C55E",
          text: "#FFFFFF"
        };
      }

      if (complaintStatus === "attached") {
        return {
          background: "#FFFF00",
          text: "#111827"
        };
      }

      return {
        background: "#FF8080",
        text: "#FFFFFF"
      };
    }

    function clearDefaultMarkerBindings(marker) {
      try {
        if (marker.unbindTooltip) {
          marker.unbindTooltip();
        }
      } catch(e) {}

      try {
        if (marker.unbindPopup) {
          marker.unbindPopup();
        }
      } catch(e) {}
    }

    function removeMarkerTitle(marker, options) {
      try {
        options.title = "";
      } catch(e) {}

      try {
        if (marker._icon) {
          marker._icon.removeAttribute("title");
        }
      } catch(e) {}
    }

    function getMarkerIconUrl(options) {
      try {
        if (options.icon &&
            options.icon.options &&
            options.icon.options.iconUrl) {
          return options.icon.options.iconUrl;
        }
      } catch(e) {}

      return null;
    }

    function buildMarkerIconHtml(meta, iconUrl) {
      var isUnit = meta.markerType === "unit";
      var isComplaint = meta.markerType === "complaint";

      var wrapClass = isUnit
        ? "emi-marker-wrap emi-marker-wrap-unit"
        : (isComplaint ? "emi-marker-wrap emi-marker-wrap-complaint" : "emi-marker-wrap");

      var html =
        '<div class="' + wrapClass + '">' +
          '<img src="' + iconUrl + '" class="emi-marker-img" onerror="this.onerror=null;this.src=\'' + getFallbackIconUrl(meta) + '\';">';

      if (isUnit && meta.unitBadgeText) {
        html +=
          '<div class="emi-unit-badge" style="background:' + getUnitBadgeColor(iconUrl) + ';">' +
            htmlEncode(meta.unitBadgeText) +
          '</div>';
      } else if (isComplaint && meta.complaintBadgeText) {
        var complaintColors = getComplaintBadgeColors(meta.complaintStatus);

        html +=
          '<div class="emi-complaint-badge" style="background:' + complaintColors.background + ';color:' + complaintColors.text + ';">' +
            htmlEncode(meta.complaintBadgeText) +
          '</div>';
      }

      html += '</div>';

      return html;
    }

    function customizeMarkerIcon(marker, meta, iconUrl) {
      if (!iconUrl) {
        return;
      }

      marker.setIcon(L.divIcon({
        html: buildMarkerIconHtml(meta, iconUrl),
        iconSize: [32, 40],
        iconAnchor: [16, 32],
        popupAnchor: [0, -24],
        className: ""
      }));
    }

    function getDefaultPickerLabel(meta) {
      if (meta.pickerLabel) {
        return meta.pickerLabel;
      }

      if (meta.markerType === "unit") {
        return "Unit " + (meta.unitBadgeText || "");
      }

      if (meta.markerType === "complaint") {
        return "Complaint";
      }

      return "Marker";
    }

    function markerIsVisible(layer) {
      try {
        if (!layer || !layer.getLatLng || !layer.options) {
          return false;
        }

        if (layer.options.emiMarkerType !== "unit" &&
            layer.options.emiMarkerType !== "complaint") {
          return false;
        }

        var el = layer.getElement ? layer.getElement() : layer._icon;

        if (el) {
          var style = window.getComputedStyle ? window.getComputedStyle(el) : el.style;

          if (style.display === "none" ||
              style.visibility === "hidden" ||
              style.opacity === "0") {
            return false;
          }
        }

        return true;
      } catch(e) {
        return false;
      }
    }

    function findNearbyMarkers(map, marker, radius) {
      var nearby = [];

      try {
        var basePoint = map.latLngToLayerPoint(marker.getLatLng());

        map.eachLayer(function(layer) {
          try {
            if (!markerIsVisible(layer)) {
              return;
            }

            var layerPoint = map.latLngToLayerPoint(layer.getLatLng());
            var dx = layerPoint.x - basePoint.x;
            var dy = layerPoint.y - basePoint.y;
            var distance = Math.sqrt((dx * dx) + (dy * dy));

            if (distance <= radius) {
              nearby.push(layer);
            }
          } catch(e) {}
        });
      } catch(e) {}

      nearby.sort(function(a, b) {
        var at = (a.options.emiMarkerType === "unit") ? 0 : 1;
        var bt = (b.options.emiMarkerType === "unit") ? 0 : 1;

        if (at !== bt) {
          return at - bt;
        }

        return String(a.options.emiPickerLabel || "")
          .localeCompare(String(b.options.emiPickerLabel || ""));
      });

      return nearby;
    }

    function buildPickerHtml(nearby) {
    var html =
      '<div class="emi-marker-picker px-1 py-1" style="width:240px;">';

    var lastType = '';

    for (var i = 0; i < nearby.length; i++) {
      var markerType = nearby[i].options.emiMarkerType || '';
      var label = nearby[i].options.emiPickerLabel || 'Marker';

      if (markerType !== lastType) {
        if (i > 0) {
          html += '<div class="emi-marker-picker-divider"></div>';
        }

        if (markerType === 'unit') {
          html += '<div class="emi-marker-picker-section">Units</div>';
        } else if (markerType === 'complaint') {
          html += '<div class="emi-marker-picker-section">Complaints</div>';
        }

        lastType = markerType;
      }

      html +=
        '<button type="button" class="btn btn-light btn-sm w-100 text-start mb-1" ' +
          'onclick="window.emiOpenMapMarkerPopup(' + i + ')">' +
          htmlEncode(label) +
        '</button>';
    }

    html += '</div>';

    return html;
  }

  function getFallbackIconUrl(meta) {
    if (meta.markerType === 'complaint') {
      if (meta.complaintStatus === 'attached') {
        return 'assets/markers/default_attached.png';
      }

      if (meta.complaintStatus === 'onscene') {
        return 'assets/markers/default_onscene.png';
      }

      return 'assets/markers/default_notattached.png';
    }

    if (meta.markerType === 'unit') {
      return 'assets/markers/car_default.png';
    }

    return 'assets/markers/default_notattached.png';
  }

    function openMarkerPicker(map, marker, nearby) {
      window.emiNearbyMarkerPick = nearby;

      window.emiOpenMapMarkerPopup = function(index) {
        var target = window.emiNearbyMarkerPick ? window.emiNearbyMarkerPick[index] : null;

        if (!target) {
          return;
        }

        try {
          map.closePopup();
        } catch(e) {}

        try {
          target.openPopup();
        } catch(e) {}
      };

      L.popup({
        className: "emi-tip emi-picker-tip",
        maxWidth: 260,
        closeButton: false,
        autoPan: true
      })
      .setLatLng(marker.getLatLng())
      .setContent(buildPickerHtml(nearby))
      .openOn(map);
    }

    function bindMarkerPopup(marker, popupHtml, meta) {
      marker.bindPopup(popupHtml, {
        className: "emi-tip",
        maxWidth: 260,
        closeButton: false,
        autoPan: true
      });

      try {
        marker.off("click");
      } catch(e) {}

      marker.on("click", function(ev) {
        try {
          if (ev && ev.originalEvent) {
            L.DomEvent.stop(ev.originalEvent);
          }
        } catch(e) {}

        var map = marker._map;

        if (!map) {
          try {
            marker.openPopup();
          } catch(e) {}

          return;
        }

        var nearby = findNearbyMarkers(map, marker, 32);

        if (nearby.length <= 1) {
          marker.openPopup();
          return;
        }

        openMarkerPicker(map, marker, nearby);
      });
    }

    window.emiCustomizeMarker = function(marker) {
      var options = marker.options || {};
      var rawTitle = options.title || options.tooltipHtml || "";
      var dataString = options.datastring || "";

      if (dataString === "device") {
        clearDefaultMarkerBindings(marker);
        return;
      }

      options.tooltipHtml = rawTitle;

      var popupHtml = options.tooltipHtml || "";
      var iconUrl = getMarkerIconUrl(options);
      var meta = readMarkerMeta(popupHtml);

      removeMarkerTitle(marker, options);
      clearDefaultMarkerBindings(marker);

      customizeMarkerIcon(marker, meta, iconUrl);

      options.emiPopupHtml = popupHtml;
      options.emiMarkerType = meta.markerType;
      options.emiPickerLabel = getDefaultPickerLabel(meta);

      bindMarkerPopup(marker, popupHtml, meta);

      removeMarkerTitle(marker, options);
    };
  end;
end;

end.
