///|
/// MoonSlicer Command-Line Interface.

///|
fn print_usage() -> Unit {
  println("==================================================")
  println("  MoonSlicer: Pure MoonBit 3D Printing Slicer")
  println("==================================================")
  println("")
  println("Usage:")
  println("  moonslicer  [options]")
  println("")
  println("Subcommands:")
  println(
    "  slice   Slice an STL model (ASCII or binary) into Marlin/RepRap G-code",
  )
  println(
    "  info    Inspect 3D mesh dimensions, facet count, and estimated layers",
  )
  println("  help    Show this help message")
  println("")
  println("Slice Options:")
  println("  -i               Input STL file path (required)")
  println(
    "  -o               Output G-code file path (default: output.gcode)",
  )
  println("  --layer-height     Layer height in millimeters (default: 0.2)")
  println("  --initial-height   First layer height in mm (default: 0.28)")
  println("  --line-width       Extrusion line width in mm (default: 0.4)")
  println("  --walls         Number of perimeter walls (default: 2)")
  println("  --bottom-solid  Number of bottom solid layers (default: 3)")
  println("  --top-solid     Number of top solid layers (default: 3)")
  println(
    "  --density       Infill density from 0.0 to 1.0 (default: 0.2)",
  )
  println(
    "  --pattern        Infill pattern: rectilinear, grid, triangles, concentric (default: rectilinear)",
  )
  println(
    "  --seam           Seam placement mode: sharpest, nearest, aligned, random (default: sharpest)",
  )
  println(
    "  --material       Filament material: pla, petg, abs, tpu (default: pla)",
  )
  println(
    "  --cost-per-kg    Filament cost per kilogram (default: 100.0)",
  )
  println(
    "  --speed          Print movement speed in mm/s (default: 50.0)",
  )
  println(
    "  --solid-speed    Solid infill movement speed in mm/s (default: 40.0)",
  )
  println(
    "  --travel-speed   Travel movement speed in mm/s (default: 120.0)",
  )
  println("  --bed-x            Bed center X coordinate (default: 100.0)")
  println("  --bed-y            Bed center Y coordinate (default: 100.0)")
}

///|
fn parse_double_arg(s : String, default_val : Double) -> Double {
  let mut res = 0.0
  let mut frac = 0.0
  let mut div = 1.0
  let mut in_frac = false
  for c in s {
    if c == '.' {
      in_frac = true
    } else {
      let d = c.to_int() - '0'.to_int()
      if d >= 0 && d <= 9 {
        if in_frac {
          div = div * 10.0
          frac = frac + d.to_double() / div
        } else {
          res = res * 10.0 + d.to_double()
        }
      }
    }
  }
  if res == 0.0 && frac == 0.0 && s != "0" && s != "0.0" {
    default_val
  } else {
    res + frac
  }
}

///|
fn parse_int_arg(s : String, default_val : Int32) -> Int32 {
  let mut res = 0
  for c in s {
    let d = c.to_int() - '0'.to_int()
    if d >= 0 && d <= 9 {
      res = res * 10 + d
    }
  }
  if res == 0 && s != "0" {
    default_val
  } else {
    res
  }
}

///|
fn run_info(input_path : String) -> Unit {
  if input_path.is_empty() {
    println("Error: Missing input file. Use: moonslicer info -i ")
    return
  }

  try {
    let bytes = @fs.read_file_to_bytes(input_path)
    println("Reading STL file: \{input_path} (\{bytes.length()} bytes)...")

    match @moonslicer.parse_stl(bytes) {
      Ok(mesh) => {
        let stats = @moonslicer.get_mesh_slice_stats(mesh)
        println("----------------------------------------")
        println("Mesh Inspection Report:")
        println("  Triangle Facets:   \{stats.triangle_count}")
        println("  Bounding Width (X):\{stats.width} mm")
        println("  Bounding Depth (Y):\{stats.depth} mm")
        println("  Bounding Height (Z):\{stats.height} mm")
        println("  Z Range:           [\{stats.min_z}, \{stats.max_z}] mm")
        println("  Estimated Layers:  \{stats.estimated_layers} (at 0.2mm)")
        println("----------------------------------------")
      }
      Err(err) => println("Error parsing STL: \{err}")
    }
  } catch {
    @fs.IOError(msg) => println("File I/O error: \{msg}")
  }
}

///|
fn run_slice(args : Array[String]) -> Unit {
  let mut input_path = ""
  let mut output_path = "output.gcode"
  let mut layer_height = 0.2
  let mut initial_height = 0.28
  let mut line_width = 0.4
  let mut walls = 2
  let mut bottom_solid = 3
  let mut top_solid = 3
  let mut density = 0.2
  let mut pattern_name = "rectilinear"
  let mut seam_name = "sharpest"
  let mut mat_name = "pla"
  let mut cost_per_kg = 100.0
  let mut speed = 50.0
  let mut solid_speed = 40.0
  let mut travel_speed = 120.0
  let mut bed_x = 100.0
  let mut bed_y = 100.0

  let n = args.length()
  let mut i = 2
  while i < n {
    let arg = args[i]
    if (arg == "-i" || arg == "--input") && i + 1 < n {
      input_path = args[i + 1]
      i += 2
    } else if (arg == "-o" || arg == "--output") && i + 1 < n {
      output_path = args[i + 1]
      i += 2
    } else if arg == "--layer-height" && i + 1 < n {
      layer_height = parse_double_arg(args[i + 1], 0.2)
      i += 2
    } else if arg == "--initial-height" && i + 1 < n {
      initial_height = parse_double_arg(args[i + 1], 0.28)
      i += 2
    } else if arg == "--line-width" && i + 1 < n {
      line_width = parse_double_arg(args[i + 1], 0.4)
      i += 2
    } else if arg == "--walls" && i + 1 < n {
      walls = parse_int_arg(args[i + 1], 2)
      i += 2
    } else if arg == "--bottom-solid" && i + 1 < n {
      bottom_solid = parse_int_arg(args[i + 1], 3)
      i += 2
    } else if arg == "--top-solid" && i + 1 < n {
      top_solid = parse_int_arg(args[i + 1], 3)
      i += 2
    } else if arg == "--density" && i + 1 < n {
      density = parse_double_arg(args[i + 1], 0.2)
      i += 2
    } else if arg == "--pattern" && i + 1 < n {
      pattern_name = args[i + 1]
      i += 2
    } else if arg == "--seam" && i + 1 < n {
      seam_name = args[i + 1]
      i += 2
    } else if arg == "--material" && i + 1 < n {
      mat_name = args[i + 1]
      i += 2
    } else if arg == "--cost-per-kg" && i + 1 < n {
      cost_per_kg = parse_double_arg(args[i + 1], 100.0)
      i += 2
    } else if arg == "--speed" && i + 1 < n {
      speed = parse_double_arg(args[i + 1], 50.0)
      i += 2
    } else if arg == "--solid-speed" && i + 1 < n {
      solid_speed = parse_double_arg(args[i + 1], 40.0)
      i += 2
    } else if arg == "--travel-speed" && i + 1 < n {
      travel_speed = parse_double_arg(args[i + 1], 120.0)
      i += 2
    } else if arg == "--bed-x" && i + 1 < n {
      bed_x = parse_double_arg(args[i + 1], 100.0)
      i += 2
    } else if arg == "--bed-y" && i + 1 < n {
      bed_y = parse_double_arg(args[i + 1], 100.0)
      i += 2
    } else {
      i += 1
    }
  }

  if input_path.is_empty() {
    println("Error: Input file (-i ) is required.")
    println("Run 'moonslicer help' for usage instructions.")
    return
  }

  let pattern : @moonslicer.InfillPattern = if pattern_name == "grid" {
    Grid
  } else if pattern_name == "triangles" {
    Triangles
  } else if pattern_name == "concentric" {
    Concentric
  } else {
    Rectilinear
  }

  let seam_mode : @moonslicer.SeamMode = if seam_name == "nearest" {
    Nearest
  } else if seam_name == "aligned" {
    Aligned
  } else if seam_name == "random" {
    Random
  } else {
    SharpestCorner
  }

  let material : @moonslicer.FilamentMaterial = if mat_name == "petg" {
    PETG
  } else if mat_name == "abs" {
    ABS
  } else if mat_name == "tpu" {
    TPU
  } else {
    PLA
  }

  let mut config = @moonslicer.SlicerConfig::default()
  config = {
    ..config,
    layer_height,
    initial_layer_height: initial_height,
    line_width,
    wall_count: walls,
    bottom_solid_layers: bottom_solid,
    top_solid_layers: top_solid,
    infill_density: density,
    infill_pattern: pattern,
    seam_mode,
    material,
    filament_cost_per_kg: cost_per_kg,
    print_speed: speed,
    solid_infill_speed: solid_speed,
    travel_speed,
  }

  println("Loading model from: \{input_path}...")
  try {
    let bytes = @fs.read_file_to_bytes(input_path)
    println("Parsing mesh (\{bytes.length()} bytes)...")

    match @moonslicer.parse_stl(bytes) {
      Ok(mesh) => {
        let stats = @moonslicer.get_mesh_slice_stats(mesh, config~)
        println(
          "Mesh: \{stats.triangle_count} facets, \{stats.width}x\{stats.depth}x\{stats.height} mm",
        )
        println(
          "Slicing layers (height: \{layer_height} mm, pattern: \{pattern}, seam: \{seam_mode})...",
        )

        let result = @moonslicer.slice_mesh_to_gcode(
          mesh,
          config~,
          bed_center_x=bed_x,
          bed_center_y=bed_y,
        )

        println("Synthesizing G-code (\{result.total_layers} layers)...")
        @fs.write_string_to_file(output_path, result.gcode)

        println("----------------------------------------")
        println("Slicing Completed Successfully!")
        println("  Output File:        \{output_path}")
        println("  Total Layers:       \{result.total_layers}")
        println("  Collisions Resolved:\{result.total_collisions_resolved}")
        println("  Filament Extruded:  \{result.total_filament_mm} mm")
        println("  Filament Mass:      \{result.total_filament_grams} g")
        println("  Estimated Cost:     ¥\{result.estimated_cost}")
        println(
          "  Est. Print Time:    \{result.estimated_print_time_sec / 60.0} min",
        )
        println("----------------------------------------")
      }
      Err(err) => println("Error parsing STL mesh: \{err}")
    }
  } catch {
    @fs.IOError(msg) => println("File I/O error: \{msg}")
  }
}

///|
fn main {
  let args = @env.args()

  if args.length() <= 1 {
    print_usage()
    return
  }

  let subcmd = args[1]
  if subcmd == "slice" {
    run_slice(args)
  } else if subcmd == "info" {
    let mut input_path = ""
    let n = args.length()
    let mut i = 2
    while i < n {
      if (args[i] == "-i" || args[i] == "--input") && i + 1 < n {
        input_path = args[i + 1]
        i += 2
      } else {
        i += 1
      }
    }
    run_info(input_path)
  } else if subcmd == "help" || subcmd == "-h" || subcmd == "--help" {
    print_usage()
  } else {
    println("Unknown subcommand: '\{subcmd}'. Run 'moonslicer help' for usage.")
  }
}