///|
pub(all) enum MissingBytePolicy {
  RejectMissing
  FillMissing(Byte)
} derive(Eq, Debug)

///|
pub(all) struct RangeCoverage {
  start_value : UInt64
  end_exclusive_value : UInt64
  present_bytes_value : UInt64
  missing_bytes_value : UInt64
} derive(Eq, Debug)

///|
fn checked_range_end(
  start : UInt64,
  length : Int,
) -> Result[UInt64, FirmwareError] {
  if length < 0 {
    return Err(invalid_image_range("range length cannot be negative"))
  }
  let width = length.to_uint64()
  if start > 0xFFFFFFFFFFFFFFFFUL - width {
    Err(invalid_image_range("range end overflows UInt64"))
  } else {
    Ok(start + width)
  }
}

///|
/// Read a bounded contiguous range with an explicit sparse-byte policy.
pub fn FirmwareImage::read_range(
  self : FirmwareImage,
  start : UInt64,
  length : Int,
  missing : MissingBytePolicy,
  max_bytes : Int,
) -> Result[Bytes, FirmwareError] {
  if max_bytes < 0 || length > max_bytes {
    return Err(
      invalid_image_range("range read exceeds the configured byte limit"),
    )
  }
  match checked_range_end(start, length) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let result : Array[Byte] = []
  for offset = 0; offset < length; offset = offset + 1 {
    let address = start + offset.to_uint64()
    match self.byte_at(address) {
      Some(value) => result.push(value)
      None =>
        match missing {
          FillMissing(value) => result.push(value)
          RejectMissing =>
            return Err(
              FirmwareError::new(
                IntegrityViolation,
                "requested range contains an absent sparse byte at address " +
                address.to_string(radix=16),
                SourcePosition::line(0),
              ),
            )
        }
    }
  }
  Ok(Bytes::makei(result.length(), index => result[index]))
}

///|
/// Measure byte coverage within a non-empty half-open address range.
pub fn FirmwareImage::coverage(
  self : FirmwareImage,
  start : UInt64,
  end_exclusive : UInt64,
) -> Result[RangeCoverage, FirmwareError] {
  if start >= end_exclusive {
    return Err(invalid_image_range("coverage range must be non-empty"))
  }
  let mut present = 0UL
  for segment in self.segments() {
    let overlap_start = if segment.address() > start {
      segment.address()
    } else {
      start
    }
    let overlap_end = if segment.end_exclusive() < end_exclusive {
      segment.end_exclusive()
    } else {
      end_exclusive
    }
    if overlap_start < overlap_end {
      present = present + overlap_end - overlap_start
    }
  }
  let width = end_exclusive - start
  Ok({
    start_value: start,
    end_exclusive_value: end_exclusive,
    present_bytes_value: present,
    missing_bytes_value: width - present,
  })
}

///|
/// Write bytes using replacement semantics while preserving the original image.
pub fn FirmwareImage::write(
  self : FirmwareImage,
  address : UInt64,
  data : Bytes,
) -> Result[FirmwareImage, FirmwareError] {
  let chunk = match FirmwareChunk::create(address, data) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let patch = match FirmwareImage::from_chunks([chunk]) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  self.merge(patch, policy=ReplaceExisting)
}

///|
/// Remove bytes in `[start, end)` and re-canonicalize remaining segments.
pub fn FirmwareImage::erase(
  self : FirmwareImage,
  start : UInt64,
  end_exclusive : UInt64,
) -> Result[FirmwareImage, FirmwareError] {
  if start >= end_exclusive {
    return Err(invalid_image_range("erase range must be non-empty"))
  }
  let chunks : Array[FirmwareChunk] = []
  for index, segment in self.segments() {
    let source = segment.data()
    if segment.address() < start {
      let prefix_end = if segment.end_exclusive() < start {
        segment.end_exclusive()
      } else {
        start
      }
      let length = (prefix_end - segment.address()).to_int()
      if length > 0 {
        chunks.push(
          FirmwareChunk::new(
            segment.address(),
            Bytes::makei(length, offset => source[offset]),
            index,
          ),
        )
      }
    }
    if segment.end_exclusive() > end_exclusive {
      let suffix_start = if segment.address() > end_exclusive {
        segment.address()
      } else {
        end_exclusive
      }
      let offset = (suffix_start - segment.address()).to_int()
      let length = (segment.end_exclusive() - suffix_start).to_int()
      if length > 0 {
        chunks.push(
          FirmwareChunk::new(
            suffix_start,
            Bytes::makei(length, item => source[offset + item]),
            index,
          ),
        )
      }
    }
  }
  let entry = match self.entry_point() {
    Some(value) if value >= start && value < end_exclusive => None
    value => value
  }
  FirmwareImage::from_chunks(chunks, entry_point=entry)
}

///|
pub fn RangeCoverage::start(self : RangeCoverage) -> UInt64 {
  self.start_value
}

///|
pub fn RangeCoverage::end_exclusive(self : RangeCoverage) -> UInt64 {
  self.end_exclusive_value
}

///|
pub fn RangeCoverage::present_bytes(self : RangeCoverage) -> UInt64 {
  self.present_bytes_value
}

///|
pub fn RangeCoverage::missing_bytes(self : RangeCoverage) -> UInt64 {
  self.missing_bytes_value
}

///|
pub fn RangeCoverage::complete(self : RangeCoverage) -> Bool {
  self.missing_bytes_value == 0UL
}

///|
pub fn RangeCoverage::ratio(self : RangeCoverage) -> Double {
  let width = self.end_exclusive_value - self.start_value
  self.present_bytes_value.to_double() / width.to_double()
}