///|
pub struct GCodeAudit {
  program_value : GCodeProgram
  statistics_value : MotionStatistics
  diagnostics_value : Array[Diagnostic]
  canonical_text_value : String
} derive(Eq, Debug)

///|
pub fn GCodeAudit::program(self : GCodeAudit) -> GCodeProgram {
  self.program_value
}

///|
pub fn GCodeAudit::statistics(self : GCodeAudit) -> MotionStatistics {
  self.statistics_value
}

///|
pub fn GCodeAudit::diagnostics(self : GCodeAudit) -> Array[Diagnostic] {
  self.diagnostics_value.copy()
}

///|
pub fn GCodeAudit::canonical_text(self : GCodeAudit) -> String {
  self.canonical_text_value
}

///|
pub fn GCodeAudit::accepted(self : GCodeAudit) -> Bool {
  self.diagnostics_value.is_empty()
}

///|
/// Run parsing, geometry analysis, safety checks, and canonical serialization together.
pub fn audit_gcode(
  text : String,
  policy? : SafetyPolicy = SafetyPolicy::standard(),
) -> Result[GCodeAudit, GCodeError] {
  let program = match parse_gcode(text) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  Ok({
    program_value: program,
    statistics_value: analyze(program),
    diagnostics_value: diagnose(program, policy~),
    canonical_text_value: serialize_gcode(program),
  })
}

///|
pub fn render_audit_text(audit : GCodeAudit) -> String {
  "accepted=" +
  audit.accepted().to_string() +
  "\n" +
  "motions=" +
  audit.statistics().total_distance().to_string() +
  " distance\n" +
  render_diagnostics(audit.diagnostics()) +
  audit.canonical_text()
}

///|
pub fn render_audit_json(audit : GCodeAudit) -> String {
  "{\"accepted\":" +
  audit.accepted().to_string() +
  ",\"motions\":" +
  audit.program().motion_count().to_string() +
  ",\"diagnostics\":" +
  audit.diagnostics().length().to_string() +
  ",\"distance\":" +
  audit.statistics().total_distance().to_string() +
  "}"
}