///|
/// Byte and target ranges associated with one window.
pub(all) struct WindowIndexEntry {
  index : Int
  file_offset : Int
  encoded_size : Int
  target_offset : Int
  target_size : Int
  source_kind : WindowSourceKind
  source_position : Int
  source_size : Int
  data_offset : Int
  instruction_offset : Int
  address_offset : Int
} derive(Eq, Debug)

///|
/// Random-access metadata for a parsed delta document.
pub(all) struct DeltaIndex {
  file_size : Int
  header_size : Int
  target_size : Int
  source_window_count : Int
  target_window_count : Int
  independent_window_count : Int
  windows : Array[WindowIndexEntry]
} derive(Eq, Debug)

///|
/// Encoding output paired with its parsed structural report.
pub(all) struct EncodeResult {
  delta : Bytes
  summary : DeltaSummary
  trace : DeltaTrace
} derive(Eq, Debug)

///|
/// Decoded target paired with the metadata used to reconstruct it.
pub(all) struct DecodeResult {
  target : Bytes
  summary : DeltaSummary
  trace : DeltaTrace
} derive(Eq, Debug)

///|
fn encoded_window_size(window : ParsedWindow) -> Int raise VcdiffError {
  let source_fields = if window.indicator == 0 {
    0
  } else {
    varint_width(window.source_size) + varint_width(window.source_position)
  }
  1 + source_fields + varint_width(window.delta_length) + window.delta_length
}

///|
/// Builds target-to-file offset mappings without requiring source contents.
pub fn index_delta(delta : Bytes) -> DeltaIndex raise VcdiffError {
  let parsed = parse_delta(delta)
  let entries : Array[WindowIndexEntry] = []
  let mut target_offset = 0
  let mut source_window_count = 0
  let mut target_window_count = 0
  let mut independent_window_count = 0
  for index, window in parsed.windows {
    let source_kind = window_source_kind(window.indicator)
    match source_kind {
      SourceDictionary => source_window_count += 1
      TargetDictionary => target_window_count += 1
      NoDictionary => independent_window_count += 1
    }
    entries.push({
      index,
      file_offset: window.offset,
      encoded_size: encoded_window_size(window),
      target_offset,
      target_size: window.target_size,
      source_kind,
      source_position: window.source_position,
      source_size: window.source_size,
      data_offset: window.data_offset,
      instruction_offset: window.instruction_offset,
      address_offset: window.address_offset,
    })
    target_offset = checked_summary_add(
      target_offset,
      window.target_size,
      window.offset,
    )
  }
  {
    file_size: delta.length(),
    header_size: parsed.header.length,
    target_size: target_offset,
    source_window_count,
    target_window_count,
    independent_window_count,
    windows: entries,
  }
}

///|
/// Finds the window containing a target byte offset.
pub fn DeltaIndex::window_for_target(
  self : DeltaIndex,
  target_offset : Int,
) -> WindowIndexEntry? {
  if target_offset < 0 || target_offset >= self.target_size {
    return None
  }
  let mut low = 0
  let mut high = self.windows.length()
  while low < high {
    let middle = low + (high - low) / 2
    let window = self.windows[middle]
    if target_offset < window.target_offset {
      high = middle
    } else if target_offset >= window.target_offset + window.target_size {
      low = middle + 1
    } else {
      return Some(window)
    }
  }
  None
}

///|
/// Finds the window whose encoded bytes contain a file byte offset.
pub fn DeltaIndex::window_for_file(
  self : DeltaIndex,
  file_offset : Int,
) -> WindowIndexEntry? {
  if file_offset < self.header_size || file_offset >= self.file_size {
    return None
  }
  for window in self.windows {
    if file_offset >= window.file_offset &&
      file_offset < window.file_offset + window.encoded_size {
      return Some(window)
    }
  }
  None
}

///|
fn write_canonical_window(
  output : @buffer.Buffer,
  window : ParsedWindow,
) -> Unit raise VcdiffError {
  output.write_byte(window.indicator.to_byte())
  if window.indicator != 0 {
    write_varint(output, window.source_size)
    write_varint(output, window.source_position)
  }
  let body = @buffer.Buffer()
  write_varint(body, window.target_size)
  body.write_byte(b'\x00')
  write_varint(body, window.data.length())
  write_varint(body, window.instructions.length())
  write_varint(body, window.addresses.length())
  body.write_bytes(window.data)
  body.write_bytes(window.instructions)
  body.write_bytes(window.addresses)
  write_varint(output, body.length())
  output.write_bytes(body.to_bytes())
}

///|
/// Reframes a valid delta with canonical integers and exact section lengths.
///
/// Instruction, data, and address section bytes remain unchanged.
pub fn canonicalize(delta : Bytes) -> Bytes raise VcdiffError {
  let parsed = parse_delta(delta)
  let output = @buffer.Buffer(size_hint=delta.length())
  write_file_header(output)
  for window in parsed.windows {
    write_canonical_window(output, window)
  }
  output.to_bytes()
}

///|
/// Returns true when canonical reframing leaves the delta byte-identical.
pub fn is_canonical(delta : Bytes) -> Bool raise VcdiffError {
  canonicalize(delta) == delta
}

///|
/// Decodes a delta while preserving its target window boundaries.
pub fn decode_windows(
  source : Bytes,
  delta : Bytes,
  limits : DecodeLimits,
) -> Array[Bytes] raise VcdiffError {
  let parsed = parse_delta(delta)
  if parsed.windows.length() > limits.max_windows {
    raise ResourceLimit(
      offset=parsed.header.length,
      resource="windows",
      limit=limits.max_windows,
    )
  }
  let table = default_code_table()
  let output : Array[Byte] = []
  let windows : Array[Bytes] = []
  for window in parsed.windows {
    let dictionary = select_dictionary(source, output, window)
    let decoded = decode_window(window, dictionary, limits, table)
    if output.length() > limits.max_output_size - decoded.length() {
      raise ResourceLimit(
        offset=window.offset,
        resource="output_size",
        limit=limits.max_output_size,
      )
    }
    windows.push(decoded)
    append_bytes(output, decoded)
  }
  windows
}

///|
/// Decodes and returns a validated half-open target byte range.
pub fn decode_range(
  source : Bytes,
  delta : Bytes,
  start : Int,
  end : Int,
  limits : DecodeLimits,
) -> Bytes raise VcdiffError {
  if start < 0 || end < start {
    raise InvalidOption(
      option="target_range",
      reason="must be a non-negative half-open range",
    )
  }
  let target = decode(source, delta, limits)
  if end > target.length() {
    raise InvalidOption(
      option="target_range",
      reason="range end exceeds decoded target size",
    )
  }
  target[start:end].to_owned()
}

///|
/// Encodes once and returns the delta, summary, and expanded trace.
pub fn encode_detailed(
  source : Bytes,
  target : Bytes,
  options : EncodeOptions,
) -> EncodeResult raise VcdiffError {
  let delta = encode(source, target, options)
  { delta, summary: inspect(delta), trace: trace(delta) }
}

///|
/// Decodes once and returns the target, summary, and expanded trace.
pub fn decode_detailed(
  source : Bytes,
  delta : Bytes,
  limits : DecodeLimits,
) -> DecodeResult raise VcdiffError {
  let summary = inspect(delta)
  let expanded = trace(delta)
  let target = decode(source, delta, limits)
  { target, summary, trace: expanded }
}