///|
/// 11-hex-bit-holder
///
/// A parametric hex bit holder for standard 1/4" (6.35mm) screwdriver bits.
/// Demonstrates using grids of holes and multi-part assemblies (base + holder).
///
/// Usage (stdout):
///   moon run --target native examples/11-hex-bit-holder -- > holder.step
///
/// Usage (write file):
///   moon run --target native examples/11-hex-bit-holder -- -o holder.step
async fn main {
  let base_args_spec = {
    "rows": @cli.opt_double(2.0, help="Number of rows in the grid", min=1.0),
    "cols": @cli.opt_double(4.0, help="Number of columns in the grid", min=1.0),
    "bitDiameter": @cli.opt_double(
      6.35,
      help="Hex bit diameter across flats (mm). Standard is 6.35 (1/4\").",
      min=0.0,
      min_exclusive=true,
    ),
    "tolerance": @cli.opt_double(
      0.15,
      help="Extra clearance for the bit hole (mm)",
      min=0.0,
    ),
    "spacing": @cli.opt_double(
      10.0,
      help="Center-to-center spacing between bits (mm)",
      min=7.5,
    ),
    "height": @cli.opt_double(
      15.0,
      help="Height of the bit holder block (mm)",
      min=2.0,
    ),
    "baseHeight": @cli.opt_double(
      3.0,
      help="Thickness of the solid base plate (mm)",
      min=1.0,
    ),
  }
  let args_spec = @cli.with_translate(@common.with_output_args(base_args_spec))
  guard @cli.parse(
      args_spec,
      prog="11-hex-bit-holder",
      description="Generate a parametric hex bit holder.",
    )
    is Some(args) else {
    return
  }
  let rows = args.double("rows").to_int()
  let cols = args.double("cols").to_int()
  let bit_diameter = args.double("bitDiameter")
  let tolerance = args.double("tolerance")
  let spacing = args.double("spacing")
  let holder_height = args.double("height")
  let base_height = args.double("baseHeight")
  let output = @common.output_paths(args)
  let (tx, ty, tz) = match args.translate_xyz() {
    Some(v) => v
    None => {
      @cli.eprintln("error: invalid translation")
      abort("")
    }
  }

  //
  let total_width = Double::from_int(cols) * spacing
  let total_length = Double::from_int(rows) * spacing

  // 1. The solid base plate
  let base = @cad.Cuboid::new(total_width, total_length, base_height)
    .with_name("holder-base")
    .with_color(@cad.Rgb::magenta())
    .translate(x=tx, y=ty, z=tz)

  // 2. The holder block with hex holes
  let holder = hex_bit_holder(
      rows,
      cols,
      bit_diameter + tolerance,
      spacing,
      holder_height,
    )
    .with_name("holder-grid")
    .with_color(@cad.Rgb::blue())
    .translate(x=tx, y=ty, z=tz + base_height)

  //
  let design = @cad.Design::new(
      name="hex-bit-holder",
      documentation=args.documentation(),
    )
    .add(base)
    .add(holder)
  @common.write_output(design, output)
}

///|
fn hex_bit_holder(
  rows : Int32,
  cols : Int32,
  bit_diameter_across_flats : Double,
  spacing : Double,
  height : Double,
) -> @cad.Shape {
  let d = bit_diameter_across_flats / @math.cos(30.0 * @math.PI / 180.0)
  let hex = @cad.Profile2D::polygon(6, d, rotation=30.0)
  let total_width = Double::from_int(cols) * spacing
  let total_length = Double::from_int(rows) * spacing
  let outer = @cad.Profile2D::rect(total_width, total_length)
  let holes = []
  for r in 0..