///|
/// IEEE CRC-32 for optional corruption detection on framed transports.
pub fn crc32(data : Bytes) -> UInt {
  let mut crc = 0xffffffffU
  for byte in data {
    crc = crc ^ byte.to_uint()
    for _bit = 0; _bit < 8; _bit = _bit + 1 {
      crc = if (crc & 1U) != 0U { (crc >> 1) ^ 0xedb88320U } else { crc >> 1 }
    }
  }
  crc.lnot()
}

///|
/// Encode payload plus a big-endian CRC-32 inside the length envelope.
pub fn encode_checked_frame(payload : Bytes) -> Bytes {
  let checksum = crc32(payload)
  let body : Array[Byte] = []
  for byte in payload {
    body.push(byte)
  }
  body.push((checksum >> 24).to_byte())
  body.push((checksum >> 16).to_byte())
  body.push((checksum >> 8).to_byte())
  body.push(checksum.to_byte())
  encode_frame(Bytes::from_array(body))
}

///|
/// Decode and verify one checksummed frame.
pub fn decode_checked_frame(
  data : Bytes,
  max_frame_size? : Int = 1048576,
) -> Result[(Bytes, Int), FrameError] {
  match decode_frame(data, max_frame_size=max_frame_size + 4) {
    Err(error) => Err(error)
    Ok((body, consumed)) => {
      if body.length() < 4 {
        return Err(MissingChecksum)
      }
      let end = body.length()
      let payload = body[:end - 4].to_owned()
      let expected = (body[end - 4].to_uint() << 24) |
        (body[end - 3].to_uint() << 16) |
        (body[end - 2].to_uint() << 8) |
        body[end - 1].to_uint()
      let actual = crc32(payload)
      if actual != expected {
        return Err(ChecksumMismatch(expected, actual))
      }
      Ok((payload, consumed))
    }
  }
}

///|
fn verify_checked_body(body : Bytes) -> Result[Bytes, FrameError] {
  if body.length() < 4 {
    return Err(MissingChecksum)
  }
  let end = body.length()
  let payload = body[:end - 4].to_owned()
  let expected = (body[end - 4].to_uint() << 24) |
    (body[end - 3].to_uint() << 16) |
    (body[end - 2].to_uint() << 8) |
    body[end - 1].to_uint()
  let actual = crc32(payload)
  if actual != expected {
    Err(ChecksumMismatch(expected, actual))
  } else {
    Ok(payload)
  }
}

///|
/// Incremental decoder for CRC-32 protected frames.
pub struct CheckedDecoder {
  inner : Decoder
}

///|
pub fn CheckedDecoder::new(
  max_frame_size? : Int = 1048576,
  max_buffer_size? : Int = max_frame_size + 9,
) -> CheckedDecoder {
  CheckedDecoder::{
    inner: Decoder::new(max_frame_size=max_frame_size + 4, max_buffer_size~),
  }
}

///|
pub fn CheckedDecoder::feed(
  self : CheckedDecoder,
  chunk : Bytes,
) -> Result[Array[Bytes], FrameError] {
  match self.inner.feed(chunk) {
    Err(error) => Err(error)
    Ok(bodies) => {
      let frames : Array[Bytes] = []
      for body in bodies {
        match verify_checked_body(body) {
          Ok(payload) => frames.push(payload)
          Err(error) => return Err(error)
        }
      }
      Ok(frames)
    }
  }
}

///|
pub fn CheckedDecoder::buffered(self : CheckedDecoder) -> Int {
  self.inner.buffered()
}

///|
pub fn CheckedDecoder::reset(self : CheckedDecoder) -> Unit {
  self.inner.reset()
}

///|
pub fn CheckedDecoder::finish(
  self : CheckedDecoder,
) -> Result[Unit, FrameError] {
  self.inner.finish()
}