// MBCH v1 block container. Metadata is checked but not BCH protected.
pub(all) struct Recovered {
  data : Array[Int]
  corrected_bits : Int
  corrected_blocks : Int
}

pub fn crc32(bytes : Array[Int]) -> UInt {
  let mut crc = 0xffffffffU
  for b in bytes {
    crc = crc ^ b.to_uint()
    for _ in 0..<8 { crc = if (crc & 1U) != 0U { (crc >> 1) ^ 0xedb88320U } else { crc >> 1 } }
  }
  crc ^ 0xffffffffU
}

fn push_u32(out : Array[Int], value : UInt) -> Unit {
  for i in 0..<4 { out.push(((value >> (8 * i)) & 255U).to_int()) }
}

fn read_u32(bytes : Array[Int], at : Int) -> UInt {
  let mut out = 0U
  for i in 0..<4 { out = out | (bytes[at + i].to_uint() << (8 * i)) }
  out
}

pub fn protect(data : Array[Int], m : Int, t : Int) -> Array[Int] raise BchError {
  if data.length() > 65536 { raise InvalidLength }
  let code = Bch::new(m, t)
  let count = (data.length() * 8 + code.k - 1) / code.k
  if count.to_int64() * code.n.to_int64() > 1048576L { raise InvalidLength }
  let bits = bits_of_bytes(data)
  let encoded : Array[Int] = []
  let mut pos = 0
  while pos < bits.length() {
    let message = Array::make(code.k, 0)
    for j in 0.. Recovered raise BchError {
  if frame.length() < 16 || frame.length() > 131088 { raise InvalidFrame }
  for b in frame { if b < 0 || b > 255 { raise InvalidFrame } }
  if frame[0] != 77 || frame[1] != 66 || frame[2] != 67 || frame[3] != 72 || frame[4] != 1 || frame[7] != 0 { raise InvalidFrame }
  let code = Bch::new(frame[5], frame[6])
  let size = read_u32(frame, 8)
  if size > 65536U { raise InvalidFrame }
  let count = (size.to_int() * 8 + code.k - 1) / code.k
  let total = count.to_int64() * code.n.to_int64()
  if total > 1048576L { raise InvalidFrame }
  if (total + 7L) / 8L + 16L != frame.length().to_int64() { raise InvalidFrame }
  let mut corrected_bits = 0
  let mut corrected_blocks = 0
  let bits : Array[Int] = []
  for block in 0..> (7 - bit % 8)) & 1
    }
    let result = code.decode(word)
    if result.corrected_positions.length() > 0 { corrected_blocks = corrected_blocks + 1 }
    corrected_bits = corrected_bits + result.corrected_positions.length()
    for bit in result.message { bits.push(bit) }
  }
  for i in (size.to_int() * 8)..> (7 - bit % 8)) & 1) != 0 { raise InvalidFrame }
  }
  let data = Array::make(size.to_int(), 0)
  for i in 0..<(size.to_int() * 8) { data[i / 8] = data[i / 8] | (bits[i] << (7 - i % 8)) }
  if crc32(data) != read_u32(frame, 12) { raise ChecksumMismatch }
  { data, corrected_bits, corrected_blocks }
}