///|
pub struct RuntimeAdapter {
  target : RuntimeTarget
  extension_name : String
  format_name : String
  supports_nine_patch : Bool
  supports_collisions : Bool
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn RuntimeAdapter::for_target(target : RuntimeTarget) -> RuntimeAdapter {
  match target {
    Godot =>
      {
        target,
        extension_name: "tres",
        format_name: "godot-spritesheet",
        supports_nine_patch: true,
        supports_collisions: true,
      }
    Phaser =>
      {
        target,
        extension_name: "json",
        format_name: "phaser-atlas-json-hash",
        supports_nine_patch: false,
        supports_collisions: true,
      }
    MoonBit =>
      {
        target,
        extension_name: "mbt.json",
        format_name: "moonbit-aseflow",
        supports_nine_patch: true,
        supports_collisions: true,
      }
    Generic =>
      {
        target,
        extension_name: "json",
        format_name: "moonbit-aseflow-generic",
        supports_nine_patch: true,
        supports_collisions: true,
      }
  }
}

///|
pub fn RuntimeAdapter::extension(self : RuntimeAdapter) -> String {
  self.extension_name
}

///|
pub fn RuntimeAdapter::format(self : RuntimeAdapter) -> String {
  self.format_name
}

///|
pub fn RuntimeAdapter::emit(
  self : RuntimeAdapter,
  bundle : ExportBundle,
) -> String {
  export_runtime_bundle(bundle, target=self.target, indent=2)
}

///|
pub fn RuntimeAdapter::emit_compact(
  self : RuntimeAdapter,
  bundle : ExportBundle,
) -> String {
  export_runtime_bundle(bundle, target=self.target, indent=0)
}

///|
pub fn RuntimeAdapter::can_export(
  self : RuntimeAdapter,
  bundle : ExportBundle,
) -> Bool {
  (self.supports_nine_patch || bundle.nine_patches.length() == 0) &&
  (self.supports_collisions || count_collision_boxes(bundle.sheet) == 0)
}

///|
pub fn RuntimeAdapter::validate_bundle(
  self : RuntimeAdapter,
  bundle : ExportBundle,
) -> Array[Diagnostic] {
  let result : Array[Diagnostic] = []
  if !self.can_export(bundle) {
    result.push({
      severity: Error,
      code: "runtime_capability",
      message: "runtime target cannot represent all bundle features",
    })
  }
  result
}

///|
pub fn RuntimeAdapter::manifest_name(
  self : RuntimeAdapter,
  base : String,
) -> String {
  base + "." + self.extension_name
}

///|
pub fn RuntimeAdapter::target_name(self : RuntimeAdapter) -> String {
  match self.target {
    Generic => "generic"
    Godot => "godot"
    Phaser => "phaser"
    MoonBit => "moonbit"
  }
}

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

///|
fn count_collision_boxes(sheet : SpriteSheet) -> Int {
  sheet.frames.fold(init=0, (total, frame) => total + frame.boxes.length())
}

///|
pub struct RuntimeManifest {
  target : RuntimeTarget
  name : String
  extension : String
  payload : String
  diagnostics : Array[Diagnostic]
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn runtime_manifest(
  bundle : ExportBundle,
  name : String,
  target : RuntimeTarget,
) -> RuntimeManifest {
  let adapter = RuntimeAdapter::for_target(target)
  {
    target,
    name: adapter.manifest_name(name),
    extension: adapter.extension(),
    payload: adapter.emit(bundle),
    diagnostics: adapter.validate_bundle(bundle),
  }
}

///|
pub fn RuntimeManifest::valid(self : RuntimeManifest) -> Bool {
  !has_errors(self.diagnostics)
}

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

///|
pub fn runtime_manifests(
  bundle : ExportBundle,
  name : String,
) -> Array[RuntimeManifest] {
  runtime_targets().map(fn(target) { runtime_manifest(bundle, name, target) })
}

///|
pub fn runtime_manifest_named(
  bundle : ExportBundle,
  name : String,
  target_name : String,
) -> RuntimeManifest {
  runtime_manifest(bundle, name, runtime_target_from_string(target_name))
}

///|
pub fn runtime_payload_size(manifest : RuntimeManifest) -> Int {
  manifest.payload.length()
}

///|
pub fn runtime_manifest_summary(manifest : RuntimeManifest) -> String {
  "\{manifest.name}: \{manifest.extension}, \{runtime_payload_size(manifest)} bytes, valid=\{manifest.valid()}"
}

///|
pub fn runtime_all_valid(manifests : Array[RuntimeManifest]) -> Bool {
  for manifest in manifests {
    if !manifest.valid() {
      return false
    }
  }
  true
}

///|
pub fn runtime_target_supports_slices(target : RuntimeTarget) -> Bool {
  RuntimeAdapter::for_target(target).supports_nine_patch
}

///|
pub fn runtime_target_supports_collisions(target : RuntimeTarget) -> Bool {
  RuntimeAdapter::for_target(target).supports_collisions
}

///|
pub fn runtime_target_extension(target : RuntimeTarget) -> String {
  RuntimeAdapter::for_target(target).extension()
}

///|
pub fn runtime_target_format(target : RuntimeTarget) -> String {
  RuntimeAdapter::for_target(target).format()
}