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

export interface StandardInfo {
  frameRate: VAPI.Video.FrameRate;
  height: 486 | 576 | 720 | 1080 | 2160;
  interlaced: boolean;
  restrict_to_transport_formats?: readonly VAPI.Video.TransportFormat[];
  segmented?: boolean;
  width: number;
  t_height: number;
  t_width: number;
}

const STANDARD_INFO: { [name in VAPI.Video.Standard]: StandardInfo } = {
  HD1080i50: {
    interlaced: true,
    height: 1080,
    width: 1920,
    frameRate: "f25",
    t_height: 1125,
    t_width: 2640,
  },
  HD1080i59_94: {
    frameRate: "f29_97",
    height: 1080,
    interlaced: true,
    width: 1920,
    t_height: 1125,
    t_width: 2200,
  },
  HD1080i60: {
    frameRate: "f30",
    height: 1080,
    interlaced: true,
    width: 1920,
    t_height: 1125,
    t_width: 2200,
  },
  HD1080p23_98: {
    frameRate: "f23_98",
    height: 1080,
    interlaced: false,
    width: 1920,
    t_height: 1125,
    t_width: 2750,
  },
  HD1080p24: {
    interlaced: false,
    height: 1080,
    width: 1920,
    frameRate: "f24",
    t_height: 1125,
    t_width: 2750,
  },
  HD1080p24_DCI: {
    frameRate: "f24",
    height: 1080,
    interlaced: false,
    width: 2048,
    t_height: 1125,
    t_width: 2750,
  },
  HD1080p25: {
    interlaced: false,
    height: 1080,
    width: 1920,
    frameRate: "f25",
    t_height: 1125,
    t_width: 2640,
  },
  HD1080p29_97: {
    frameRate: "f29_97",
    height: 1080,
    interlaced: false,
    width: 1920,
    t_height: 1125,
    t_width: 2200,
  },
  HD1080p30: {
    interlaced: false,
    height: 1080,
    width: 1920,
    frameRate: "f30",
    t_height: 1125,
    t_width: 2200,
  },
  HD1080p50: {
    interlaced: false,
    height: 1080,
    width: 1920,
    frameRate: "f50",
    t_height: 1125,
    t_width: 2640,
  },
  HD1080p59_94: {
    frameRate: "f59_94",
    height: 1080,
    interlaced: false,
    width: 1920,
    t_height: 1125,
    t_width: 2200,
  },
  HD1080p60: {
    interlaced: false,
    height: 1080,
    width: 1920,
    frameRate: "f60",
    t_height: 1125,
    t_width: 2200,
  },
  HD1080sF23_98: {
    frameRate: "f23_98",
    height: 1080,
    interlaced: false,
    segmented: true,
    width: 1920,
    t_height: 1125,
    t_width: 2750,
  },
  HD1080sF24: {
    frameRate: "f24",
    height: 1080,
    interlaced: false,
    segmented: true,
    width: 1920,
    t_height: 1125,
    t_width: 2750,
  },
  HD1080sF25: {
    frameRate: "f25",
    height: 1080,
    interlaced: false,
    segmented: true,
    width: 1920,
    t_height: 1125,
    t_width: 2640,
  },
  HD1080sF25_DCI: {
    frameRate: "f25",
    height: 1080,
    interlaced: false,
    segmented: true,
    width: 2048,
    t_height: 1125,
    t_width: 2640,
  },
  HD1080sF29_97: {
    frameRate: "f29_97",
    height: 1080,
    interlaced: false,
    segmented: true,
    width: 1920,
    t_height: 1125,
    t_width: 2200,
  },
  HD1080sF30: {
    frameRate: "f30",
    height: 1080,
    interlaced: false,
    segmented: true,
    width: 1920,
    t_height: 1125,
    t_width: 2200,
  },
  HD2160p23_98: {
    frameRate: "f23_98",
    height: 2160,
    interlaced: false,
    restrict_to_transport_formats: [
      "ST2110_GPM",
      "ST2110_BPM",
      "ST2110_22_JpegXS",
      "ST2042_raw",
    ] as VAPI.Video.TransportFormat[],
    width: 3840,
    t_height: 2250,
    t_width: 5500,
  },
  HD2160p24: {
    frameRate: "f24",
    height: 2160,
    interlaced: false,
    restrict_to_transport_formats: [
      "ST2110_GPM",
      "ST2110_BPM",
      "ST2110_22_JpegXS",
      "ST2042_raw",
    ] as VAPI.Video.TransportFormat[],
    width: 3840,
    t_height: 2250,
    t_width: 5500,
  },
  HD2160p25: {
    frameRate: "f25",
    height: 2160,
    interlaced: false,
    restrict_to_transport_formats: [
      "ST2110_GPM",
      "ST2110_BPM",
      "ST2110_22_JpegXS",
      "ST2042_raw",
    ] as VAPI.Video.TransportFormat[],
    width: 3840,
    t_height: 2250,
    t_width: 5280,
  },
  HD2160p29_97: {
    frameRate: "f29_97",
    height: 2160,
    interlaced: false,
    restrict_to_transport_formats: [
      "ST2110_GPM",
      "ST2110_BPM",
      "ST2110_22_JpegXS",
      "ST2042_raw",
    ] as VAPI.Video.TransportFormat[],
    width: 3840,
    t_height: 2250,
    t_width: 4400,
  },
  HD2160p30: {
    frameRate: "f30",
    height: 2160,
    interlaced: false,
    restrict_to_transport_formats: [
      "ST2110_GPM",
      "ST2110_BPM",
      "ST2110_22_JpegXS",
      "ST2042_raw",
    ] as VAPI.Video.TransportFormat[],
    width: 3840,
    t_height: 2250,
    t_width: 4400,
  },
  HD2160p50: {
    frameRate: "f50",
    height: 2160,
    interlaced: false,
    restrict_to_transport_formats: [
      "ST2110_GPM",
      "ST2110_BPM",
      "ST2110_22_JpegXS",
      "ST2042_raw",
    ] as VAPI.Video.TransportFormat[],
    width: 3840,
    t_height: 2250,
    t_width: 5280,
  },
  HD2160p59_94: {
    frameRate: "f59_94",
    height: 2160,
    interlaced: false,
    restrict_to_transport_formats: [
      "ST2110_GPM",
      "ST2110_BPM",
      "ST2110_22_JpegXS",
      "ST2042_raw",
    ] as VAPI.Video.TransportFormat[],
    width: 3840,
    t_height: 2250,
    t_width: 4400,
  } as const,
  HD2160p60: {
    frameRate: "f60",
    height: 2160,
    interlaced: false,
    restrict_to_transport_formats: [
      "ST2110_GPM",
      "ST2110_BPM",
      "ST2110_22_JpegXS",
      "ST2042_raw",
    ] as VAPI.Video.TransportFormat[],
    width: 3840,
    t_height: 2250,
    t_width: 4400,
  } as const,
  HD720p25: {
    interlaced: false,
    height: 720,
    width: 1280,
    frameRate: "f25",
    t_height: 750,
    t_width: 3960,
  },
  HD720p29_97: {
    frameRate: "f29_97",
    height: 720,
    interlaced: false,
    width: 1280,
    t_height: 750,
    t_width: 3300,
  },
  HD720p30: {
    interlaced: false,
    height: 720,
    width: 1280,
    frameRate: "f30",
    t_height: 750,
    t_width: 3300,
  },
  HD720p50: {
    interlaced: false,
    height: 720,
    width: 1280,
    frameRate: "f50",
    t_height: 750,
    t_width: 1980,
  },
  HD720p59_94: {
    frameRate: "f59_94",
    height: 720,
    interlaced: false,
    width: 1280,
    t_height: 750,
    t_width: 1650,
  },
  HD720p60: {
    interlaced: false,
    height: 720,
    width: 1280,
    frameRate: "f60",
    t_height: 750,
    t_width: 1650,
  },
  NTSC: {
    frameRate: "f29_97",
    height: 486,
    interlaced: true,
    restrict_to_transport_formats: ["ST2022_6"],
    width: 720,
    t_height: 525,
    t_width: 858,
  },
  PAL: {
    frameRate: "f25",
    height: 576,
    interlaced: true,
    restrict_to_transport_formats: ["ST2022_6"],
    width: 720,
    t_height: 625,
    t_width: 864,
  },
} as const;

export function frameRate(std: VAPI.Video.Standard) {
  return STANDARD_INFO[std].frameRate;
}

export function exactFrameRate(std: VAPI.Video.Standard) {
  switch (frameRate(std)) {
    case "f23_98":
      return [24000, 1001];
    case "f24":
      return [24, 1];
    case "f25":
      return [25, 1];
    case "f29_97":
      return [30000, 1001];
    case "f30":
      return [30, 1];
    case "f50":
      return [50, 1];
    case "f59_94":
      return [60000, 1001];
    case "f60":
      return [60, 1];
  }
}

export function vstart(
  std: VAPI.Video.Standard,
  field: "first" | "second" | null,
) {
  switch (std) {
    case "PAL":
      return field === "second" ? 336 : 23;
    case "NTSC":
      return field === "second" ? 283 : 20;
    default: {
      const std_info = STANDARD_INFO[std];
      if (std_info.height === 720) {
        enforce(field !== "second");
        return 26;
      } else if (std_info.interlaced || std_info.segmented) {
        return field === "second" ? 584 : 21;
      } else {
        enforce(field !== "second");
        return 42;
      }
    }
  }
}

export function vend(
  std: VAPI.Video.Standard,
  field: "first" | "second" | null,
) {
  switch (std) {
    case "PAL":
      return field === "second" ? 624 : 311;
    case "NTSC":
      return field === "second" ? 1 : 264;
    default: {
      const std_info = STANDARD_INFO[std];
      if (std_info.height === 720) {
        enforce(field !== "second");
        return 746;
      } else if (std_info.interlaced || std_info.segmented) {
        return field === "second" ? 1124 : 561;
      } else {
        enforce(field !== "second");
        return 1122;
      }
    }
  }
}

export function framelength(std: VAPI.Video.Standard): Duration {
  const [num, denom] = exactFrameRate(std);
  return new Duration(enforce_nonnull(denom) / enforce_nonnull(num), "s");
}

export function modulo_framelength(
  d: Duration,
  std: VAPI.Video.Standard,
): Duration {
  const FL = framelength(std).s();
  const secs = d.s();
  return new Duration(
    Math.sign(secs) * (((Math.abs(secs) + FL / 2) % FL) - FL / 2),
    "s",
  );
}

export function standard_info(std: VAPI.Video.Standard) {
  return STANDARD_INFO[std];
}

export function is_3G(std: VAPI.Video.Standard) {
  switch (std) {
    case "HD1080p50":
    case "HD1080p59_94":
    case "HD1080p60":
      return true;
    default:
      return false;
  }
}

export function is_3G_or_higher(std: VAPI.Video.Standard) {
  switch (std) {
    case "HD1080p50":
    case "HD1080p59_94":
    case "HD1080p60":
    case "HD2160p23_98":
    case "HD2160p24":
    case "HD2160p25":
    case "HD2160p29_97":
    case "HD2160p30":
    case "HD2160p50":
    case "HD2160p59_94":
    case "HD2160p60":
      return true;
    default:
      return false;
  }
}

export function is_higher_than_3G(std: VAPI.Video.Standard) {
  switch (std) {
    case "HD2160p23_98":
    case "HD2160p24":
    case "HD2160p25":
    case "HD2160p29_97":
    case "HD2160p30":
    case "HD2160p50":
    case "HD2160p59_94":
    case "HD2160p60":
      return true;
    default:
      return false;
  }
}

export function is_12G(std: VAPI.Video.Standard) {
  switch (std) {
    case "HD2160p50":
    case "HD2160p59_94":
    case "HD2160p60":
      return true;
    default:
      return false;
  }
}

export function get_audio_signal_generator(vm: VAPI.VM.Any) {
  return vm instanceof VAPI.AT1130.Root
    ? vm.audio_signal_generator!.genlock.row(0).f48000
    : vm.audio_signal_generator!;
}

export function guess_standard(pars: { width: number; height: number }) {
  for (const std of VAPI.Video.Enums.Standard) {
    if (
      standard_info(std).width === pars.width &&
      standard_info(std).height === pars.height
    )
      return std;
  }
  throw new Error(
    `Found no supported standard matching the supplied image dimensions of ${pars.width}x${pars.height} pixels`,
  );
}
