/// Stateful AV1 video decoding, the mount point an AVIF image sequence needs.
///
/// A sequence is a list of items, each one temporal unit. Only the first item
/// has to carry the sequence header, and any item after a key frame predicts
/// from frames stored earlier, so both the parsed sequence and the reference
/// buffer must survive between items. `av1_decode` stays the stateless
/// single-image entry point and shares its reconstruction path with this one.

///|
/// A decoder that keeps presentation state across temporal units.
pub(all) struct Av1VideoDecoder {
  mut sequence : Av1SequenceInfo?
  map : Av1FrameMap
}

///|
/// Create an idle video decoder with an empty reference buffer.
pub fn av1_video_decoder() -> Av1VideoDecoder {
  { sequence: None, map: av1_frame_map(), }
}

///|
/// Decode a temporal unit and return its first displayed image. All frames in
/// the unit update the reference map. Use av1_video_decode_temporal_unit to
/// receive one presentation per temporal unit, including show-existing. OP0's
/// highest spatial layer actually present is selected independently per unit.
fn av1_video_decode_frame_limited(
  decoder : Av1VideoDecoder,
  data : Array[Byte],
  allow_monochrome? : Bool = false,
  limits? : DecodeLimits? = None,
) -> Image? {
  if !decode_input_allowed(data.length(), limits) {
    return None
  }
  match
    av1_video_decode_temporal_unit_planes(
      decoder,
      data,
      allow_monochrome~,
      budget=decode_budget(limits),
    ) {
    Some(frames) =>
      if frames.is_empty() {
        None
      } else {
        av1_frame_to_rgba(frames[0])
      }
    None => None
  }
}

///|
/// Decode temporal units in bitstream order, selecting the highest spatial
/// layer actually present in OP0 for each unit. Input may contain several
/// TD-delimited units; an ISOBMFF sample implicitly ends its single unit.
/// Some([]) denotes a valid unit containing only hidden frames or metadata;
/// None denotes malformed syntax or a missing reference.
/// Failure does not roll back reference updates; discard the decoder after
/// None. Native output is available through av1_video_decode_temporal_unit_native
/// and can be interleaved with this entry point on the same decoder.
fn av1_video_decode_temporal_unit_limited(
  decoder : Av1VideoDecoder,
  data : Array[Byte],
  allow_monochrome? : Bool = false,
  limits? : DecodeLimits? = None,
) -> Array[Image]? {
  if !decode_input_allowed(data.length(), limits) {
    return None
  }
  let frames = match
    av1_video_decode_temporal_unit_planes(
      decoder,
      data,
      allow_monochrome~,
      budget=decode_budget(limits),
    ) {
    Some(value) => value
    None => return None
  }
  let images : Array[Image] = []
  for frame in frames {
    match av1_frame_to_rgba(frame) {
      Some(image) => images.push(image)
      None => return None
    }
  }
  Some(images)
}

///|
/// Native display samples are retained for alpha-track UNORM conversion and
/// container composition; no intermediate eight-bit RGB conversion is needed.
fn av1_video_decode_temporal_unit_planes(
  decoder : Av1VideoDecoder,
  data : Array[Byte],
  allow_monochrome? : Bool = false,
  budget? : DecodeBudget? = None,
) -> Array[Av1FramePlanes]? {
  match
    av1_decode_obu_planes(
      data,
      decoder.map,
      decoder.sequence,
      allow_monochrome,
      budget~,
    ) {
    Some((sequence, displayed)) => {
      decoder.sequence = Some(sequence)
      Some(displayed)
    }
    None => None
  }
}