///|
fn ivf_u16(data : Array[Byte], pos : Int) -> Int {
  data[pos].to_int() | (data[pos + 1].to_int() << 8)
}

///|
fn ivf_u32(data : Array[Byte], pos : Int) -> Int64 {
  let mut value = 0L
  for i in 0..<4 {
    value = value | (data[pos + i].to_int().to_int64() << (i * 8))
  }
  value
}

///|
fn ivf_timestamp(data : Array[Byte], pos : Int) -> Int64 {
  let mut value = 0L
  for i in 0..<8 {
    value = value | (data[pos + i].to_int().to_int64() << (i * 8))
  }
  value
}

///|
/// Read AV01 IVF records. The header time base is scale/rate seconds;
/// timestamps become pts*scale ticks with timescale=rate, checked in Int64.
/// IVF carries neither DTS, duration nor sync flags, so those remain None.
/// A zero declared record count means unspecified; a nonzero count is checked.
pub fn av1_demux_ivf(
  data : Array[Byte],
  limits? : DecodeLimits = DecodeLimits::default(),
) -> Result[Av1DemuxedTrack, DecodeError] {
  match av1_demux_validate_input(data, limits) {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  if data.length() < 32 {
    return Err(
      decode_error(TruncatedInput, "ivf.header", "Incomplete IVF header"),
    )
  }
  if data[0] != b'D' || data[1] != b'K' || data[2] != b'I' || data[3] != b'F' {
    return Err(
      decode_error(InvalidData, "ivf.header", "Missing DKIF signature"),
    )
  }
  if ivf_u16(data, 4) != 0 {
    return Err(
      decode_error(Unsupported, "ivf.header", "Unsupported IVF version"),
    )
  }
  let header_size = ivf_u16(data, 6)
  if header_size < 32 {
    return Err(decode_error(InvalidData, "ivf.header", "Invalid header size"))
  }
  if header_size > data.length() {
    return Err(
      decode_error(TruncatedInput, "ivf.header", "Truncated extended header"),
    )
  }
  if data[8] != b'A' || data[9] != b'V' || data[10] != b'0' || data[11] != b'1' {
    return Err(decode_error(Unsupported, "ivf.codec", "IVF stream is not AV1"))
  }
  let width = ivf_u16(data, 12)
  let height = ivf_u16(data, 14)
  let rate = ivf_u32(data, 16)
  let scale = ivf_u32(data, 20)
  let declared = ivf_u32(data, 24)
  if width == 0 || height == 0 || rate == 0L || scale == 0L {
    return Err(
      decode_error(InvalidData, "ivf.header", "Invalid dimensions or time base"),
    )
  }
  if width > limits.max_frame_pixels / height ||
    declared > limits.max_frames.to_int64() {
    return Err(
      decode_error(LimitExceeded, "ivf.header", "Track exceeds selected limits"),
    )
  }
  let packets : Array[Av1Packet] = []
  let mut pos = header_size
  while pos < data.length() {
    if packets.length() == limits.max_frames {
      return Err(
        decode_error(
          LimitExceeded,
          "ivf.packet",
          "Packet count exceeds max_frames",
        ),
      )
    }
    if data.length() - pos < 12 {
      return Err(
        decode_error(
          TruncatedInput,
          "ivf.packet",
          "Incomplete record header",
          offset=Some(pos),
        ),
      )
    }
    let length = ivf_u32(data, pos)
    let timestamp = ivf_timestamp(data, pos + 4)
    if length == 0L {
      return Err(
        decode_error(
          InvalidData,
          "ivf.packet",
          "Empty AV1 record",
          offset=Some(pos),
        ),
      )
    }
    if length > (data.length() - pos - 12).to_int64() {
      return Err(
        decode_error(
          TruncatedInput,
          "ivf.packet",
          "Record payload is truncated",
          offset=Some(pos),
        ),
      )
    }
    if timestamp > 9223372036854775807L / scale ||
      timestamp < (-9223372036854775807L - 1L) / scale {
      return Err(
        decode_error(
          Unsupported,
          "ivf.timestamp",
          "Scaled timestamp does not fit Int64",
          offset=Some(pos + 4),
        ),
      )
    }
    pos += 12
    let payload = Array::makei(length.to_int(), i => data[pos + i])
    packets.push({
      data: payload,
      presentation_timestamp: Some(timestamp * scale),
      decode_timestamp: None,
      duration: None,
      sync_sample: None,
    })
    pos += length.to_int()
  }
  if declared != 0L && declared != packets.length().to_int64() {
    let kind = if declared > packets.length().to_int64() {
      TruncatedInput
    } else {
      InvalidData
    }
    return Err(
      decode_error(
        kind, "ivf.count", "Declared and actual record counts differ",
      ),
    )
  }
  Ok({
    format: Ivf,
    track_id: 0L,
    width,
    height,
    timescale: rate,
    movie_timescale: None,
    edits: [],
    codec_initialization: [],
    packets,
  })
}