///|
/// Calculate the reflected IEEE CRC-32 checksum.
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 {
      if (crc & 1U) != 0U {
        crc = (crc >> 1) ^ 0xEDB88320U
      } else {
        crc = crc >> 1
      }
    }
  }
  crc ^ 0xFFFFFFFFU
}

///|
/// Calculate CRC-16/CCITT-FALSE with initial value `0xFFFF`.
pub fn crc16_ccitt_false(data : Bytes) -> Int {
  let mut crc = 0xFFFF
  for byte in data {
    crc = crc ^ (byte.to_int() << 8)
    for bit = 0; bit < 8; bit = bit + 1 {
      if (crc & 0x8000) != 0 {
        crc = ((crc << 1) ^ 0x1021) & 0xFFFF
      } else {
        crc = (crc << 1) & 0xFFFF
      }
    }
  }
  crc
}

///|
pub(all) struct SegmentDigest {
  address_value : UInt64
  length_value : Int
  crc32_value : UInt
  crc16_value : Int
} derive(Eq, Debug)

///|
pub struct ImageManifest {
  segment_digest_values : Array[SegmentDigest]
  total_bytes_value : Int
  entry_point_value : UInt64?
  fingerprint_value : UInt
} derive(Eq, Debug)

///|
fn crc32_update_byte(crc : UInt, byte : Byte) -> UInt {
  let mut value = crc ^ byte.to_uint()
  for bit = 0; bit < 8; bit = bit + 1 {
    if (value & 1U) != 0U {
      value = (value >> 1) ^ 0xEDB88320U
    } else {
      value = value >> 1
    }
  }
  value
}

///|
fn image_fingerprint(image : FirmwareImage) -> UInt {
  let mut crc = 0xFFFFFFFFU
  for segment in image.segments() {
    let address = segment.address()
    for index = 0; index < 8; index = index + 1 {
      let shift = (7 - index) * 8
      crc = crc32_update_byte(crc, ((address >> shift) & 0xFFUL).to_byte())
    }
    let length = segment.length().to_uint64()
    for index = 0; index < 8; index = index + 1 {
      let shift = (7 - index) * 8
      crc = crc32_update_byte(crc, ((length >> shift) & 0xFFUL).to_byte())
    }
    for byte in segment.data() {
      crc = crc32_update_byte(crc, byte)
    }
  }
  crc ^ 0xFFFFFFFFU
}

///|
/// Build reproducible per-segment checksums and an address-sensitive fingerprint.
pub fn build_manifest(image : FirmwareImage) -> ImageManifest {
  let digests : Array[SegmentDigest] = []
  for segment in image.segments() {
    let data = segment.data()
    digests.push({
      address_value: segment.address(),
      length_value: segment.length(),
      crc32_value: crc32(data),
      crc16_value: crc16_ccitt_false(data),
    })
  }
  {
    segment_digest_values: digests,
    total_bytes_value: image.total_bytes(),
    entry_point_value: image.entry_point(),
    fingerprint_value: image_fingerprint(image),
  }
}

///|
pub fn SegmentDigest::address(self : SegmentDigest) -> UInt64 {
  self.address_value
}

///|
pub fn SegmentDigest::length(self : SegmentDigest) -> Int {
  self.length_value
}

///|
pub fn SegmentDigest::crc32(self : SegmentDigest) -> UInt {
  self.crc32_value
}

///|
pub fn SegmentDigest::crc16(self : SegmentDigest) -> Int {
  self.crc16_value
}

///|
pub fn ImageManifest::segment_digests(
  self : ImageManifest,
) -> Array[SegmentDigest] {
  self.segment_digest_values.copy()
}

///|
pub fn ImageManifest::total_bytes(self : ImageManifest) -> Int {
  self.total_bytes_value
}

///|
pub fn ImageManifest::entry_point(self : ImageManifest) -> UInt64? {
  self.entry_point_value
}

///|
pub fn ImageManifest::fingerprint(self : ImageManifest) -> UInt {
  self.fingerprint_value
}