///|
pub struct QualityReport {
  frame_count : Int
  animation_count : Int
  slice_count : Int
  collision_box_count : Int
  total_duration : Int
  atlas_width : Int
  atlas_height : Int
  occupancy : Double
  errors : Int
  warnings : Int
  passed : Bool
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn quality_report(
  sheet : SpriteSheet,
  atlas? : AtlasOptions = AtlasOptions::default(),
) -> QualityReport {
  let bundle = build_export_bundle(sheet, atlas~)
  let summary = summarize_diagnostics(bundle.diagnostics)
  let mut collisions = 0
  for frame in bundle.sheet.frames {
    collisions += frame.boxes.length()
  }
  {
    frame_count: bundle.sheet.frames.length(),
    animation_count: bundle.machine.clips.length(),
    slice_count: bundle.sheet.slices.length(),
    collision_box_count: collisions,
    total_duration: total_duration(bundle.sheet),
    atlas_width: bundle.atlas.size.w,
    atlas_height: bundle.atlas.size.h,
    occupancy: bundle.atlas.occupancy,
    errors: summary.errors,
    warnings: summary.warnings,
    passed: summary.errors == 0,
  }
}

///|
pub fn QualityReport::to_json_string(
  self : QualityReport,
  indent? : Int = 2,
) -> String {
  self.to_json().stringify(indent~)
}

///|
pub fn QualityReport::has_warnings(self : QualityReport) -> Bool {
  self.warnings > 0
}

///|
pub fn QualityReport::frame_density(self : QualityReport) -> Double {
  if self.atlas_width <= 0 || self.atlas_height <= 0 {
    0.0
  } else {
    self.frame_count.to_double() /
    (self.atlas_width * self.atlas_height).to_double()
  }
}

///|
pub fn QualityReport::duration_per_frame(self : QualityReport) -> Double {
  if self.frame_count == 0 {
    0.0
  } else {
    self.total_duration.to_double() / self.frame_count.to_double()
  }
}

///|
pub fn QualityReport::summary_line(self : QualityReport) -> String {
  let status = if self.passed { "pass" } else { "fail" }
  "\{status}: \{self.frame_count} frames, \{self.animation_count} animations, \{self.errors} errors, \{self.warnings} warnings"
}

///|
pub fn quality_gate(
  sheet : SpriteSheet,
  atlas? : AtlasOptions = AtlasOptions::default(),
) -> Bool {
  quality_report(sheet, atlas~).passed
}

///|
pub fn compare_quality(left : QualityReport, right : QualityReport) -> Int {
  if left.errors != right.errors {
    left.errors.compare(right.errors)
  } else if left.warnings != right.warnings {
    left.warnings.compare(right.warnings)
  } else if left.occupancy != right.occupancy {
    if left.occupancy < right.occupancy {
      -1
    } else {
      1
    }
  } else {
    0
  }
}