///|
/// Returns a stable lowercase dictionary label for reports.
pub fn WindowSourceKind::name(self : WindowSourceKind) -> String {
  match self {
    NoDictionary => "none"
    SourceDictionary => "source"
    TargetDictionary => "target"
  }
}

///|
fn append_window_text(builder : StringBuilder, window : WindowSummary) -> Unit {
  builder <+ "window \{window.index}:\n"
  builder <+ "  offset: \{window.offset}\n"
  builder <+ "  source: \{window.source_kind.name()}\n"
  builder <+
    "  source segment: \{window.source_position}+\{window.source_size}\n"
  builder <+ "  target size: \{window.target_size}\n"
  builder <+ "  delta size: \{window.delta_size}\n"
  builder <+
    "  sections: data=\{window.data_size} instructions=\{window.instruction_size} addresses=\{window.address_size}\n"
  builder <+
    "  operations: ADD=\{window.add_count} RUN=\{window.run_count} COPY=\{window.copy_count}\n"
}

///|
/// Renders a human-readable multi-line structural report.
pub fn DeltaSummary::to_text(self : DeltaSummary) -> String {
  let builder = StringBuilder()
  builder <+ "MoonVCDIFF structural summary\n"
  builder <+ "file size: \{self.file_size}\n"
  builder <+ "header size: \{self.header_size}\n"
  builder <+ "windows: \{self.window_count}\n"
  builder <+ "target size: \{self.target_size}\n"
  builder <+
    "sections: data=\{self.data_size} instructions=\{self.instruction_size} addresses=\{self.address_size}\n"
  builder <+
    "operations: ADD=\{self.add_count} RUN=\{self.run_count} COPY=\{self.copy_count}\n"
  builder <+ "uses source: \{self.uses_source}\n"
  builder <+ "uses target: \{self.uses_target}\n"
  for window in self.windows {
    append_window_text(builder, window)
  }
  builder.to_string()
}

///|
fn append_json_bool(builder : StringBuilder, value : Bool) -> Unit {
  if value {
    builder.write_string("true")
  } else {
    builder.write_string("false")
  }
}

///|
fn append_window_json(builder : StringBuilder, window : WindowSummary) -> Unit {
  builder <+ "{"
  builder <+ "\"index\":\{window.index},"
  builder <+ "\"offset\":\{window.offset},"
  builder <+ "\"source_kind\":\"\{window.source_kind.name()}\","
  builder <+ "\"source_size\":\{window.source_size},"
  builder <+ "\"source_position\":\{window.source_position},"
  builder <+ "\"target_size\":\{window.target_size},"
  builder <+ "\"delta_size\":\{window.delta_size},"
  builder <+ "\"data_size\":\{window.data_size},"
  builder <+ "\"instruction_size\":\{window.instruction_size},"
  builder <+ "\"address_size\":\{window.address_size},"
  builder <+ "\"add_count\":\{window.add_count},"
  builder <+ "\"run_count\":\{window.run_count},"
  builder <+ "\"copy_count\":\{window.copy_count}"
  builder <+ "}"
}

///|
/// Renders compact valid JSON without requiring a JSON dependency.
pub fn DeltaSummary::to_json(self : DeltaSummary) -> String {
  let builder = StringBuilder()
  builder <+ "{"
  builder <+ "\"format\":\"VCDIFF\","
  builder <+ "\"file_size\":\{self.file_size},"
  builder <+ "\"header_size\":\{self.header_size},"
  builder <+ "\"window_count\":\{self.window_count},"
  builder <+ "\"target_size\":\{self.target_size},"
  builder <+ "\"data_size\":\{self.data_size},"
  builder <+ "\"instruction_size\":\{self.instruction_size},"
  builder <+ "\"address_size\":\{self.address_size},"
  builder <+ "\"add_count\":\{self.add_count},"
  builder <+ "\"run_count\":\{self.run_count},"
  builder <+ "\"copy_count\":\{self.copy_count},"
  builder <+ "\"uses_source\":"
  append_json_bool(builder, self.uses_source)
  builder <+ ",\"uses_target\":"
  append_json_bool(builder, self.uses_target)
  builder <+ ",\"windows\":["
  for index, window in self.windows {
    if index > 0 {
      builder.write_string(",")
    }
    append_window_json(builder, window)
  }
  builder <+ "]}"
  builder.to_string()
}

///|
/// Renders one stable verification status line.
pub fn VerifyReport::to_text(self : VerifyReport) -> String {
  if self.matches {
    "verified: \{self.actual_size} bytes match"
  } else {
    "mismatch at byte \{self.first_mismatch}: expected \{self.expected_size} bytes, decoded \{self.actual_size}"
  }
}