import * as VAPI from "../artefacts/vapi/index.js";
import { asyncFilter, asyncIter, Duration, enforce, Watcher } from "vscript";

export async function await_genlock_state(
  genlock: VAPI.AT1101.Genlock.All | VAPI.AT1130.Genlock.AT1130Genlock,
  state: VAPI.Servos.State,
  pars?: {
    timeout?: Duration;
    on_state_loss?: (
      genlock: VAPI.AT1101.Genlock.All | VAPI.AT1130.Genlock.SingleGenlockState,
    ) => void;
  },
): Promise<Watcher[]> {
  const result: Watcher[] = [];
  if (genlock instanceof VAPI.AT1101.Genlock.All) {
    await genlock.state.wait_until((s) => s === state, pars);
    if (pars?.on_state_loss) {
      result.push(
        await genlock.state.watch((s) => {
          if (s !== "Calibrated") {
            pars?.on_state_loss?.(genlock);
          }
        }),
      );
    }
  } else {
    enforce(genlock instanceof VAPI.AT1130.Genlock.AT1130Genlock);
    const ALL_LANES = [
      genlock.backend.lanes.audio,
      genlock.backend.lanes.video_f50_ish,
      genlock.backend.lanes.video_f59_ish,
    ];
    await Promise.all(
      ALL_LANES.map((x) =>
        x.wait_until((s) => s?.state === state, {
          timeout: pars?.timeout ?? new Duration(60, "s"),
        }),
      ),
    );
    if (pars?.on_state_loss) {
      for (const x of ALL_LANES) {
        result.push(
          await x.watch((x) => {
            if (x === null) {
              enforce(false);
            }
            if (x.state !== state) {
              pars?.on_state_loss?.(x);
            }
          }),
        );
      }
    }
  }
  return result;
}

export async function await_genlock_calibration(vm: VAPI.VM.Any, timeout: Duration) {
  if (!vm.genlock) {
    enforce((await vm.system.selected_fpga.read()) === "PCAP_40GbE");
    throw new Error(
      `${vm.raw.ip} is currently running a pcap application, hence there is no genlock to calibrate`,
    );
  }
  if (vm instanceof VAPI.AT1101.Root) {
    await vm.genlock.state.wait_until((s) => s === "Calibrated", { timeout });
  } else {
    await asyncIter(
      await asyncFilter(
        [...vm.genlock.instances],
        async (genlock) => (await genlock.t_src.status.read()) !== null,
      ),
      async (genlock) => {
        await await_genlock_state(genlock, "Calibrated", { timeout });
      },
    );
  }
}
