#!/usr/bin/env python3
from build123d import *
import os

OUT = "/app/projects/esp32-box/exports"
os.makedirs(OUT, exist_ok=True)

# PARAMS
W=80.0; D=70.0; H=50.0
BASE_H=32.0; LID_H=18.0
POST_SIZE=12.0  # corner post square
WALL=2.0        # thin wall
TOL=0.15

# Screw cavity in pillar
CAVITY_D=6.0    # diameter of screw head cavity
CAVITY_H=3.0    # depth of cavity from top
SCREW_D=3.0     # screw through-hole diameter

# Window panel  
WIN_W=14.0      # panel thickness (fits in slot)
SLOT_H=BASE_H-7.0  # opening height (keeps 3mm top/bottom margin)

CLEAR=0.15

def build_base():
    """4 corner posts + thin walls + window slots + screw cavities"""
    parts = []
    
    # 4 corner posts with screw cavities
    for sx in [-1, 1]:
        for sy in [-1, 1]:
            cx = sx * (W/2 - POST_SIZE/2)
            cy = sy * (D/2 - POST_SIZE/2)
            
            # Post body
            post = Box(POST_SIZE, POST_SIZE, BASE_H)
            
            # Screw cavity at top (counterbore)
            cavity = Pos(0, 0, BASE_H/2 - CAVITY_H/2) * Cylinder(CAVITY_D/2, CAVITY_D/2, CAVITY_H)
            post -= cavity
            
            # Through-hole for screw shaft (below cavity)
            hole_h = BASE_H - CAVITY_H
            hole = Pos(0, 0, -BASE_H/2 + hole_h/2) * Cylinder(SCREW_D/2 + TOL, SCREW_D/2 + TOL, hole_h)
            post -= hole
            
            parts.append(Pos(cx, cy, BASE_H/2) * post)
    
    # X-axis walls (front/back, along Y)
    for sy in [-1, 1]:
        py = sy * (D/2 - WALL/2)
        wall_len = W - 2*POST_SIZE
        if wall_len > 0:
            parts.append(Pos(0, py, BASE_H/2) * Box(wall_len, WALL, BASE_H))
    
    # Y-axis walls (left/right, along X)  
    for sx in [-1, 1]:
        px = sx * (W/2 - WALL/2)
        wall_len = D - 2*POST_SIZE
        if wall_len > 0:
            parts.append(Pos(px, 0, BASE_H/2) * Box(WALL, wall_len, BASE_H))
    
    # Combine
    base = parts[0]
    for p in parts[1:]:
        base += p
    
    # Cut window openings
    gap = 1.5  # gap from post
    for sy in [-1, 1]:
        py = sy * (D/2 - WALL/2)
        sl = W - 2*POST_SIZE - 2*gap
        slot = Pos(0, py, 2.0 + SLOT_H/2) * Box(sl, WALL + 0.4, SLOT_H)
        base -= slot
    
    for sx in [-1, 1]:
        px = sx * (W/2 - WALL/2)
        sl = D - 2*POST_SIZE - 2*gap
        slot = Pos(px, 0, 2.0 + SLOT_H/2) * Box(WALL + 0.4, sl, SLOT_H)
        base -= slot
    
    return base

def build_lid():
    lid = Box(W, D, 2.0)
    
    # Edge walls
    for sx in [-1, 1]:
        lid += Pos(0, sx*(D/2 - WALL/2), 1.5/2) * Box(W, WALL, 1.5)
    for sy in [-1, 1]:
        lid += Pos(sy*(W/2 - WALL/2), 0, 1.5/2) * Box(WALL, D, 1.5)
    
    # Tongue blocks (fit into posts)
    tsize = POST_SIZE - 1.0
    th = 3.0
    for sx in [-1, 1]:
        for sy in [-1, 1]:
            cx = sx * (W/2 - POST_SIZE/2)
            cy = sy * (D/2 - POST_SIZE/2)
            t = Pos(cx, cy, -th/2) * Box(tsize - TOL, tsize - TOL, th)
            lid += t
    
    # Screw clearance (countersink through lid)
    for sx in [-1, 1]:
        for sy in [-1, 1]:
            cx = sx * (W/2 - POST_SIZE/2)
            cy = sy * (D/2 - POST_SIZE/2)
            # Clearance for screw body
            lid -= Pos(cx, cy, 0) * Cylinder(SCREW_D/2 + TOL, SCREW_D/2 + TOL, LID_H + 0.2)
    
    # Ribs
    rib_spacing = (W - 2*POST_SIZE) / 2
    for i in range(2):
        rx = i * rib_spacing - rib_spacing/2
        lid += Pos(rx, 0, -LID_H/2 + 4.0) * Box(1.5, D - 2*POST_SIZE, 8.0)
    for i in range(2):
        ry = i * rib_spacing - rib_spacing/2
        lid += Pos(0, ry, -LID_H/2 + 4.0) * Box(W - 2*POST_SIZE, 1.5, 8.0)
    
    return lid

def build_window(cutouts=None):
    pw = W - 2*POST_SIZE
    ph = WIN_W
    pd = SLOT_H
    panel = Box(pw, ph, pd)
    for side in [-1, 1]:
        tab = Pos(0, side*(ph/2 - 0.5), 0) * Box(pw-2, 1.0, pd+0.5)
        panel += tab
    if cutouts:
        for cxo, cyo, cw, ch in cutouts:
            panel -= Pos(cxo, cyo, 0) * Box(cw, ch, pd + 0.2)
    return panel

def build_screw():
    s = Cylinder(3.0, 3.0, 2.0)
    s += Pos(0, 0, -5.0) * Cylinder(1.5, 1.5, 10.0)
    return s

print("Building base...")
base = build_base()
export_stl(base, "%s/base.stl" % OUT)
print("  base.stl: %dx%dx%dmm (%d tri)" % (W, D, BASE_H, len(base.faces())))

print("Building lid...")
lid = build_lid()
export_stl(lid, "%s/lid.stl" % OUT)
print("  lid.stl: %dx%dx%dmm" % (W, D, LID_H))

print("Building windows...")
pw = W - 2*POST_SIZE
export_stl(build_window(), "%s/window_smooth.stl" % OUT)
print("  window_smooth.stl: %dx%dmm" % (pw, WIN_W))

kc = [(-pw/4, 0, 17, WIN_W+1), (pw/4, 0, 9, 9)]
export_stl(build_window(kc), "%s/window_keystone_dc.stl" % OUT)
print("  window_keystone_dc.stl")

export_stl(build_window([(0, 0, 9, 5)]), "%s/window_usb.stl" % OUT)
print("  window_usb.stl")

print("Building screw...")
export_stl(build_screw(), "%s/screw.stl" % OUT)
print("  screw.stl")

print("\nDONE")
