///|
/// 29-headphone-hook
///
/// A parametric wall-mounted headphone hook.
/// Demonstrates profile construction with computed fillets for strength
/// and aesthetics.
///
/// Usage (stdout):
///   moon run --target native examples/29-headphone-hook -- > hook.step
///
/// Usage (write file):
///   moon run --target native examples/29-headphone-hook -- -o hook.step
async fn main {
  let base_args_spec = {
    "width": @cli.opt_double(30.0, help="Width of the hook (mm)", min=10.0),
    "armLength": @cli.opt_double(
      60.0,
      help="Length of the horizontal arm (mm)",
      min=20.0,
    ),
    "hookHeight": @cli.opt_double(
      25.0,
      help="Height of the front retaining lip (mm)",
      min=10.0,
    ),
    "mountHeight": @cli.opt_double(
      50.0,
      help="Height of the mounting plate (mm)",
      min=20.0,
    ),
    "thickness": @cli.opt_double(
      6.0,
      help="Main material thickness (mm)",
      min=2.0,
    ),
    "filletRadius": @cli.opt_double(
      8.0,
      help="Radius of the inner fillets (mm)",
      min=0.0,
    ),
  }
  let args_spec = @cli.with_translate(@common.with_output_args(base_args_spec))
  guard @cli.parse(
      args_spec,
      prog="29-headphone-hook",
      description="Generate a parametric headphone hook.",
    )
    is Some(args) else {
    return
  }
  let width = args.double("width")
  let arm_l = args.double("armLength")
  let hook_h = args.double("hookHeight")
  let mount_h = args.double("mountHeight")
  let t = args.double("thickness")
  let r = args.double("filletRadius")
  let output = @common.output_paths(args)
  let (tx, ty, tz) = match args.translate_xyz() {
    Some(v) => v
    None => {
      @cli.eprintln("error: invalid translation")
      abort("")
    }
  }

  // Create the main hook body
  let hook = main_hook_body(width, arm_l, hook_h, mount_h, t, r).rotate_x(90.0)

  // Add a reinforcing rib in the middle
  let rib_t = t / 2.0
  let rib = reinforcing_rib(mount_h, t, rib_t)
    .rotate_x(90.0)
    .translate(y=-(width - rib_t) / 2.0)

  // Add branding text on the side face
  let font = @cad.load_font("baloo")
  let design = @cad.Design::new(
      name="headphone-hook",
      documentation=args.documentation(),
    )
    .add(hook)
    .add(rib)
    .add_text_on_face(
      hook,
      "STUDIO",
      Top,
      font,
      font_size=t * 0.8,
      offset_x=t / 2.0,
      offset_y=t / 2.0 - mount_h / 2.0,
      color=@cad.Rgb::orange(),
    )
    .translate(x=tx, y=ty, z=tz)
  @common.write_output(design, output)
}

///|
fn main_hook_body(
  width : Double,
  arm_l : Double,
  hook_h : Double,
  mount_h : Double,
  t : Double,
  r : Double,
) -> @cad.SceneNode {
  let pts = []
  pts.push((0.0, 0.0))
  pts.push((arm_l, 0.0))
  pts.push((arm_l, hook_h))
  pts.push((arm_l - t, hook_h))
  let c2 = (arm_l - t - r, t + r)
  pts.push_iter(
    @cad.Profile2D::arc(2.0 * r, 0.0, -0.5 * @math.PI, segments=8)
    .iter()
    .map(fn(p) { (p.0 + c2.0, p.1 + c2.1) }),
  )
  let c1 = (t + r, t + r)
  pts.push_iter(
    @cad.Profile2D::arc(2.0 * r, 1.5 * @math.PI, @math.PI, segments=8)
    .iter()
    .map(fn(p) { (p.0 + c1.0, p.1 + c1.1) }),
  )
  pts.push((t, mount_h))
  pts.push((0.0, mount_h))
  @cad.ExtrudedProfile::new(pts, width)
  .with_name("headphone-hook")
  .with_color(@cad.Rgb::new(0.4, 0.2, 0.6))
}

///|
fn reinforcing_rib(
  mount_h : Double,
  t : Double,
  rib_t : Double,
) -> @cad.SceneNode {
  let profile = [(t, t), (t + mount_h * 0.5, t), (t, t + mount_h * 0.5)]
  @cad.ExtrudedProfile::new(profile, rib_t)
  .with_name("rib")
  .with_color(@cad.Rgb::new(0.3, 0.1, 0.5))
}