// src/plugins/aprs-shared.ts
function aprsPacketCategory(packet) {
  const type = (packet.type ?? "").toLowerCase();
  const info = (packet.info ?? "").toLowerCase();
  if (packet.lat != null && packet.lon != null || type.includes("position")) return "position";
  if (type.includes("message") || info.startsWith(":")) return "message";
  if (type.includes("weather") || info.startsWith("_")) return "weather";
  if (type.includes("telemetry") || info.startsWith("t#")) return "telemetry";
  return "other";
}
function aprsCategoryLabel(category) {
  switch (category) {
    case "position":
      return "Position";
    case "message":
      return "Message";
    case "weather":
      return "Weather";
    case "telemetry":
      return "Telemetry";
    default:
      return "Other";
  }
}
function aprsAgeText(timestampMs) {
  if (typeof timestampMs !== "number" || !Number.isFinite(timestampMs)) return "just now";
  const seconds = Math.round(Math.max(0, Date.now() - timestampMs) / 1e3);
  if (seconds < 5) return "just now";
  if (seconds < 60) return `${String(seconds)}s ago`;
  const minutes = Math.round(seconds / 60);
  if (minutes < 60) return `${String(minutes)}m ago`;
  return `${String(Math.round(minutes / 60))}h ago`;
}
function aprsPacketSignature(packet) {
  return [
    packet.srcCall ?? "",
    packet.destCall ?? "",
    packet.path ?? "",
    packet.info ?? "",
    packet.type ?? "",
    packet.lat?.toFixed(4) ?? "",
    packet.lon?.toFixed(4) ?? ""
  ].join("|");
}
function collapseAprsDuplicates(packets) {
  const seen = /* @__PURE__ */ new Set();
  return packets.filter((packet) => {
    const signature = aprsPacketSignature(packet);
    if (seen.has(signature)) return false;
    seen.add(signature);
    return true;
  });
}
function aprsHexBytes(bytes) {
  if (!bytes?.length) return "--";
  return bytes.map((byte) => byte.toString(16).toUpperCase().padStart(2, "0")).join(" ");
}
function renderAprsInfo(packet) {
  if (packet.info_bytes?.length) {
    return packet.info_bytes.map((byte) => renderAprsByte(byte)).join("");
  }
  return Array.from(packet.info ?? "", (character) => renderAprsCharacter(character)).join("");
}
function renderAprsByte(byte) {
  return byte >= 32 && byte <= 126 ? escapeAprsCharacter(String.fromCharCode(byte)) : `<span class="aprs-byte">0x${byte.toString(16).toUpperCase().padStart(2, "0")}</span>`;
}
function renderAprsCharacter(character) {
  const code = character.charCodeAt(0);
  return code >= 32 && code <= 126 ? escapeAprsCharacter(character) : `<span class="aprs-byte">0x${code.toString(16).toUpperCase().padStart(2, "0")}</span>`;
}
function escapeAprsCharacter(character) {
  if (character === "<") return "&lt;";
  if (character === ">") return "&gt;";
  if (character === "&") return "&amp;";
  if (character === '"') return "&quot;";
  return character;
}
var APRS_SPRITE_COLUMNS = 16;
var APRS_SPRITE_CELL_PX = 24;
var APRS_SPRITE_FIRST_CODE = 33;
var APRS_SPRITE_LAST_CODE = 126;
function aprsSpriteOffset(code) {
  if (code.length !== 1) return null;
  const point = code.charCodeAt(0);
  if (point < APRS_SPRITE_FIRST_CODE || point > APRS_SPRITE_LAST_CODE) return null;
  const index = point - APRS_SPRITE_FIRST_CODE;
  const column = index % APRS_SPRITE_COLUMNS;
  const row = Math.floor(index / APRS_SPRITE_COLUMNS);
  return `${String(-column * APRS_SPRITE_CELL_PX)}px ${String(-row * APRS_SPRITE_CELL_PX)}px`;
}
function aprsSymbolSprite(symbolTable, symbolCode) {
  if (!symbolTable || !symbolCode) return null;
  const symbolOffset = aprsSpriteOffset(symbolCode);
  if (!symbolOffset) return null;
  if (symbolTable === "/") {
    return {
      className: "aprs-symbol-primary",
      backgroundPosition: symbolOffset,
      label: `Primary APRS symbol ${symbolTable}${symbolCode}`
    };
  }
  if (symbolTable === "\\") {
    return {
      className: "aprs-symbol-alternate",
      backgroundPosition: symbolOffset,
      label: `Alternate APRS symbol ${symbolTable}${symbolCode}`
    };
  }
  const overlayOffset = aprsSpriteOffset(symbolTable);
  if (!overlayOffset) return null;
  return {
    className: "aprs-symbol-overlaid",
    backgroundPosition: `${overlayOffset}, ${symbolOffset}`,
    label: `Alternate APRS symbol \\${symbolCode} with overlay ${symbolTable}`
  };
}
function renderLocalAprsSymbol(packet, escapeHtml) {
  if (!packet.symbolTable || !packet.symbolCode) return "";
  const sprite = aprsSymbolSprite(packet.symbolTable, packet.symbolCode);
  if (sprite) {
    return `<span class="aprs-symbol ${sprite.className}" role="img" style="background-position:${sprite.backgroundPosition}" title="${escapeHtml(sprite.label)}" aria-label="${escapeHtml(sprite.label)}"></span>`;
  }
  const symbol = escapeHtml(packet.symbolCode);
  const table = packet.symbolTable === "/" ? "Primary" : "Alternate";
  return `<span class="aprs-symbol aprs-symbol-local" title="${table} APRS symbol ${symbol}">${symbol}</span>`;
}
function normalizeAprsPacket(packet, receiver) {
  return {
    rig_id: packet.rig_id || null,
    receiver,
    srcCall: packet.src_call ?? "",
    destCall: packet.dest_call ?? "",
    path: packet.path ?? "",
    info: packet.info ?? "",
    info_bytes: packet.info_bytes ?? [],
    type: packet.packet_type ?? "",
    crcOk: packet.crc_ok ?? false,
    ts_ms: packet.ts_ms ?? null,
    lat: packet.lat ?? null,
    lon: packet.lon ?? null,
    symbolTable: packet.symbol_table ?? null,
    symbolCode: packet.symbol_code ?? null
  };
}

export {
  aprsPacketCategory,
  aprsCategoryLabel,
  aprsAgeText,
  collapseAprsDuplicates,
  aprsHexBytes,
  renderAprsInfo,
  aprsSymbolSprite,
  renderLocalAprsSymbol,
  normalizeAprsPacket
};
