///|
pub fn rect_to_json(rect : Rect) -> String {
  "{\"x\":\{rect.x},\"y\":\{rect.y},\"w\":\{rect.width},\"h\":\{rect.height}}"
}

///|
pub fn size_to_json(size : Size) -> String {
  "{\"w\":\{size.width},\"h\":\{size.height}}"
}

///|
pub fn frame_ref_to_json(frame : AnimationFrameRef) -> String {
  "{" +
  "\"name\":" +
  json_quote(frame.name) +
  "," +
  "\"index\":\{frame.frame_index}," +
  "\"duration\":\{frame.duration_ms}," +
  "\"rect\":" +
  rect_to_json(frame.rect) +
  "}"
}

///|
pub fn clip_to_json(clip : AnimationClip) -> String {
  let frames : Array[String] = []
  for frame in clip.frames {
    frames.push(frame_ref_to_json(frame))
  }
  "{" +
  "\"name\":" +
  json_quote(clip.name) +
  "," +
  "\"direction\":" +
  json_quote(clip.direction.to_string()) +
  "," +
  "\"loop\":" +
  json_quote(clip.loop_mode.to_string()) +
  "," +
  "\"duration\":\{clip.total_duration_ms}," +
  "\"frames\":[" +
  join_strings(frames, ",") +
  "]" +
  "}"
}

///|
pub fn frame_box_to_json(box : FrameBox) -> String {
  "{" +
  "\"frame\":\{box.frame_index}," +
  "\"frameName\":" +
  json_quote(box.frame_name) +
  "," +
  "\"name\":" +
  json_quote(box.name) +
  "," +
  "\"kind\":" +
  json_quote(box.kind.to_string()) +
  "," +
  "\"bounds\":" +
  rect_to_json(box.bounds) +
  "," +
  "\"pivot\":{\"x\":\{box.pivot.x},\"y\":\{box.pivot.y}}," +
  "\"hasPivot\":\{box.has_pivot}" +
  "}"
}

///|
pub fn export_runtime_manifest(sheet : SpriteSheet) -> String {
  let library = build_animation_library(sheet)
  let boxes = build_frame_box_table(sheet)
  export_runtime_manifest_from_parts(sheet, library, boxes)
}

///|
pub fn export_runtime_manifest_from_parts(
  sheet : SpriteSheet,
  library : AnimationLibrary,
  boxes : FrameBoxTable,
) -> String {
  let clips : Array[String] = []
  for clip in library.clips {
    clips.push(clip_to_json(clip))
  }
  let box_items : Array[String] = []
  for box in boxes.boxes {
    box_items.push(frame_box_to_json(box))
  }
  "{" +
  "\"schema\":\"moonpixelanimkit.runtime.v1\"," +
  "\"image\":" +
  json_quote(sheet.meta.image) +
  "," +
  "\"atlasSize\":" +
  size_to_json(sheet.meta.size) +
  "," +
  "\"frameCount\":\{sheet.frames.length()}," +
  "\"duration\":\{sheet.duration_ms()}," +
  "\"clips\":[" +
  join_strings(clips, ",") +
  "]," +
  "\"boxes\":[" +
  join_strings(box_items, ",") +
  "]" +
  "}"
}

///|
pub fn export_phaser_animation_plan(sheet : SpriteSheet) -> String {
  let library = build_animation_library(sheet)
  let lines : Array[String] = []
  lines.push("// Generated by MoonPixelAnimKit")
  lines.push("const textureKey = " + json_quote(sheet.meta.image) + ";")
  for clip in library.clips {
    lines.push("scene.anims.create({")
    lines.push("  key: " + json_quote(clip.name) + ",")
    lines.push("  frames: [")
    for frame in clip.frames {
      lines.push(
        "    { key: textureKey, frame: " + json_quote(frame.name) + " },",
      )
    }
    lines.push("  ],")
    lines.push("  frameRate: \{estimate_clip_fps(clip)},")
    let repeat = if clip.loop_mode == LoopOnce { "0" } else { "-1" }
    lines.push("  repeat: " + repeat)
    lines.push("});")
  }
  join_strings(lines, "\n")
}

///|
pub(all) struct CanvasDrawCommand {
  frame_name : String
  source : Rect
  destination : Rect
  duration_ms : Int
} derive(Eq, @debug.Debug)

///|
pub fn CanvasDrawCommand::new(
  frame_name : String,
  source : Rect,
  destination : Rect,
  duration_ms : Int,
) -> CanvasDrawCommand {
  { frame_name, source, destination, duration_ms }
}

///|
pub fn build_canvas_draw_plan(
  sheet : SpriteSheet,
  scale? : Int = 1,
) -> Array[CanvasDrawCommand] {
  let commands : Array[CanvasDrawCommand] = []
  let s = if scale <= 0 { 1 } else { scale }
  for frame in sheet.frames {
    commands.push(
      CanvasDrawCommand::new(
        frame.name,
        frame.rect,
        Rect::new(0, 0, frame.rect.width * s, frame.rect.height * s),
        frame.duration_ms,
      ),
    )
  }
  commands
}

///|
pub fn canvas_command_to_text(command : CanvasDrawCommand) -> String {
  "draw " +
  command.frame_name +
  " src=" +
  rect_to_json(command.source) +
  " dst=" +
  rect_to_json(command.destination) +
  " duration=" +
  command.duration_ms.to_string()
}

///|
pub fn export_canvas_draw_plan_text(
  sheet : SpriteSheet,
  scale? : Int = 1,
) -> String {
  let lines : Array[String] = []
  for command in build_canvas_draw_plan(sheet, scale~) {
    lines.push(canvas_command_to_text(command))
  }
  join_strings(lines, "\n")
}