///|
/// A single report object for CLI diagnostics and benchmark artifacts.
pub(all) struct GifDocumentReport {
  bytes : Int
  width : Int
  height : Int
  frames : Int
  duration_cs : Int
  encoded_pixels : Int
  unique_colors : Int
  entropy_milli : Int
  estimated_bytes : Int
  validation_errors : Int
  validation_warnings : Int
  complete_index : Bool
} derive(Debug)

///|
pub fn GifDocumentReport::bytes(self : GifDocumentReport) -> Int {
  self.bytes
}

///|
pub fn GifDocumentReport::width(self : GifDocumentReport) -> Int {
  self.width
}

///|
pub fn GifDocumentReport::height(self : GifDocumentReport) -> Int {
  self.height
}

///|
pub fn GifDocumentReport::frames(self : GifDocumentReport) -> Int {
  self.frames
}

///|
pub fn GifDocumentReport::duration_cs(self : GifDocumentReport) -> Int {
  self.duration_cs
}

///|
pub fn GifDocumentReport::encoded_pixels(self : GifDocumentReport) -> Int {
  self.encoded_pixels
}

///|
pub fn GifDocumentReport::unique_colors(self : GifDocumentReport) -> Int {
  self.unique_colors
}

///|
pub fn GifDocumentReport::entropy_milli(self : GifDocumentReport) -> Int {
  self.entropy_milli
}

///|
pub fn GifDocumentReport::estimated_bytes(self : GifDocumentReport) -> Int {
  self.estimated_bytes
}

///|
pub fn GifDocumentReport::validation_errors(self : GifDocumentReport) -> Int {
  self.validation_errors
}

///|
pub fn GifDocumentReport::validation_warnings(self : GifDocumentReport) -> Int {
  self.validation_warnings
}

///|
pub fn GifDocumentReport::complete_index(self : GifDocumentReport) -> Bool {
  self.complete_index
}

///|
/// Analyze an encoded GIF without altering the input buffer.
pub fn report_gif(data : Bytes) -> GifDocumentReport raise GifError {
  let info = inspect(data)
  let validation = validate_gif_bytes(data)
  let index = index_gif(data)
  GifDocumentReport::{
    bytes: data.length(),
    width: info.width(),
    height: info.height(),
    frames: info.frame_count(),
    duration_cs: 0,
    encoded_pixels: info.width() * info.height() * info.frame_count(),
    unique_colors: 0,
    entropy_milli: 0,
    estimated_bytes: data.length(),
    validation_errors: validation.errors(),
    validation_warnings: validation.warnings(),
    complete_index: index.is_complete(),
  }
}

///|
/// Analyze source animation quality and predicted export cost.
pub fn report_animation(
  animation : Animation,
  options : OptimizationOptions,
) -> GifDocumentReport raise GifError {
  let validation = validate_animation(animation)
  let complexity = animation.complexity()
  let optimization = animation.optimization_report(options)
  GifDocumentReport::{
    bytes: 0,
    width: animation.width,
    height: animation.height,
    frames: animation.frame_count(),
    duration_cs: animation.duration_cs(),
    encoded_pixels: optimization.output_pixels(),
    unique_colors: complexity.mean_unique_colors(),
    entropy_milli: complexity.peak_entropy(),
    estimated_bytes: optimization.estimated_bytes(),
    validation_errors: validation.errors(),
    validation_warnings: validation.warnings(),
    complete_index: false,
  }
}

///|
/// Produce stable key/value text suitable for a benchmark log.
pub fn GifDocumentReport::summary(self : GifDocumentReport) -> String {
  "bytes=\{self.bytes} size=\{self.width}x\{self.height} frames=\{self.frames} " +
  "duration_cs=\{self.duration_cs} encoded_pixels=\{self.encoded_pixels} " +
  "estimated_bytes=\{self.estimated_bytes} errors=\{self.validation_errors} " +
  "warnings=\{self.validation_warnings} index=\{self.complete_index}"
}

///|
/// Compare two reports and return byte, pixel, and quality deltas.
pub fn report_delta(
  before : GifDocumentReport,
  after : GifDocumentReport,
) -> (Int, Int, Int) {
  (
    after.estimated_bytes - before.estimated_bytes,
    after.encoded_pixels - before.encoded_pixels,
    after.entropy_milli - before.entropy_milli,
  )
}

///|
/// Report whether an export meets basic acceptance invariants.
pub fn GifDocumentReport::meets_invariants(self : GifDocumentReport) -> Bool {
  self.width > 0 &&
  self.height > 0 &&
  self.frames > 0 &&
  self.validation_errors == 0 &&
  self.complete_index
}