import * as VAPI from "../artefacts/vapi/index.js";
import { Timestamp, VSocket, Watcher } from "vscript";

// keeps track of a given VSocket's PTP clock relative to the local system clock,
export class ClockTracker {
  private anchor: [reference_time: Timestamp, date_ms: number] = [
    new Timestamp(0),
    0,
  ];
  private watcher: Watcher | undefined;

  private constructor() {}

  private async initialize(vsocket: VSocket) {
    if (vsocket.build_info.version.startsWith("1.")) {
      this.anchor = [
        new Timestamp(
          (
            await vsocket.read<any[]>({
              kwl: "p_t_p_clock.micro_epochs",
              kw: "current",
            })
          )[2] as string | number,
        ),
        new Date().valueOf(),
      ];
      this.watcher = await vsocket.watch<any[]>(
        { kwl: "p_t_p_clock.micro_epochs", kw: "current" },
        (x) =>
          (this.anchor = [
            new Timestamp(x[2] as string | number),
            new Date().valueOf(),
          ]),
      );
    } else {
      const vm = VAPI.VM.adopt(vsocket);
      const t_first = (
        await vm.p_t_p_clock.micro_epochs.wait_until((_) => true)
      ).current.reference_time;
      this.anchor = [t_first, new Date().valueOf()];
      this.watcher = await vm.p_t_p_clock.micro_epochs.watch((epochs) => {
        this.anchor = [epochs.current.reference_time, new Date().valueOf()];
      });
    }
  }

  public close() {
    this.watcher?.unwatch();
    this.watcher = undefined;
  }

  public to_ptp(date: Date): number {
    const delta = date.valueOf() - this.anchor[1];
    const t =
      /* round to milliseconds */ Math.round(
        this.anchor[0].seconds() * 1e3 + delta,
      ) * 1e-3;
    return t;
  }

  public now_ptp(): number {
    return this.to_ptp(new Date());
  }

  static async create(vm_or_socket: VAPI.VM.Any | VSocket) {
    const tracker = new ClockTracker();
    await tracker.initialize(
      vm_or_socket instanceof VSocket ? vm_or_socket : vm_or_socket.raw,
    );
    return tracker;
  }
}
