///|
/// A self-checking stable word frame around a supported codec payload.
pub(all) struct BitmapFrame {
  format : CodecFormat
  payload : Array[Int]
  checksum : Int
}

///|
/// Errors for framed transport validation.
pub(all) enum FrameError {
  Codec(CodecError)
  ChecksumMismatch(Int, Int)
}

///|
/// Encode a bitmap and record a deterministic checksum of the resulting words.
pub fn RoaringBitmap::frame(
  self : RoaringBitmap,
  format : CodecFormat,
) -> Result[BitmapFrame, CodecError] {
  match self.encode(format) {
    Ok(payload) => Ok({ format, checksum: frame_checksum(payload), payload })
    Err(error) => Err(error)
  }
}

///|
/// Validate a frame checksum before decoding it.
pub fn BitmapFrame::decode(
  self : BitmapFrame,
) -> Result[RoaringBitmap, FrameError] {
  let actual = frame_checksum(self.payload)
  if actual != self.checksum {
    return Err(ChecksumMismatch(self.checksum, actual))
  }
  match decode(self.format, self.payload) {
    Ok(bitmap) => Ok(bitmap)
    Err(error) => Err(Codec(error))
  }
}

///|
/// Replace a frame payload for corruption-testing or transport adapters.
pub fn BitmapFrame::with_payload(
  self : BitmapFrame,
  payload : Array[Int],
) -> BitmapFrame {
  { format: self.format, checksum: self.checksum, payload }
}

///|
/// A target-independent bounded checksum; it is corruption detection, not cryptography.
fn frame_checksum(words : Array[Int]) -> Int {
  let mut total = 17
  for word in words {
    let part = if word < 0 { -word % 1_000_003 } else { word % 1_000_003 }
    total = (total + part + 31) % 2_000_000_011
  }
  total
}