import * as VAPI from "../artefacts/vapi/index.js";
import { Audio, Definitions } from "../artefacts/vapi/index.js";

export type SampleCount = 6 | 12 | 16 | 24 | 32 | 48;

export const PTIME_MAP: Record<SampleCount, Definitions.PacketTime> = {
  6: "p0_125",
  12: "p0_250",
  16: "p0_333",
  24: "p0_500",
  32: "p0_666",
  48: "p1",
} as const;

export const REVERSE_PTIME_MAP: Record<Definitions.PacketTime, SampleCount> = {
  p0_125: 6,
  p0_250: 12,
  p0_333: 16,
  p0_500: 24,
  p0_666: 32,
  p1: 48,
} as const;

export function bytes_per_sample(format: Audio.Format) {
  switch (format) {
    case "L16":
      return 2;
    case "L24":
      return 3;
    case "AM824":
      return 4;
  }
}

export function max_num_channels(pars: {
  audio_format: Audio.Format;
  samplecount: SampleCount;
}): number {
  if (PTIME_MAP[pars.samplecount] === undefined) {
    return 0;
  }
  const MAX_PAYLOAD_BYTES = 1440;
  return Math.min(
    80,
    Math.floor(MAX_PAYLOAD_BYTES / (bytes_per_sample(pars.audio_format) * pars.samplecount)),
  );
}

export function allowed_channelcounts(
  tx: VAPI.Any.RTPTransmitter.AudioStreamer,
  pars: {
    ptime: Definitions.PacketTime;
    audio_format: Audio.Format;
  },
): number[] {
  const result: number[] = [];
  const samplecount = REVERSE_PTIME_MAP[pars.ptime];
  for (
    let channels = 1;
    channels <= max_num_channels({ audio_format: pars.audio_format, samplecount });
    ++channels
  ) {
    // the AT1130 encoder doesn't permit L24 configurations that meet the following condition:
    if (pars.audio_format === "L24" && tx instanceof VAPI.AT1130.RTPTransmitter.AudioStreamer) {
      const magic_remainder = (8 + channels * samplecount * 3) % 32;
      if (magic_remainder === 1 || magic_remainder === 2) continue;
    }
    result.push(channels);
  }
  return result;
}

export async function get_peaks(a: VAPI.Any.Audio.Essence): Promise<(number | undefined)[]> {
  const channels = await a.channels.read();
  const slice_peaks = new Map<string, number[]>();
  await Promise.all(
    channels.map(async (c) => {
      const ass = c?.signal?.enclosing_subtree;
      if (ass) {
        const name = ass.raw.kwl;
        if (!slice_peaks.has(name)) {
          // first store dummy value so that the entry exists
          slice_peaks.set(name, []);
          // now do the async read
          const peaks = await ass.peak_meter.read();
          // update entry to real value
          slice_peaks.set(name, peaks);
        }
      }
    }),
  );
  return channels.map((c): number | undefined => {
    const ass = c?.signal?.enclosing_subtree;
    return ass ? slice_peaks.get(ass.raw.kwl)?.[c!.signal!.index] : undefined;
  });
}

export function asg_level_to_peak(level: VAPI.Audio.Level): number {
  switch (level) {
    case "set_6dbfs":
      return -60;
    case "set_18dbfs":
      return -180;
    case "set_20dbfs":
      return -200;
  }
}

export function make_expected_peak_levels(pars: {
  level: number;
  channels?: number; // how many channels of 'level' - the rest are silent
  used_channels?: number; // how many channels are in use - the rest are undefined
}): (number | undefined)[] {
  const channels = pars.channels ?? 80;
  const used_channels = pars.used_channels ?? 80;
  if (channels > used_channels)
    throw new Error(`channels ${channels} must not be bigger than used_channels ${used_channels}`);
  return [...Array(80)].map((_, i) => {
    return i < channels ? pars.level : i < used_channels ? -32768 : undefined;
  });
}
