import { createRequire } from "node:module";
var __require = /* @__PURE__ */ createRequire(import.meta.url);

// src/routes/codec.ts
class BinaryCodec {
  airportIndex;
  airportList;
  stringTable;
  stringIndex;
  constructor() {
    this.airportIndex = new Map;
    this.airportList = [];
    this.stringTable = [];
    this.stringIndex = new Map;
  }
  encode(routes) {
    this.airportIndex.clear();
    this.airportList = [];
    this.stringTable = [];
    this.stringIndex.clear();
    for (const [iata, airport] of Object.entries(routes)) {
      if (!this.airportIndex.has(iata)) {
        this.airportIndex.set(iata, this.airportList.length);
        this.airportList.push({ iata, lat: 0, lon: 0 });
      }
      for (const route of airport.routes) {
        if (!this.airportIndex.has(route.iata)) {
          this.airportIndex.set(route.iata, this.airportList.length);
          this.airportList.push({ iata: route.iata, lat: 0, lon: 0 });
        }
      }
    }
    const carrierSet = new Set;
    for (const airport of Object.values(routes)) {
      for (const route of airport.routes) {
        for (const carrier of route.carriers) {
          carrierSet.add(carrier.iata);
        }
      }
    }
    this.stringTable = Array.from(carrierSet);
    for (let i = 0;i < this.stringTable.length; i++) {
      const carrier = this.stringTable[i];
      if (carrier !== undefined) {
        this.stringIndex.set(carrier, i);
      }
    }
    const airportCount = this.airportList.length;
    const headerSize = 5;
    const stringTableSize = this.stringTable.reduce((sum, s) => sum + 1 + s.length, 0);
    const iataTableSize = airportCount * 3;
    const offsetTableSize = airportCount * 4;
    let routeDataSize = 0;
    for (const airport of this.airportList) {
      const airportData = routes[airport.iata];
      const routeList = airportData ? airportData.routes : [];
      routeDataSize += this.varIntLength(routeList.length);
      for (const route of routeList) {
        routeDataSize += 2;
        routeDataSize += this.varIntLength(route.km);
        routeDataSize += this.varIntLength(route.min);
        routeDataSize += 1;
        routeDataSize += route.carriers.length;
      }
    }
    const totalSize = headerSize + stringTableSize + iataTableSize + offsetTableSize + routeDataSize;
    const buffer = new Uint8Array(totalSize);
    const writer = new BinaryWriter(buffer);
    writer.writeUint8(1);
    writer.writeUint16(airportCount);
    writer.writeUint16(this.stringTable.length);
    for (const s of this.stringTable) {
      writer.writeUint8(s.length);
      for (let i = 0;i < s.length; i++) {
        writer.writeUint8(s.charCodeAt(i));
      }
    }
    for (const airport of this.airportList) {
      const iata = airport.iata;
      writer.writeUint8(iata.charCodeAt(0) ?? 32);
      writer.writeUint8(iata.charCodeAt(1) ?? 32);
      writer.writeUint8(iata.charCodeAt(2) ?? 32);
    }
    const offsetTablePos = writer.pos;
    writer.pos += offsetTableSize;
    const routeOffsets = new Array(airportCount);
    for (const airport of this.airportList) {
      const airportId = this.airportIndex.get(airport.iata);
      if (airportId === undefined)
        continue;
      routeOffsets[airportId] = writer.pos - (headerSize + stringTableSize + iataTableSize + offsetTableSize);
      const airportData = routes[airport.iata];
      const routeList = airportData ? airportData.routes : [];
      writer.writeVarInt(routeList.length);
      for (const route of routeList) {
        const destId = this.airportIndex.get(route.iata);
        if (destId === undefined)
          continue;
        writer.writeUint16(destId);
        writer.writeVarInt(route.km);
        writer.writeVarInt(route.min);
        writer.writeUint8(route.carriers.length);
        for (const carrier of route.carriers) {
          const carrierId = this.stringIndex.get(carrier.iata);
          if (carrierId === undefined)
            continue;
          writer.writeUint8(carrierId);
        }
      }
    }
    const savedPos = writer.pos;
    writer.pos = offsetTablePos;
    for (let i = 0;i < airportCount; i++) {
      writer.writeUint32(routeOffsets[i] ?? 0);
    }
    writer.pos = savedPos;
    return buffer.buffer;
  }
  decode(buffer) {
    const data = new Uint8Array(buffer);
    const reader = new BinaryReader(data);
    const version = reader.readUint8();
    if (version !== 1) {
      throw new Error(`Unsupported binary codec version: ${version}`);
    }
    const airportCount = reader.readUint16();
    const stringTableSize = reader.readUint16();
    const stringTable = [];
    for (let i = 0;i < stringTableSize; i++) {
      const len = reader.readUint8();
      const chars = [];
      for (let j = 0;j < len; j++) {
        chars.push(String.fromCharCode(reader.readUint8()));
      }
      stringTable.push(chars.join(""));
    }
    const iataList = [];
    for (let i = 0;i < airportCount; i++) {
      const c1 = String.fromCharCode(reader.readUint8());
      const c2 = String.fromCharCode(reader.readUint8());
      const c3 = String.fromCharCode(reader.readUint8());
      iataList.push((c1 + c2 + c3).trim());
    }
    const routeOffsets = [];
    for (let i = 0;i < airportCount; i++) {
      routeOffsets.push(reader.readUint32());
    }
    const routeDataBase = reader.pos;
    const airports = {};
    for (let i = 0;i < airportCount; i++) {
      const iata = iataList[i];
      if (!iata)
        continue;
      const offset = routeOffsets[i];
      if (offset === undefined)
        continue;
      reader.pos = routeDataBase + offset;
      const routeCount = reader.readVarInt();
      const routes = [];
      for (let j = 0;j < routeCount; j++) {
        const destId = reader.readUint16();
        const km = reader.readVarInt();
        const min = reader.readVarInt();
        const carrierCount = reader.readUint8();
        const carriers = [];
        for (let k = 0;k < carrierCount; k++) {
          const carrierId = reader.readUint8();
          const carrierIata = stringTable[carrierId];
          if (carrierIata !== undefined) {
            carriers.push({ iata: carrierIata });
          }
        }
        const destIata = iataList[destId];
        if (destIata === undefined)
          continue;
        routes.push({
          carriers,
          iata: destIata,
          km,
          min
        });
      }
      airports[iata] = { iata, routes };
    }
    return airports;
  }
  toRad(degrees) {
    return degrees * Math.PI / 180;
  }
  varIntLength(value) {
    let length = 0;
    do {
      value >>>= 7;
      length++;
    } while (value > 0);
    return length;
  }
}

class BinaryWriter {
  buffer;
  pos;
  constructor(buffer, pos = 0) {
    this.buffer = buffer;
    this.pos = pos;
  }
  writeUint8(value) {
    this.buffer[this.pos++] = value & 255;
  }
  writeUint16(value) {
    this.buffer[this.pos++] = value >> 8 & 255;
    this.buffer[this.pos++] = value & 255;
  }
  writeUint32(value) {
    this.buffer[this.pos++] = value >> 24 & 255;
    this.buffer[this.pos++] = value >> 16 & 255;
    this.buffer[this.pos++] = value >> 8 & 255;
    this.buffer[this.pos++] = value & 255;
  }
  writeVarInt(value) {
    while (value > 127) {
      this.buffer[this.pos++] = value & 127 | 128;
      value >>>= 7;
    }
    this.buffer[this.pos++] = value & 127;
  }
}

class BinaryReader {
  data;
  pos;
  constructor(data) {
    this.data = data;
    this.pos = 0;
  }
  readUint8() {
    const value = this.data[this.pos];
    if (value === undefined) {
      throw new Error("Unexpected end of buffer");
    }
    this.pos++;
    return value;
  }
  readUint16() {
    const b1 = this.data[this.pos];
    const b2 = this.data[this.pos + 1];
    if (b1 === undefined || b2 === undefined) {
      throw new Error("Unexpected end of buffer");
    }
    this.pos += 2;
    return b1 << 8 | b2;
  }
  readUint32() {
    const b1 = this.data[this.pos];
    const b2 = this.data[this.pos + 1];
    const b3 = this.data[this.pos + 2];
    const b4 = this.data[this.pos + 3];
    if (b1 === undefined || b2 === undefined || b3 === undefined || b4 === undefined) {
      throw new Error("Unexpected end of buffer");
    }
    this.pos += 4;
    return b1 << 24 | b2 << 16 | b3 << 8 | b4;
  }
  readVarInt() {
    let result = 0;
    let shift = 0;
    let b;
    do {
      b = this.readUint8();
      result |= (b & 127) << shift;
      shift += 7;
    } while (b & 128);
    return result;
  }
}

export { __require, BinaryCodec };
