///|
enum Stage {
  Prefix
  Body(Int)
}

///|
/// Incremental parser retaining at most one bounded section plus a ten-byte prefix.
/// feed returns completed events only; finish is mandatory to detect truncation.
/// After a parse error the decoder is poisoned and cannot be resumed.
pub struct Decoder {
  limits : Limits
  prefix : @buffer.Buffer
  body : @buffer.Buffer
  mut stage : Stage
  mut header_seen : Bool
  mut position : Int
  mut section_start : Int
  mut body_start : Int
  mut count : Int
  mut closed : Bool
  mut failed : Bool
}

///|
pub fn Decoder::new(limits? : Limits = Limits::default()) -> Decoder {
  {
    limits,
    prefix: @buffer.Buffer(),
    body: @buffer.Buffer(),
    stage: Prefix,
    header_seen: false,
    position: 0,
    section_start: 0,
    body_start: 0,
    count: 0,
    closed: false,
    failed: false,
  }
}

///|
fn Decoder::accept(self : Decoder, byte : Byte) -> Event? raise CarError {
  if self.position >= self.limits.max_archive {
    raise LimitExceeded(
      "archive",
      self.limits.max_archive.to_uint64(),
      self.position.to_uint64() + 1UL,
    )
  }
  self.position += 1
  match self.stage {
    Prefix => {
      self.prefix.write_byte(byte)
      if self.prefix.length() > 10 {
        raise InvalidVarint(self.section_start)
      }
      if (byte.to_int() & 128) != 0 {
        if self.prefix.length() == 10 {
          raise InvalidVarint(self.section_start)
        }
        return None
      }
      let (length, _) = match @cid.decode_u64(self.prefix.to_bytes()) {
        Ok(value) => value
        Err(_) => raise InvalidVarint(self.section_start)
      }
      if length == 0UL {
        if self.header_seen {
          raise InvalidCid("zero-length block section")
        } else {
          raise InvalidHeader("zero-length header")
        }
      }
      if self.header_seen && self.count >= self.limits.max_blocks {
        raise LimitExceeded(
          "blocks",
          self.limits.max_blocks.to_uint64(),
          self.count.to_uint64() + 1UL,
        )
      }
      let name = if self.header_seen { "block" } else { "header" }
      let maximum = if self.header_seen {
        self.limits.max_block
      } else {
        self.limits.max_header
      }
      let length = bounded_length(length, maximum, name)
      if length > self.limits.max_archive - self.position {
        raise LimitExceeded(
          "archive",
          self.limits.max_archive.to_uint64(),
          self.position.to_uint64() + length.to_uint64(),
        )
      }
      self.stage = Body(length)
      self.body_start = self.position
      None
    }
    Body(length) => {
      self.body.write_byte(byte)
      if self.body.length() < length {
        return None
      }
      let bytes = self.body.to_bytes()
      let event = if !self.header_seen {
        let header = decode_header(bytes, self.limits)
        self.header_seen = true
        Event::Header(header)
      } else {
        let (block, cid_length) = decode_block(bytes, self.limits)
        self.count += 1
        Event::Block(block, {
          section_offset: self.section_start,
          data_offset: self.body_start + cid_length,
          data_length: length - cid_length,
          section_length: self.position - self.section_start,
        })
      }
      self.prefix.reset()
      self.body.reset()
      self.stage = Prefix
      self.section_start = self.position
      Some(event)
    }
  }
}

///|
pub fn Decoder::feed(
  self : Decoder,
  chunk : BytesView,
) -> Array[Event] raise CarError {
  if self.closed || self.failed {
    raise InvalidState("decoder is closed or failed")
  }
  errdefer {
    self.failed = true
  }
  let events = []
  for byte in chunk {
    if self.accept(byte) is Some(event) {
      events.push(event)
    }
  }
  events
}

///|
pub fn Decoder::finish(self : Decoder) -> Unit raise CarError {
  if self.closed || self.failed {
    raise InvalidState("decoder is closed or failed")
  }
  self.closed = true
  if !self.header_seen || self.prefix.length() != 0 || self.body.length() != 0 {
    raise Truncated(self.position)
  }
}

///|
pub fn Decoder::position(self : Decoder) -> Int {
  self.position
}