"""
The insert - ONE print: false-floor tray with the hardware bay across its
front edge, under the slot.   X across the slot (153), Y depth (255), Z up.

Tray: flat 13 mm deck on a perimeter rim + ribs standing on the steel; a
wire corridor down the centre; two sensor slabs on the centreline.

Bay (fixed): floor plate, slot-side wall with a top RAIL carrying the GROOVE
the cover's top edge drops into, end walls (= tray side rims at the front),
inner LEDGES the cover rests on, a vertical WIRE TUBE up the inside of the
right end wall near the back of the bay, foot-screw BOSSES under the deck,
drains, and the cable notch through the rim for the two floor sensors.

The removable cover (rear wall + slope + foot) is bay_cover.py. It drops in
vertically: top edge into the groove, foot onto the deck, two screws down.

Run: python insert.py -> out/insert.stl + step
"""
from pathlib import Path
from build123d import *
import params as P

OUT = Path(__file__).resolve().parent.parent / "out"; OUT.mkdir(exist_ok=True)
WALL = P.WALL
Y0 = P.TRAY_Y0; HX = P.TRAY_HX; RIM_T = 2 * WALL
DECK_TOP = P.FLOOR_TOTAL_H; DECK_BOT = DECK_TOP - P.FLOOR_T
COVER_OUT_Y = Y0 + P.BAY_D                  # 28.5
COVER_IN_Y = COVER_OUT_Y - P.COVER_T        # 26.1
LEDGE_Y = COVER_IN_Y - P.COVER_CLR          # 25.7 = deck/rim front edge
RIM_Y1 = LEDGE_Y + RIM_T                    # 29.7

BEND = (COVER_OUT_Y, P.COVER_BEND_Z)
PEAK = (P.LIP_Y1 + P.COVER_CLR, P.TOP_Z)                # (4.7, 84)
_dy, _dz = PEAK[0] - BEND[0], PEAK[1] - BEND[1]
_len = (_dy ** 2 + _dz ** 2) ** 0.5
SLOPE_DIR = (_dy / _len, _dz / _len)
NORM_IN = (-_dz / _len, _dy / _len)
if NORM_IN[0] > 0: NORM_IN = (-NORM_IN[0], -NORM_IN[1])

def slope_z(y, offset=0.0):
    by, bz = BEND[0] + NORM_IN[0] * offset, BEND[1] + NORM_IN[1] * offset
    return bz + SLOPE_DIR[1] * ((by - y) / -SLOPE_DIR[0])

SILHOUETTE = [(Y0, 0), (COVER_OUT_Y, 0), BEND, PEAK, (Y0, P.TOP_Z)]
LEDGE_OFF = P.COVER_T + P.COVER_CLR
# ledge: solid from the slot wall back to the cover's inner line, capped at
# the step height near the wall so the cover's top edge clears it
LEDGE = [(Y0, 0), (LEDGE_Y, 0), (LEDGE_Y, slope_z(LEDGE_Y, LEDGE_OFF)),
         (P.STEP_Y1, min(slope_z(P.STEP_Y1, LEDGE_OFF), P.STEP_Z)),
         (P.STEP_Y1, P.STEP_Z), (Y0, P.STEP_Z)]
# slot wall with the step: 2 mm wall, 45 deg gusset out to the step, thin lip
GUSSET = P.STEP_Y1 - (Y0 + WALL)
SLOT_WALL = [(Y0, 0), (Y0 + WALL, 0), (Y0 + WALL, P.STEP_Z - GUSSET),
             (P.STEP_Y1, P.STEP_Z), (P.LIP_Y1, P.STEP_Z), (P.LIP_Y1, P.TOP_Z),
             (Y0, P.TOP_Z)]
TUBE_X1 = HX - RIM_T; TUBE_X0 = TUBE_X1 - P.TUBE_OUT
TUBE_Y1 = P.TUBE_Y0 + P.TUBE_OUT
TUBE_TOP = slope_z(P.TUBE_Y0) + P.TUBE_TOP_PROUD

def box(x, y, w, d, z0, z1, mode=Mode.ADD):
    with BuildSketch(Plane.XY.offset(z0)) as sk:
        with Locations((x, y)): Rectangle(w, d)
    extrude(sk.sketch, amount=z1 - z0, mode=mode)

def cyl(x, y, dia, z0, z1, mode=Mode.SUBTRACT):
    with BuildSketch(Plane.XY.offset(z0)) as sk:
        with Locations((x, y)): Circle(dia / 2)
    extrude(sk.sketch, amount=z1 - z0, mode=mode)

def yz_poly(pts, x0, thickness):
    with BuildSketch(Plane.YZ.offset(x0)) as sk: Polygon(*pts, align=None)
    extrude(sk.sketch, amount=thickness)

def build_insert():
    Y1 = P.TRAY_Y1
    with BuildPart() as bp:
        # ---------------- tray ----------------
        box(0, (LEDGE_Y + Y1) / 2, 2 * HX, Y1 - LEDGE_Y, DECK_BOT, DECK_TOP)
        for sx in (-1, 1):
            box(sx * (HX - RIM_T / 2), (Y0 + Y1) / 2, RIM_T, Y1 - Y0, 0, DECK_BOT)
        box(0, Y1 - RIM_T / 2, 2 * HX, RIM_T, 0, DECK_BOT)
        box(0, (LEDGE_Y + RIM_Y1) / 2, 2 * HX, RIM_T, 0, DECK_BOT)
        rw = P.RIB_W
        for rx in P.RIB_X:
            for sx in (-1, 1):
                box(sx * rx, (RIM_Y1 + Y1) / 2, rw, Y1 - RIM_Y1, 0, DECK_BOT)
        for sx in (-1, 1):
            box(sx * P.CORRIDOR_HALF_W, (RIM_Y1 + Y1) / 2, rw, Y1 - RIM_Y1, 0, DECK_BOT)
        for ry in P.RIB_Y:
            for sx in (-1, 1):
                x0, x1 = P.CORRIDOR_HALF_W + rw / 2, HX
                box(sx * (x0 + x1) / 2, ry, x1 - x0, rw, 0, DECK_BOT)
        for sy in P.SENSOR_Y:
            with BuildSketch(Plane.XY) as sk:
                with Locations((P.BEAM_X, sy)): RectangleRounded(P.PT_SLAB_L, P.PT_SLAB_W, 3.0)
            extrude(sk.sketch, amount=DECK_BOT)

        # ---------------- bay ----------------
        L = 2 * HX
        box(0, (Y0 + LEDGE_Y + 0.2) / 2, L, LEDGE_Y + 0.2 - Y0, 0, WALL)
        yz_poly(SLOT_WALL, -L / 2, L)
        yz_poly(SILHOUETTE, HX - WALL, WALL); yz_poly(SILHOUETTE, -HX, WALL)
        yz_poly(LEDGE, HX - RIM_T, WALL);     yz_poly(LEDGE, -HX + WALL, WALL)
        tx, ty = (TUBE_X0 + TUBE_X1) / 2, (P.TUBE_Y0 + TUBE_Y1) / 2
        box(tx, ty, P.TUBE_OUT, P.TUBE_OUT, WALL, TUBE_TOP)
        box(tx, ty, P.TUBE_BORE, P.TUBE_BORE, WALL + 1.0, TUBE_TOP + 0.1, Mode.SUBTRACT)
        box(TUBE_X0 + 0.7, ty, 1.6, P.TUBE_BORE, WALL + 1.0, WALL + 8.0, Mode.SUBTRACT)
        for sx in (-P.FOOT_SCREW_X, P.FOOT_SCREW_X):
            fy = COVER_OUT_Y + P.FOOT_SCREW_Y_OFF
            cyl(sx, fy, P.BOSS_DIA, 0, DECK_TOP, Mode.ADD)
            cyl(sx, fy, P.SCREW_PILOT_D, -0.1, DECK_TOP + 0.1)

        # ---------------- cuts ----------------
        for i in range(P.DRAIN_COUNT):
            xd = (i - (P.DRAIN_COUNT - 1) / 2) * (L / (P.DRAIN_COUNT + 1))
            cyl(xd, Y0 + P.BAY_D / 2, P.DRAIN_DIA, -0.1, WALL + 0.1)
        box(0, (LEDGE_Y - 1 + RIM_Y1 + 0.5) / 2, P.CABLE_NOTCH_W,
            RIM_Y1 + 0.5 - (LEDGE_Y - 1), -0.1, P.CABLE_NOTCH_H, Mode.SUBTRACT)
        recess_d = P.PT_BORE_DIA + 2 * P.PT_RIDGE_W
        body_d = P.PT_DIA + 2 * P.PART_CLR
        flange_d = P.PT_FLANGE_DIA + 0.6
        body_bot = P.LEG_CH_D; seat_top = body_bot + P.PT_FLANGE_SEAT_D
        body_top = body_bot + P.PT_BODY_H; bore_top = DECK_TOP - P.PT_RECESS
        for sy in P.SENSOR_Y:
            x = P.BEAM_X
            cyl(x, sy, recess_d, bore_top, DECK_TOP + 0.1)
            cyl(x, sy, P.PT_BORE_DIA, body_top, bore_top + 0.01)
            cyl(x, sy, body_d, seat_top - 0.01, body_top + 0.01)
            cyl(x, sy, flange_d, -0.1, seat_top)
            for dx in (-P.LEAD_PITCH / 2, P.LEAD_PITCH / 2):
                y_out = sy - P.PT_SLAB_W / 2 - 0.5
                box(x + dx, (sy + y_out) / 2, P.LEG_CH_W, sy - y_out, -0.1, P.LEG_CH_D, Mode.SUBTRACT)
        box(5.5, P.SENSOR_Y[0], 3.0, P.PT_SLAB_W + 1.0, -0.1, 4.0, Mode.SUBTRACT)
    return bp.part

if __name__ == "__main__":
    part = build_insert(); bb = part.bounding_box()
    print(f"Insert X {bb.min.X:.1f}..{bb.max.X:.1f} ({bb.size.X:.1f})  Y {bb.min.Y:.1f}..{bb.max.Y:.1f} ({bb.size.Y:.1f})  Z {bb.min.Z:.1f}..{bb.max.Z:.1f}")
    print(f"Volume {part.volume/1000:.1f} cm^3   tube top Z {TUBE_TOP:.1f}")
    lim = P.BUILD_VOL - P.PRINT_MARGIN
    print("  checks passed" if (bb.max.Z <= P.BOX_H + 1e-6 and bb.size.X <= lim and bb.size.Y <= lim) else "  CHECKS FAILED")
    export_stl(part, str(OUT / "insert.stl")); export_step(part, str(OUT / "insert.step"))
    print("wrote out/insert.stl, out/insert.step")
