///|
/// Resource ceilings applied while decoding untrusted delta files.
///
/// All limits are strict upper bounds. A zero value permits only empty input
/// for the corresponding resource.
pub(all) struct DecodeLimits {
  max_output_size : Int
  max_window_size : Int
  max_windows : Int
  max_instructions_per_window : Int
} derive(Eq, Debug)

///|
/// Returns conservative defaults suitable for command-line and server use.
pub fn default_decode_limits() -> DecodeLimits {
  {
    max_output_size: 256 * 1024 * 1024,
    max_window_size: 16 * 1024 * 1024,
    max_windows: 4096,
    max_instructions_per_window: 4 * 1024 * 1024,
  }
}

///|
/// Creates explicit decode limits after checking that no field is negative.
pub fn DecodeLimits::new(
  max_output_size~ : Int,
  max_window_size~ : Int,
  max_windows~ : Int,
  max_instructions_per_window~ : Int,
) -> DecodeLimits raise VcdiffError {
  if max_output_size < 0 {
    raise InvalidOption(option="max_output_size", reason="must not be negative")
  }
  if max_window_size < 0 {
    raise InvalidOption(option="max_window_size", reason="must not be negative")
  }
  if max_windows < 0 {
    raise InvalidOption(option="max_windows", reason="must not be negative")
  }
  if max_instructions_per_window < 0 {
    raise InvalidOption(
      option="max_instructions_per_window",
      reason="must not be negative",
    )
  }
  { max_output_size, max_window_size, max_windows, max_instructions_per_window }
}

///|
/// Deterministic encoder configuration.
pub(all) struct EncodeOptions {
  window_size : Int
  minimum_match : Int
  max_candidate_chain : Int
  run_threshold : Int
  enable_target_matches : Bool
} derive(Eq, Debug)

///|
/// Returns the stable encoder defaults used by the CLI.
pub fn default_encode_options() -> EncodeOptions {
  {
    window_size: 1024 * 1024,
    minimum_match: 8,
    max_candidate_chain: 64,
    run_threshold: 4,
    enable_target_matches: true,
  }
}

///|
/// Creates an explicit deterministic encoder configuration.
pub fn EncodeOptions::new(
  window_size~ : Int,
  minimum_match~ : Int,
  max_candidate_chain~ : Int,
  run_threshold~ : Int,
  enable_target_matches~ : Bool,
) -> EncodeOptions raise VcdiffError {
  let options = {
    window_size,
    minimum_match,
    max_candidate_chain,
    run_threshold,
    enable_target_matches,
  }
  validate_encode_options(options)
  options
}

///|
fn validate_encode_options(options : EncodeOptions) -> Unit raise VcdiffError {
  if options.window_size <= 0 {
    raise InvalidOption(option="window_size", reason="must be positive")
  }
  if options.minimum_match < 4 {
    raise InvalidOption(
      option="minimum_match",
      reason="must be at least four bytes",
    )
  }
  if options.max_candidate_chain <= 0 {
    raise InvalidOption(option="max_candidate_chain", reason="must be positive")
  }
  if options.run_threshold < 2 {
    raise InvalidOption(
      option="run_threshold",
      reason="must be at least two bytes",
    )
  }
  ignore(options.enable_target_matches)
}

///|
/// The dictionary source selected by one encoded window.
pub(all) enum WindowSourceKind {
  NoDictionary
  SourceDictionary
  TargetDictionary
} derive(Eq, Debug)

///|
/// Structural and instruction statistics for one target window.
pub(all) struct WindowSummary {
  index : Int
  offset : Int
  source_kind : WindowSourceKind
  source_size : Int
  source_position : Int
  target_size : Int
  delta_size : Int
  data_size : Int
  instruction_size : Int
  address_size : Int
  add_count : Int
  run_count : Int
  copy_count : Int
} derive(Eq, Debug)

///|
/// A complete structural summary returned by inspect.
pub(all) struct DeltaSummary {
  file_size : Int
  header_size : Int
  window_count : Int
  target_size : Int
  data_size : Int
  instruction_size : Int
  address_size : Int
  add_count : Int
  run_count : Int
  copy_count : Int
  uses_source : Bool
  uses_target : Bool
  windows : Array[WindowSummary]
} derive(Eq, Debug)

///|
/// Result of decoding a delta and comparing it with expected target bytes.
pub(all) struct VerifyReport {
  matches : Bool
  expected_size : Int
  actual_size : Int
  first_mismatch : Int
} derive(Eq, Debug)