///|
/// ISO 3309 / zlib CRC-32 polynomial, reflected.
fn crc32_poly() -> UInt {
  0xEDB88320U
}

///|
/// Build the 256-entry CRC-32 table used by python-evtx `binascii.crc32`.
fn crc32_table() -> Array[UInt] {
  let table : Array[UInt] = []
  let poly = crc32_poly()
  for i = 0; i < 256; i = i + 1 {
    let mut crc = i.reinterpret_as_uint()
    for bit = 0; bit < 8; bit = bit + 1 {
      if (crc & 1U) == 1U {
        crc = poly ^ (crc >> 1)
      } else {
        crc = crc >> 1
      }
    }
    table.push(crc)
  }
  table
}

///|
/// CRC-32 of `data[start, end)` with the ISO 3309 final XOR.
pub fn crc32_slice(
  data : Bytes,
  start : Int,
  end : Int,
) -> Result[UInt, EvtxError] {
  if start < 0 || end < start || end > data.length() {
    return Err(Truncated("crc32 range", start, end - start))
  }
  let table = crc32_table()
  let mut crc = 0xFFFFFFFFU
  for i = start; i < end; i = i + 1 {
    let mixed = crc ^ data[i].to_int().reinterpret_as_uint()
    let idx = (mixed & 0xFFU).reinterpret_as_int()
    crc = table[idx] ^ (crc >> 8)
  }
  Ok(crc ^ 0xFFFFFFFFU)
}

///|
/// CRC-32 of a whole byte string.
pub fn crc32_bytes(data : Bytes) -> UInt {
  crc32_slice(data, 0, data.length()).unwrap()
}

///|
/// Concatenate two ranges before hashing, matching chunk header checksums.
pub fn crc32_two_slices(
  data : Bytes,
  a0 : Int,
  a1 : Int,
  b0 : Int,
  b1 : Int,
) -> Result[UInt, EvtxError] {
  let left = match bytes_slice_result(data, a0, a1) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let right = match bytes_slice_result(data, b0, b1) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  Ok(crc32_bytes(concat_bytes(left, right)))
}