///|
pub(all) enum ReportFormat {
  ReportText
  ReportJson
} derive(Eq, @debug.Debug)

///|
pub(all) struct AssetReport {
  parse_ok : Bool
  validation_ok : Bool
  frame_count : Int
  tag_count : Int
  slice_count : Int
  layer_count : Int
  duration_ms : Int
  fps : Int
  manifest_bytes : Int
  message : String
} derive(Eq, @debug.Debug)

///|
pub fn AssetReport::empty() -> AssetReport {
  {
    parse_ok: false,
    validation_ok: false,
    frame_count: 0,
    tag_count: 0,
    slice_count: 0,
    layer_count: 0,
    duration_ms: 0,
    fps: 0,
    manifest_bytes: 0,
    message: "",
  }
}

///|
pub fn analyze_sprite_sheet_json(input : String) -> AssetReport {
  let parsed = parse_sprite_sheet_json(input)
  if !parsed.ok {
    { ..AssetReport::empty(), message: parsed.error.message }
  } else {
    let validation = validate_sprite_sheet(parsed.sheet)
    let manifest = export_runtime_manifest(parsed.sheet)
    {
      parse_ok: true,
      validation_ok: validation.ok,
      frame_count: parsed.sheet.frames.length(),
      tag_count: parsed.sheet.meta.frame_tags.length(),
      slice_count: parsed.sheet.meta.slices.length(),
      layer_count: parsed.sheet.meta.layers.length(),
      duration_ms: parsed.sheet.duration_ms(),
      fps: infer_frame_rate(parsed.sheet),
      manifest_bytes: manifest.length(),
      message: validation.summary(),
    }
  }
}

///|
pub fn asset_report_from_json(
  input : String,
  format? : ReportFormat = ReportText,
) -> String {
  let parsed = parse_sprite_sheet_json(input)
  if !parsed.ok {
    format_parse_error(parsed.error, format)
  } else {
    let validation = validate_sprite_sheet(parsed.sheet)
    let library = build_animation_library(parsed.sheet)
    let boxes = build_frame_box_table(parsed.sheet)
    let manifest = export_runtime_manifest_from_parts(
      parsed.sheet,
      library,
      boxes,
    )
    match format {
      ReportText =>
        render_text_report(parsed.sheet, validation, library, boxes, manifest)
      ReportJson =>
        render_json_report(parsed.sheet, validation, library, boxes, manifest)
    }
  }
}

///|
fn format_parse_error(error : PixelAnimError, format : ReportFormat) -> String {
  match format {
    ReportText =>
      "MoonPixelAnimKit report\nparse: failed\nerror: \{error.message}"
    ReportJson =>
      "{" +
      "\"parse\":\"failed\"," +
      "\"error\":" +
      json_quote(error.message) +
      "," +
      "\"code\":" +
      json_quote(error.code()) +
      "}"
  }
}

///|
fn render_text_report(
  sheet : SpriteSheet,
  validation : ValidationReport,
  library : AnimationLibrary,
  boxes : FrameBoxTable,
  manifest : String,
) -> String {
  let lines : Array[String] = []
  lines.push("MoonPixelAnimKit asset report")
  lines.push("parse: ok")
  lines.push("validation: " + validation.summary())
  lines.push("image: " + sheet.meta.image)
  lines.push("atlas: \{sheet.meta.size.width}x\{sheet.meta.size.height}")
  lines.push("frames: \{sheet.frames.length()}")
  lines.push("tags: \{sheet.meta.frame_tags.length()}")
  lines.push("layers: \{sheet.meta.layers.length()}")
  lines.push("slices: \{sheet.meta.slices.length()}")
  lines.push("duration: \{sheet.duration_ms()} ms")
  lines.push("estimated fps: \{infer_frame_rate(sheet)}")
  lines.push("runtime clips: \{library.clips.length()}")
  lines.push("runtime boxes: \{boxes.boxes.length()}")
  lines.push("manifest bytes: \{manifest.length()}")
  if validation.issues.length() > 0 {
    lines.push("issues:")
    for issue in validation.issues {
      lines.push(
        "- [" +
        issue.severity.to_string() +
        "] " +
        issue.code +
        " " +
        issue.path +
        ": " +
        issue.message,
      )
    }
  }
  join_strings(lines, "\n")
}

///|
fn render_json_report(
  sheet : SpriteSheet,
  validation : ValidationReport,
  library : AnimationLibrary,
  boxes : FrameBoxTable,
  manifest : String,
) -> String {
  "{" +
  "\"parse\":\"ok\"," +
  "\"validation\":" +
  json_quote(if validation.ok { "ok" } else { "failed" }) +
  "," +
  "\"image\":" +
  json_quote(sheet.meta.image) +
  "," +
  "\"atlas\":" +
  size_to_json(sheet.meta.size) +
  "," +
  "\"frames\":\{sheet.frames.length()}," +
  "\"tags\":\{sheet.meta.frame_tags.length()}," +
  "\"layers\":\{sheet.meta.layers.length()}," +
  "\"slices\":\{sheet.meta.slices.length()}," +
  "\"duration\":\{sheet.duration_ms()}," +
  "\"fps\":\{infer_frame_rate(sheet)}," +
  "\"clips\":\{library.clips.length()}," +
  "\"boxes\":\{boxes.boxes.length()}," +
  "\"manifestBytes\":\{manifest.length()}," +
  "\"issues\":\{validation.issues.length()}" +
  "}"
}

///|
pub fn ase_binary_report(
  bytes : Array[Int],
  format? : ReportFormat = ReportText,
) -> String {
  let parsed = parse_aseprite_binary(bytes)
  if !parsed.ok {
    format_parse_error(parsed.error, format)
  } else {
    let validation = validate_ase_file(parsed.file)
    match format {
      ReportText => render_ase_text_report(parsed.file, validation)
      ReportJson => render_ase_json_report(parsed.file, validation)
    }
  }
}

///|
fn render_ase_text_report(
  file : AseFile,
  validation : ValidationReport,
) -> String {
  let lines : Array[String] = []
  lines.push("MoonPixelAnimKit ASE report")
  lines.push("parse: ok")
  lines.push("validation: " + validation.summary())
  lines.push("canvas: \{file.header.width}x\{file.header.height}")
  lines.push("frames: \{file.frames.length()}")
  lines.push("color depth: \{file.header.color_depth}")
  lines.push("chunks: \{file.chunk_count()}")
  lines.push("compressed cels: \{ase_has_compressed_cels(file)}")
  join_strings(lines, "\n")
}

///|
fn render_ase_json_report(
  file : AseFile,
  validation : ValidationReport,
) -> String {
  "{" +
  "\"parse\":\"ok\"," +
  "\"validation\":" +
  json_quote(if validation.ok { "ok" } else { "failed" }) +
  "," +
  "\"width\":\{file.header.width}," +
  "\"height\":\{file.header.height}," +
  "\"frames\":\{file.frames.length()}," +
  "\"colorDepth\":\{file.header.color_depth}," +
  "\"chunks\":\{file.chunk_count()}," +
  "\"compressedCels\":\{ase_has_compressed_cels(file)}" +
  "}"
}