///|
/// Color palette for SVG rectangle rendering.
let svg_palette : Array[String] = [
  "#4F46E5", "#06B6D4", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6", "#EC4899", "#3B82F6",
  "#14B8A6", "#84CC16", "#F97316", "#6366F1", "#0EA5E9", "#D946EF",
]

///|
/// Exports a packed rectangle layout to a self-contained, responsive SVG image.
pub fn to_svg(
  bin_width : Int32,
  bin_height : Int32,
  rects : Array[Rect],
  title~ : String = "moon_binpack Texture Atlas",
) -> String {
  let mut total_used_area = 0
  for r in rects {
    total_used_area = total_used_area + r.area()
  }
  let total_area = bin_width * bin_height
  let occupancy_pct = if total_area > 0 {
    (total_used_area.to_double() * 1000.0 / total_area.to_double())
    .to_int()
    .to_double() /
    10.0
  } else {
    0.0
  }

  let buf = StringBuilder()
  buf.write_string("\n")

  // Background and style
  buf.write_string("  \n")

  // Canvas background
  buf.write_string("  \n")

  // Header info
  buf.write_string("  ")
  buf.write_string(title)
  buf.write_string("\n")
  buf.write_string("  Occupancy: ",
  )
  buf.write_string(occupancy_pct.to_string())
  buf.write_string("% | ")
  buf.write_string(rects.length().to_string())
  buf.write_string(" items\n")

  // Bin outline
  buf.write_string("  \n")
  buf.write_string("    \n")

  // Packed rectangles
  for i = 0; i < rects.length(); i = i + 1 {
    let r = rects[i]
    let color_idx = (r.id + i) % svg_palette.length()
    let color = svg_palette[color_idx]

    buf.write_string("    \n")
    buf.write_string("      \n",
    )

    // Label if large enough
    if r.width >= 24 && r.height >= 16 {
      let cx = r.x + r.width / 2
      let cy = r.y + r.height / 2
      buf.write_string("      #")
      buf.write_string(r.id.to_string())
      buf.write_string("\n")
    }

    buf.write_string("    \n")
  }

  buf.write_string("  \n")
  buf.write_string("\n")

  buf.to_string()
}

///|
/// Exports packed rectangles into a standard JSON texture atlas manifest format.
pub fn to_json(
  bin_width : Int32,
  bin_height : Int32,
  rects : Array[Rect],
) -> String {
  let buf = StringBuilder()
  buf.write_string("{\n")
  buf.write_string("  \"meta\": {\n")
  buf.write_string("    \"width\": ")
  buf.write_string(bin_width.to_string())
  buf.write_string(",\n")
  buf.write_string("    \"height\": ")
  buf.write_string(bin_height.to_string())
  buf.write_string(",\n")
  buf.write_string("    \"count\": ")
  buf.write_string(rects.length().to_string())
  buf.write_string("\n  },\n")
  buf.write_string("  \"frames\": [\n")

  for i = 0; i < rects.length(); i = i + 1 {
    let r = rects[i]
    buf.write_string("    {\n")
    buf.write_string("      \"id\": ")
    buf.write_string(r.id.to_string())
    buf.write_string(",\n")
    buf.write_string("      \"x\": ")
    buf.write_string(r.x.to_string())
    buf.write_string(",\n")
    buf.write_string("      \"y\": ")
    buf.write_string(r.y.to_string())
    buf.write_string(",\n")
    buf.write_string("      \"width\": ")
    buf.write_string(r.width.to_string())
    buf.write_string(",\n")
    buf.write_string("      \"height\": ")
    buf.write_string(r.height.to_string())
    buf.write_string(",\n")
    buf.write_string("      \"rotated\": ")
    buf.write_string(if r.rotated { "true" } else { "false" })
    buf.write_string(",\n")
    buf.write_string("      \"bin\": ")
    buf.write_string(r.bin_index.to_string())
    buf.write_string("\n    }")
    if i + 1 < rects.length() {
      buf.write_string(",")
    }
    buf.write_string("\n")
  }

  buf.write_string("  ]\n")
  buf.write_string("}\n")

  buf.to_string()
}

///|
/// Exports packed rectangles into standard CSS Sprite rules.
/// Generates a common base class and individual utility classes with width, height,
/// and negative background-position offsets.
pub fn to_css(
  rects : Array[Rect],
  class_prefix~ : String = "sprite",
  image_url~ : String = "atlas.png",
) -> String {
  let buf = StringBuilder()
  buf.write_string("/* Generated by moon_binpack CSS Sprite Exporter */\n")
  buf.write_string(".")
  buf.write_string(class_prefix)
  buf.write_string(" {\n")
  buf.write_string("  display: inline-block;\n")
  buf.write_string("  background-image: url('")
  buf.write_string(image_url)
  buf.write_string("');\n")
  buf.write_string("  background-repeat: no-repeat;\n")
  buf.write_string("}\n\n")

  for i = 0; i < rects.length(); i = i + 1 {
    let r = rects[i]
    buf.write_string(".")
    buf.write_string(class_prefix)
    buf.write_string("-")
    buf.write_string(r.id.to_string())
    buf.write_string(" {\n")
    buf.write_string("  width: ")
    buf.write_string(r.width.to_string())
    buf.write_string("px;\n")
    buf.write_string("  height: ")
    buf.write_string(r.height.to_string())
    buf.write_string("px;\n")
    buf.write_string("  background-position: -")
    buf.write_string(r.x.to_string())
    buf.write_string("px -")
    buf.write_string(r.y.to_string())
    buf.write_string("px;\n")
    buf.write_string("}\n")
    if i + 1 < rects.length() {
      buf.write_string("\n")
    }
  }

  buf.to_string()
}

///|
/// Exports packed rectangles into standard CSV format for CAD, CNC, and spreadsheet analysis.
pub fn to_csv(rects : Array[Rect]) -> String {
  let buf = StringBuilder()
  buf.write_string("id,x,y,width,height,rotated,bin,area\n")
  for r in rects {
    buf.write_string(r.id.to_string())
    buf.write_string(",")
    buf.write_string(r.x.to_string())
    buf.write_string(",")
    buf.write_string(r.y.to_string())
    buf.write_string(",")
    buf.write_string(r.width.to_string())
    buf.write_string(",")
    buf.write_string(r.height.to_string())
    buf.write_string(",")
    buf.write_string(if r.rotated { "true" } else { "false" })
    buf.write_string(",")
    buf.write_string(r.bin_index.to_string())
    buf.write_string(",")
    buf.write_string(r.area().to_string())
    buf.write_string("\n")
  }
  buf.to_string()
}

///|
/// Exports packed rectangles into TexturePacker / Pixi.js / Phaser JSON Hash format.
pub fn to_pixi_json(
  bin_width : Int32,
  bin_height : Int32,
  rects : Array[Rect],
  image_name~ : String = "atlas.png",
) -> String {
  let buf = StringBuilder()
  buf.write_string("{\n")
  buf.write_string("  \"frames\": {\n")

  for i = 0; i < rects.length(); i = i + 1 {
    let r = rects[i]
    let orig_w = if r.rotated { r.height } else { r.width }
    let orig_h = if r.rotated { r.width } else { r.height }

    buf.write_string("    \"frame_")
    buf.write_string(r.id.to_string())
    buf.write_string("\": {\n")
    buf.write_string("      \"frame\": {\"x\": ")
    buf.write_string(r.x.to_string())
    buf.write_string(", \"y\": ")
    buf.write_string(r.y.to_string())
    buf.write_string(", \"w\": ")
    buf.write_string(r.width.to_string())
    buf.write_string(", \"h\": ")
    buf.write_string(r.height.to_string())
    buf.write_string("},\n")
    buf.write_string("      \"rotated\": ")
    buf.write_string(if r.rotated { "true" } else { "false" })
    buf.write_string(",\n")
    buf.write_string("      \"trimmed\": false,\n")
    buf.write_string("      \"spriteSourceSize\": {\"x\": 0, \"y\": 0, \"w\": ")
    buf.write_string(orig_w.to_string())
    buf.write_string(", \"h\": ")
    buf.write_string(orig_h.to_string())
    buf.write_string("},\n")
    buf.write_string("      \"sourceSize\": {\"w\": ")
    buf.write_string(orig_w.to_string())
    buf.write_string(", \"h\": ")
    buf.write_string(orig_h.to_string())
    buf.write_string("}\n")
    buf.write_string("    }")
    if i + 1 < rects.length() {
      buf.write_string(",")
    }
    buf.write_string("\n")
  }

  buf.write_string("  },\n")
  buf.write_string("  \"meta\": {\n")
  buf.write_string("    \"app\": \"moon_binpack\",\n")
  buf.write_string("    \"version\": \"0.1.0\",\n")
  buf.write_string("    \"image\": \"")
  buf.write_string(image_name)
  buf.write_string("\",\n")
  buf.write_string("    \"format\": \"RGBA8888\",\n")
  buf.write_string("    \"size\": {\"w\": ")
  buf.write_string(bin_width.to_string())
  buf.write_string(", \"h\": ")
  buf.write_string(bin_height.to_string())
  buf.write_string("},\n")
  buf.write_string("    \"scale\": \"1\"\n")
  buf.write_string("  }\n")
  buf.write_string("}\n")

  buf.to_string()
}

///|
/// Exports packed rectangles into a standalone, interactive HTML visual inspection report.
pub fn to_html(
  bin_width : Int32,
  bin_height : Int32,
  rects : Array[Rect],
  title~ : String = "moon_binpack Texture Atlas Report",
) -> String {
  let mut total_used_area = 0
  for r in rects {
    total_used_area = total_used_area + r.area()
  }
  let total_area = bin_width * bin_height
  let occupancy_pct = if total_area > 0 {
    (total_used_area.to_double() * 1000.0 / total_area.to_double())
    .to_int()
    .to_double() /
    10.0
  } else {
    0.0
  }

  let svg_content = to_svg(bin_width, bin_height, rects, title~)

  let buf = StringBuilder()
  buf.write_string("\n")
  buf.write_string("\n\n")
  buf.write_string("  \n")
  buf.write_string(
    "  \n",
  )
  buf.write_string("  ")
  buf.write_string(title)
  buf.write_string("\n")
  buf.write_string("  \n\n\n")
  buf.write_string("  
\n") buf.write_string("
\n") buf.write_string("

") buf.write_string(title) buf.write_string("

\n") buf.write_string( "

Interactive Texture Atlas Inspection Report generated by moon_binpack

\n", ) buf.write_string("
\n\n") // Metrics cards buf.write_string("
\n") buf.write_string( "
Atlas Dimensions
", ) buf.write_string(bin_width.to_string()) buf.write_string(" × ") buf.write_string(bin_height.to_string()) buf.write_string(" px
\n") buf.write_string( "
Occupancy Rate
", ) buf.write_string(occupancy_pct.to_string()) buf.write_string("%
\n") buf.write_string( "
Packed Items
", ) buf.write_string(rects.length().to_string()) buf.write_string("
\n") buf.write_string( "
Used Surface Area
", ) buf.write_string(total_used_area.to_string()) buf.write_string(" px²
\n") buf.write_string("
\n\n") // SVG Preview buf.write_string("
\n") buf.write_string(svg_content) buf.write_string("\n
\n\n") // Details Table buf.write_string("
\n") buf.write_string( "
Frame Details
\n", ) buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") buf.write_string( " \n", ) buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") for i = 0; i < rects.length(); i = i + 1 { let r = rects[i] buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") buf.write_string(" \n") } buf.write_string(" \n") buf.write_string("
#IDXYWidthHeightRotatedBin
") buf.write_string((i + 1).to_string()) buf.write_string("#") buf.write_string(r.id.to_string()) buf.write_string("") buf.write_string(r.x.to_string()) buf.write_string("") buf.write_string(r.y.to_string()) buf.write_string("") buf.write_string(r.width.to_string()) buf.write_string("") buf.write_string(r.height.to_string()) buf.write_string("") if r.rotated { buf.write_string("Yes") } else { buf.write_string("No") } buf.write_string("") buf.write_string(r.bin_index.to_string()) buf.write_string("
\n") buf.write_string("
\n\n") buf.write_string("
\n") buf.write_string( " Powered by moon_binpack — High-Performance 2D Bin Packing Engine in Pure MoonBit\n", ) buf.write_string("
\n") buf.write_string("
\n\n\n") buf.to_string() }