///|
/// Raw streams have no metadata or MD5. Skipped reports every discarded byte,
/// including initial garbage and damaged frames; it is never silent recovery.
pub(all) enum RawDecodeEvent {
  RawFrame(Frame)
  Skipped(Int64, Int32)
  RawNeedInput
  RawDone
}

///|
/// Decoder for independently framed FLAC (RFC 9639 section 9). Format changes
/// are allowed. Optional info supplies the values represented by header code 0;
/// they are not inferred from earlier frames. Returned frame sample_number is
/// the local count of successfully decoded samples; number retains the original
/// encoded number. The receiver must use each frame's rate/depth/channels.
/// Incomplete frames are retried at geometrically increasing buffer sizes.
/// This bounds repeated parsing for tiny input chunks, with up to the current
/// partial-frame length of lookahead latency. finish always forces a retry.
struct RawDecoder {
  mut pending : Bytes
  incoming : Array[Byte]
  mut retry_at : Int32
  mut offset : Int64
  info : StreamInfo?
  limit : Int32
  mut peak : Int32
  mut ended : Bool
  mut failed : FlacError?
  mut queued : Frame?
  mut samples : Int64
}

///|
pub fn RawDecoder::new(
  info? : StreamInfo,
  max_buffer_bytes~ : Int32 = 33554432,
) -> RawDecoder {
  {
    pending: b"",
    incoming: [],
    retry_at: 0,
    offset: 0L,
    info,
    limit: max_buffer_bytes,
    peak: 0,
    ended: false,
    failed: None,
    queued: None,
    samples: 0L,
  }
}

///|
/// Copy an input prefix, returning accepted bytes. Drain next before retrying.
pub fn RawDecoder::push(
  self : RawDecoder,
  input : Bytes,
) -> Int32 raise FlacError {
  if self.failed is Some(error) {
    raise error
  }
  if self.ended {
    raise Invalid(self.offset, "raw input supplied after finish")
  }
  if self.limit < 4 {
    raise Limit("raw compressed buffer limit must be at least four")
  }
  let room = self.limit - self.buffered_bytes()
  if room <= 0 {
    raise Limit("raw compressed buffer full; drain or raise limit")
  }
  let count = if input.length() < room { input.length() } else { room }
  for i in 0.. self.peak {
    self.peak = self.buffered_bytes()
  }
  count
}

///|
pub fn RawDecoder::buffered_bytes(self : RawDecoder) -> Int32 {
  self.pending.length() + self.incoming.length()
}

///|
pub fn RawDecoder::peak_buffered_bytes(self : RawDecoder) -> Int32 {
  self.peak
}

///|
/// Declare EOF. Drain next to RawDone; incomplete candidates raise an error.
pub fn RawDecoder::finish(self : RawDecoder) -> Unit raise FlacError {
  if self.failed is Some(error) {
    raise error
  }
  self.ended = true
}

///|
fn raw_header_info(
  data : Bytes,
  start : Int32,
  offset : Int64,
  fallback : StreamInfo?,
) -> StreamInfo raise FlacError {
  let r : FrameBits = {
    data,
    pos: start,
    bit: 0,
    base: offset - start.to_int64(),
  }
  if r.read(15) != 0x7ffcL {
    r.bad("invalid raw sync")
  }
  let variable = r.read(1) != 0L
  let block = r.read(4).to_int()
  let rate = r.read(4).to_int()
  let assignment = r.read(4).to_int()
  let depth = r.read(3).to_int()
  if r.read(1) != 0L ||
    block == 0 ||
    rate == 15 ||
    assignment > 10 ||
    depth == 3 {
    r.bad("reserved raw header value")
  }
  ignore(frame_number(r, variable))
  if block == 6 {
    ignore(r.read(8))
  }
  if block == 7 {
    ignore(r.read(16))
  }
  if rate == 12 {
    ignore(r.read(8))
  }
  if rate == 13 || rate == 14 {
    ignore(r.read(16))
  }
  let crc = frame_crc(data, start, r.pos, 8)
  if r.read(8).to_int() != crc {
    r.bad("raw header CRC8 mismatch")
  }
  if fallback is None && (rate == 0 || depth == 0) {
    raise Limit(
      "raw frame header requires explicit StreamInfo sample rate/bit depth",
    )
  }
  let info = fallback.unwrap_or({
    min_block_size: 16,
    max_block_size: 65535,
    min_frame_size: 0,
    max_frame_size: 0,
    sample_rate: 1,
    channels: 1,
    bits_per_sample: 4,
    total_samples: 0L,
    md5: b"",
  })
  if (rate == 0 && (info.sample_rate < 0 || info.sample_rate > 1048575)) ||
    (depth == 0 && (info.bits_per_sample < 4 || info.bits_per_sample > 32)) {
    raise Limit("invalid explicit raw StreamInfo sample rate/bit depth")
  }
  info
}

///|
fn RawDecoder::discard(self : RawDecoder, count : Int32) -> RawDecodeEvent {
  let offset = self.offset
  self.pending = self.pending[count:].to_owned()
  self.offset += count.to_int64()
  self.retry_at = 0
  Skipped(offset, count)
}

///|
/// Header CRC8 and complete frame CRC16 must pass before any PCM is emitted.
/// Every resynchronization is reported. A truncated candidate at EOF is an
/// error; a fully rejected candidate is reported as discarded bytes.
pub fn RawDecoder::next(self : RawDecoder) -> RawDecodeEvent raise FlacError {
  if self.failed is Some(error) {
    raise error
  }
  self.read_raw_event() catch {
    error => {
      self.failed = Some(error)
      raise error
    }
  }
}

///|
fn RawDecoder::read_raw_event(
  self : RawDecoder,
) -> RawDecodeEvent raise FlacError {
  if self.queued is Some(frame) {
    self.queued = None
    return RawFrame(frame)
  }
  if !self.ended && self.buffered_bytes() < self.retry_at {
    return RawNeedInput
  }
  if !self.incoming.is_empty() {
    self.pending = self.pending + Bytes::from_array(self.incoming)
    self.incoming.clear()
  }
  if self.pending.is_empty() {
    return if self.ended { RawDone } else { RawNeedInput }
  }
  let mut first_incomplete = -1
  for start = 0; start < self.pending.length(); start = start + 1 {
    if self.pending[start] != 0xff {
      continue
    }
    if start + 1 == self.pending.length() {
      if first_incomplete < 0 {
        first_incomplete = start
      }
      break
    }
    if (self.pending[start + 1].to_int() & 0xfe) != 0xf8 {
      continue
    }
    let info = raw_header_info(
      self.pending,
      start,
      self.offset + start.to_int64(),
      self.info,
    ) catch {
      NeedMore => {
        first_incomplete = start
        break
      }
      Invalid(_, _) => continue
      error => raise error
    }
    let parsed = Some(
      parse_frame(self.pending, start, info, self.offset + start.to_int64()),
    ) catch {
      NeedMore => {
        // A later sync can be ordinary PCM inside this still-valid candidate.
        // Resume scanning only after this candidate is conclusively invalid.
        first_incomplete = start
        break
      }
      Invalid(_, _) => None
      error => raise error
    }
    if parsed is Some((decoded, end)) {
      let frame = { ..decoded, sample_number: self.samples, }
      self.samples += frame.block_size.to_int64()
      let previous_offset = self.offset
      self.pending = self.pending[end:].to_owned()
      self.offset += end.to_int64()
      self.retry_at = 0
      if start > 0 {
        self.queued = Some(frame)
        return Skipped(previous_offset, start)
      }
      return RawFrame(frame)
    }
  }
  if first_incomplete >= 0 {
    if first_incomplete > 0 {
      let event = self.discard(first_incomplete)
      self.defer_raw_retry()
      return event
    }
    if self.ended {
      raise Invalid(
        self.offset + self.pending.length().to_int64(),
        "truncated raw FLAC frame",
      )
    }
    if self.pending.length() >= self.limit {
      raise Limit("raw frame exceeds compressed buffer limit")
    }
    self.defer_raw_retry()
    RawNeedInput
  } else {
    self.discard(self.pending.length())
  }
}

///|
fn RawDecoder::defer_raw_retry(self : RawDecoder) -> Unit {
  let size = self.pending.length()
  self.retry_at = if size > self.limit / 2 {
    self.limit
  } else {
    let doubled = size * 2
    if doubled < 32 {
      if self.limit < 32 {
        self.limit
      } else {
        32
      }
    } else {
      doubled
    }
  }
}