///|
pub struct FrameReader {
  mut pending : Array[Int32]
  mut byte_count : Int32
  mut frame_count : Int32
  byte_limit : Int32
  frame_limit : Int32
}

///|
pub fn FrameReader::new(
  max_bytes~ : Int32 = 1048576,
  max_frames~ : Int32 = 4096,
) -> FrameReader raise CodeError {
  if max_bytes < 30 || max_bytes > 16777216 {
    raise Limit("stream byte budget must be 30..16777216")
  }
  if max_frames < 1 || max_frames > 65536 {
    raise Limit("stream frame budget must be 1..65536")
  }
  {
    pending: [],
    byte_count: 0,
    frame_count: 0,
    byte_limit: max_bytes,
    frame_limit: max_frames,
  }
}

///|
pub fn FrameReader::buffered(self : FrameReader) -> Int32 {
  self.pending.length()
}

///|
pub fn FrameReader::received_bytes(self : FrameReader) -> Int32 {
  self.byte_count
}

///|
pub fn FrameReader::frames(self : FrameReader) -> Int32 {
  self.frame_count
}

///|
fn header_length(
  values : Array[Int32],
  offset : Int32,
) -> Int32 raise CodeError {
  if values[offset] != 77 ||
    values[offset + 1] != 82 ||
    values[offset + 2] != 67 ||
    values[offset + 3] != 49 {
    raise Corrupt("stream packet magic/version mismatch")
  }
  if values[offset + 4] != 0 {
    raise Corrupt("reserved stream packet flags")
  }
  let count = values[offset + 5]
  let width = (values[offset + 6] << 8) | values[offset + 7]
  if count < 1 || count > 64 || width < 1 || width > 4096 {
    raise Limit("stream packet dimensions")
  }
  let length = read_u31(values, offset + 20)
  if length != 28 + count + width {
    raise Shape("stream declared length mismatch")
  }
  length
}

///|
// A failed feed changes neither pending bytes nor counters. There is no resync.
// Each feed validates ALL complete frames before publishing any of them.
pub fn FrameReader::feed(
  self : FrameReader,
  chunk : Symbols,
) -> Array[Packet] raise CodeError {
  if chunk.length() > self.byte_limit - self.byte_count {
    raise Limit("stream byte budget exhausted")
  }
  let values = copy_values(self.pending)
  for byte in chunk.values {
    values.push(byte)
  }
  let out : Array[Packet] = []
  let mut offset = 0
  while values.length() - offset >= 24 {
    let length = header_length(values, offset)
    if values.length() - offset < length {
      break
    }
    if out.length() >= self.frame_limit - self.frame_count {
      raise Limit("stream frame budget exhausted")
    }
    let frame : Array[Int32] = []
    for i in offset..<(offset + length) {
      frame.push(values[i])
    }
    out.push(packet_from_wire({ values: frame, }))
    offset = offset + length
  }
  let pending : Array[Int32] = []
  for i in offset.. 4188 {
    raise Limit("pending frame exceeds maximum frame length")
  }
  if pending.length() >= 4 &&
    (
      pending[0] != 77 ||
      pending[1] != 82 ||
      pending[2] != 67 ||
      pending[3] != 49
    ) {
    raise Corrupt("partial frame magic/version mismatch")
  }
  self.pending = pending
  self.byte_count = self.byte_count + chunk.length()
  self.frame_count = self.frame_count + out.length()
  out
}

///|
pub fn FrameReader::finish(self : FrameReader) -> Unit raise CodeError {
  if self.pending.length() != 0 {
    raise Truncated
  }
}