///|
fn json_escape(text : String) -> String {
  let out = StringBuilder()
  for ch in text {
    match ch {
      '"' => out.write_string("\\\"")
      '\\' => out.write_string("\\\\")
      '\n' => out.write_string("\\n")
      '\r' => out.write_string("\\r")
      '\t' => out.write_string("\\t")
      _ => out.write_char(ch)
    }
  }
  out.to_string()
}

///|
/// Quote CSV fields only when their content requires it.
fn csv_escape(text : String) -> String {
  let needs_quotes = text.contains(",") ||
    text.contains("\"") ||
    text.contains("\n") ||
    text.contains("\r")
  if !needs_quotes {
    return text
  }
  let out = StringBuilder()
  out.write_char('"')
  for ch in text {
    if ch == '"' {
      out.write_string("\"\"")
    } else {
      out.write_char(ch)
    }
  }
  out.write_char('"')
  out.to_string()
}

///|
fn algorithm_name(algorithm : Algorithm) -> String {
  match algorithm {
    ShelfFirstFit => "shelf-first-fit"
    GuillotineBestArea => "guillotine-best-area"
    SkylineBottomLeft => "skyline-bottom-left"
  }
}

///|
/// Export a deterministic JSON manifest. The output avoids external JSON
/// dependencies so it works in small WASM examples.
pub fn to_json_manifest(layout : Layout) -> String {
  let out = StringBuilder()
  out.write_string("{\n")
  out.write_string(
    "  \"bin\": { \"width\": \{layout.bin.width}, \"height\": \{layout.bin.height} },\n",
  )
  out.write_string(
    "  \"algorithm\": \"\{algorithm_name(layout.algorithm)}\",\n",
  )
  out.write_string("  \"occupancy_permille\": \{layout.occupancy_permille},\n")
  out.write_string("  \"waste_area\": \{layout.waste_area},\n")
  out.write_string("  \"placements\": [\n")
  for i in 0.. String {
  let out = StringBuilder()
  out.write_string(
    "rectloom layout \{layout.bin.width}x\{layout.bin.height} using \{algorithm_name(layout.algorithm)}\n",
  )
  out.write_string(
    "placed=\{layout.placements.length()} unpacked=\{layout.unpacked.length()} occupancy=\{layout.occupancy_permille}/1000 waste=\{layout.waste_area}\n",
  )
  for p in layout.placements {
    let marker = if p.rotated { " rotated" } else { "" }
    out.write_string(
      "- \{p.id}: x=\{p.x} y=\{p.y} w=\{p.width} h=\{p.height}\{marker}\n",
    )
  }
  if !layout.unpacked.is_empty() {
    out.write_string("unpacked:\n")
    for r in layout.unpacked {
      out.write_string("- \{r.id}: \{r.width}x\{r.height}\n")
    }
  }
  out.to_string()
}