///|
priv struct JsonIssue {
  code : String
  severity : String
  message : String
  line : Json
} derive(ToJson)

///|
priv struct JsonCoordinateFormat {
  integer_digits : Int
  decimal_digits : Int
} derive(ToJson)

///|
priv struct JsonStatistics {
  apertures : Int
  aperture_macros : Int
  moves : Int
  draws : Int
  flashes : Int
  regions : Int
  arc_draws : Int
  comments : Int
  attributes : Int
  unknown_commands : Int
} derive(ToJson)

///|
priv struct JsonReport {
  tool : String
  version : String
  command : String
  file : String
  status : String
  unit : Json
  coordinate_format : Json
  file_function : Json
  file_polarity : Json
  generation_software : Json
  statistics : JsonStatistics
  errors : Int
  warnings : Int
  issues : Array[JsonIssue]
} derive(ToJson)

///|
fn string_or_null(s : String?) -> Json {
  match s {
    Some(v) => Json::string(v)
    None => Json::null()
  }
}

///|
fn unit_or_null(unit : GerberUnit?) -> Json {
  match unit {
    Some(u) => Json::string(unit_json(u))
    None => Json::null()
  }
}

///|
fn format_or_null(fmt : CoordinateFormat?) -> Json {
  match fmt {
    Some(f) => {
      let dto : JsonCoordinateFormat = {
        integer_digits: f.integer_digits,
        decimal_digits: f.decimal_digits,
      }
      ToJson::to_json(dto)
    }
    None => Json::null()
  }
}

///|
fn line_or_null(line : Int?) -> Json {
  match line {
    Some(n) => Json::number(n.to_double())
    None => Json::null()
  }
}

///|
pub fn render_json(
  report : GerberReport,
  file_name : String,
  command : CliCommand,
) -> String {
  let issues : Array[JsonIssue] = []
  for issue in report.issues {
    issues.push({
      code: issue.code,
      severity: severity_json(issue.severity),
      message: issue.message,
      line: line_or_null(issue.line),
    })
  }
  let stats = report.statistics
  let dto : JsonReport = {
    tool: "gerberguard",
    version: VERSION,
    command: cli_command_json(command),
    file: file_name,
    status: status_json(report.status),
    unit: unit_or_null(report.unit),
    coordinate_format: format_or_null(report.coordinate_format),
    file_function: string_or_null(report.file_function),
    file_polarity: string_or_null(report.file_polarity),
    generation_software: string_or_null(report.generation_software),
    statistics: {
      apertures: stats.apertures,
      aperture_macros: stats.aperture_macros,
      moves: stats.moves,
      draws: stats.draws,
      flashes: stats.flashes,
      regions: stats.regions,
      arc_draws: stats.arc_draws,
      comments: stats.comments,
      attributes: stats.attributes,
      unknown_commands: stats.unknown_commands,
    },
    errors: count_errors(report.issues),
    warnings: count_warnings(report.issues),
    issues,
  }
  ToJson::to_json(dto).stringify()
}