"""Single integrated keychain — bottle opener + whistle + head relief.
Designed from scratch as ONE coherent shape (source files used only as size/proportion reference)."""

import trimesh
import numpy as np
from shapely.geometry import box, Point, Polygon
from shapely.ops import unary_union
from pathlib import Path

DIR = Path("/home/johnbarley/files/atydarytuvas")

# ════════════════════════════════════════════════════════════════════
# DIMENSIONS
# ════════════════════════════════════════════════════════════════════
LEN          = 95          # total length X (left=opener end, right=whistle end)
THK          = 14          # thickness Z
W_NARROW     = 26          # width at opener end (Y)
W_WIDE       = 38          # width at whistle end (Y)

# Bottle opener notch (left end)
NOTCH_W      = 24          # along X
NOTCH_D      = 9           # into the body from the +Y edge
LIP_LEN      = 4           # cap-grip lip length (Y direction inside notch)
LIP_Z        = 5           # lip thickness (Z; sits on top, leaving a 4mm gap below for cap to slide under)

# Whistle (right end) — drilled into body
CHAMBER_DIA  = 16          # resonant chamber diameter
CHAMBER_X    = LEN/2 - 12  # chamber center X position
WINDWAY_W    = 8           # width of windway slit (X dir)
WINDWAY_GAP  = 1.0         # height of air gap (Z) — narrow slit
MOUTH_LEN    = 14          # length of mouthpiece tube projecting from edge

# Keychain hole
KEY_R        = 2.5
KEY_X        = -LEN/2 + 6

# Head relief
HEAD_SCALE   = 0.16        # ~22mm tall head
HEAD_EMBOSS  = 1.5

HEAD_POLY = np.array([
  [20.64,129.16],[22.19,125.48],[25.11,120.10],[24.35,119.55],[21.68,119.13],
  [17.60,119.74],[13.58,121.22],[9.20,123.61],[1.16,128.80],[0.00,129.24],
  [3.23,118.60],[4.96,115.15],[8.53,109.31],[7.71,109.23],[6.35,110.00],
  [0.71,115.02],[7.73,101.83],[5.68,102.86],[0.94,106.98],[0.58,107.21],
  [0.59,106.93],[4.08,100.88],[9.80,92.86],[5.60,95.96],[4.89,96.19],
  [5.28,95.43],[9.54,89.18],[13.42,84.60],[8.95,86.99],[11.87,82.78],
  [15.28,79.35],[15.07,79.07],[14.36,79.15],[9.78,80.71],[13.12,77.61],
  [20.70,71.78],[22.93,69.58],[27.18,63.50],[28.27,61.02],[29.10,57.99],
  [30.04,51.36],[29.95,44.51],[23.89,29.26],[23.02,26.26],[84.03,0.87],
  [86.61,0.02],[87.02,0.05],[87.15,0.45],[87.23,9.58],[86.55,16.40],
  [85.11,22.65],[82.83,28.35],[79.52,34.18],[73.13,43.48],[72.35,45.47],
  [71.75,49.55],[71.85,54.83],[72.64,58.89],[74.04,62.47],[76.91,65.44],
  [79.86,67.43],[84.44,69.43],[92.14,72.05],[97.25,73.42],[104.11,74.78],
  [137.78,80.32],[140.69,81.44],[141.29,82.22],[141.39,83.06],[140.63,84.02],
  [135.55,85.44],[101.69,93.01],[94.93,94.82],[92.84,96.05],[88.78,99.20],
  [76.06,110.23],[72.20,112.93],[68.48,115.06],[57.27,119.31],[52.99,121.58],
  [50.37,120.24],[48.26,119.99],[45.94,120.66],[42.34,122.68],[42.91,120.15],
  [38.68,122.22],[32.91,125.64],[34.24,121.93],[33.64,121.85],[27.79,124.48],
  [23.86,127.06],[20.53,129.97],[20.61,129.30]
])


# ════════════════════════════════════════════════════════════════════
# 1. BODY — teardrop/paddle: narrow on left (opener), wide on right (whistle)
# ════════════════════════════════════════════════════════════════════
print("Building body shape...")
# 2D outline made of: trapezoid (left tapering to wide) + circle (right whistle area)
# Use a polygon that tapers smoothly + a circle at +X end blended via union
trap = Polygon([
    (-LEN/2,        -W_NARROW/2),
    (-LEN/2,         W_NARROW/2),
    (CHAMBER_X,      W_WIDE/2),
    (CHAMBER_X,     -W_WIDE/2),
])
circ = Point(CHAMBER_X, 0).buffer(W_WIDE/2, resolution=64)
body_2d = unary_union([trap, circ])
# Smooth rounding
body_2d = body_2d.buffer(2, resolution=24, cap_style=1, join_style=1).buffer(-2)
# Add overall corner rounding on left end
body_2d = body_2d.buffer(3, resolution=24, join_style=1).buffer(-3, resolution=24, join_style=1)

body = trimesh.creation.extrude_polygon(body_2d, height=THK)
print(f"  body: {body.extents.round(2).tolist()} watertight={body.is_watertight}")

# ════════════════════════════════════════════════════════════════════
# 2. BOTTLE OPENER NOTCH — U-shape on +Y edge, with cap-grip overhang
# ════════════════════════════════════════════════════════════════════
print("Cutting bottle opener notch...")
notch_x = -LEN/2 + 4 + NOTCH_W/2  # left end, with margin
# LOWER cut (full notch width × full depth), but only goes 60% up Z (leaves a top overhang lip)
notch_lower_2d = box(
    notch_x - NOTCH_W/2,
    W_NARROW/2 - NOTCH_D,
    notch_x + NOTCH_W/2,
    W_NARROW/2 + 2,
)
notch_lower = trimesh.creation.extrude_polygon(notch_lower_2d, height=THK*0.6 + 1)
notch_lower.apply_translation([0, 0, -1])
body = trimesh.boolean.difference([body, notch_lower], engine='manifold')

# UPPER cut (slightly shorter Y depth, so a lip remains on the inner notch wall)
notch_upper_2d = box(
    notch_x - NOTCH_W/2,
    W_NARROW/2 - NOTCH_D + LIP_LEN,
    notch_x + NOTCH_W/2,
    W_NARROW/2 + 2,
)
notch_upper = trimesh.creation.extrude_polygon(notch_upper_2d, height=THK*0.4 + 2)
notch_upper.apply_translation([0, 0, THK*0.6 - 1])
body = trimesh.boolean.difference([body, notch_upper], engine='manifold')
print(f"  after notch: V={len(body.vertices)} watertight={body.is_watertight}")

# ════════════════════════════════════════════════════════════════════
# 3. WHISTLE — drill resonant chamber + cut mouthpiece slit + form fipple edge
# ════════════════════════════════════════════════════════════════════
print("Carving whistle features...")
# 3a. Cylindrical resonant chamber through full thickness
chamber = trimesh.creation.cylinder(radius=CHAMBER_DIA/2, height=THK + 2, sections=64)
chamber.apply_translation([CHAMBER_X, 0, THK/2])
body = trimesh.boolean.difference([body, chamber], engine='manifold')

# 3b. Mouthpiece — rectangular slit cut from +Y outside edge into the chamber.
# The slit is OFFSET from the chamber's centerline (on -X side of chamber) so when air enters
# from +Y direction, it passes the fipple (chamber wall at +X side of slit) and resonates inside.
slit_x_center = CHAMBER_X - 4   # offset toward -X side
slit_2d = box(
    slit_x_center - WINDWAY_W/2,
    -W_WIDE/2 + 2,                    # outside edge entry
    slit_x_center + WINDWAY_W/2,
    -CHAMBER_DIA/2 + 1,                # ends inside chamber
)
slit = trimesh.creation.extrude_polygon(slit_2d, height=WINDWAY_GAP)
slit.apply_translation([0, 0, THK/2 - WINDWAY_GAP/2])
body = trimesh.boolean.difference([body, slit], engine='manifold')

# 3c. Top opening — round hole on top of chamber (sound exit / blow-out)
top_hole = trimesh.creation.cylinder(radius=3, height=THK + 2, sections=32)
top_hole.apply_translation([CHAMBER_X + 4, 0, THK/2])  # offset from chamber center
# Don't subtract — the chamber already provides the top opening.
# (The cylindrical chamber goes all the way through Z, so it's open top and bottom.)
print(f"  after whistle: V={len(body.vertices)} watertight={body.is_watertight}")

# ════════════════════════════════════════════════════════════════════
# 4. KEYCHAIN HOLE — small ∅5 hole at left end
# ════════════════════════════════════════════════════════════════════
key_hole = trimesh.creation.cylinder(radius=KEY_R, height=THK + 2, sections=32)
key_hole.apply_translation([KEY_X, 0, THK/2])
body = trimesh.boolean.difference([body, key_hole], engine='manifold')

# ════════════════════════════════════════════════════════════════════
# 5. HEAD RELIEF — woodpecker silhouette embossed on top face
# ════════════════════════════════════════════════════════════════════
print("Embossing head relief...")
scaled_poly = HEAD_POLY * HEAD_SCALE
scaled_poly -= [(scaled_poly[:, 0].min() + scaled_poly[:, 0].max()) / 2,
                (scaled_poly[:, 1].min() + scaled_poly[:, 1].max()) / 2]
head_emboss = trimesh.creation.extrude_polygon(Polygon(scaled_poly), height=HEAD_EMBOSS)
# Position centered between bottle opener notch (left) and chamber (right) on top face
head_x = (notch_x + NOTCH_W/2 + CHAMBER_X - CHAMBER_DIA/2) / 2 + 2
head_emboss.apply_translation([head_x, 0, THK])

body = trimesh.boolean.union([body, head_emboss], engine='manifold')

print(f"\n=== FINAL ===")
print(f"V={len(body.vertices)} F={len(body.faces)} extents={body.extents.round(1).tolist()} vol={body.volume/1000:.1f}cm³ watertight={body.is_watertight}")

out_stl = DIR / "atidarytuvas-v1.stl"
out_3mf = DIR / "atidarytuvas-v1.3mf"
body.export(out_stl)
body.export(out_3mf)
print(f"Saved: {out_stl.name}, {out_3mf.name}")
