"""Generate the custom STEP models used by the OptoTest PCB libraries.

The coordinate system follows Altium's usual convention: XY is the PCB plane and
Z points away from the top side of the PCB.  All dimensions are in millimetres.
"""

from pathlib import Path
import sys


CADQUERY_PATH = Path.home() / "AppData/Local/Temp/codex-cadquery"
if CADQUERY_PATH.exists():
    sys.path.insert(0, str(CADQUERY_PATH))

import cadquery as cq


OUT_DIR = Path(__file__).resolve().parent


def box(x, y, z, cx=0.0, cy=0.0, bottom=0.0):
    return cq.Workplane("XY").box(x, y, z).translate((cx, cy, bottom + z / 2))


def cylinder(diameter, height, cx=0.0, cy=0.0, bottom=0.0):
    return (
        cq.Workplane("XY")
        .circle(diameter / 2)
        .extrude(height)
        .translate((cx, cy, bottom))
    )


def add_dip8_precision_socket(assembly):
    """7.62-mm-row precision/turned-pin DIP-8 socket seated on the PCB."""
    black = cq.Color(0.055, 0.055, 0.060)
    gold = cq.Color(0.78, 0.55, 0.12)
    dark = cq.Color(0.015, 0.015, 0.018)

    # Two turned-pin carrier rails and end bridges.  The centre remains open,
    # matching the precision socket used by the SN75451 model.
    assembly.add(box(2.35, 10.15, 3.65, -3.81, 0, 0.35), color=black)
    assembly.add(box(2.35, 10.15, 3.65, +3.81, 0, 0.35), color=black)
    assembly.add(box(5.30, 1.15, 1.10, 0, -4.50, 0.35), color=black)
    assembly.add(box(5.30, 1.15, 1.10, 0, +4.50, 0.35), color=black)

    for x in (-3.81, 3.81):
        for y in (-3.81, -1.27, 1.27, 3.81):
            assembly.add(cylinder(0.50, 3.15, x, y, -2.80), color=gold)
            assembly.add(cylinder(1.22, 3.20, x, y, 0.55), color=gold)
            assembly.add(cylinder(0.70, 3.23, x, y, 0.57), color=dark)


def make_opa380_adapter():
    assembly = cq.Assembly(name="OPA380_SOIC8_ADAPTER_DIP8_PRECISION_SOCKET")
    add_dip8_precision_socket(assembly)

    green = cq.Color(0.035, 0.30, 0.12)
    copper = cq.Color(0.72, 0.43, 0.12)
    silver = cq.Color(0.72, 0.74, 0.76)
    black = cq.Color(0.035, 0.035, 0.040)
    marking = cq.Color(0.82, 0.82, 0.78)

    # DIP-8/SOIC-8 adapter. Its lower pins plug into the precision socket.
    for x in (-3.81, 3.81):
        for y in (-3.81, -1.27, 1.27, 3.81):
            assembly.add(cylinder(0.46, 3.30, x, y, 2.80), color=copper)

    assembly.add(box(10.25, 10.25, 1.20, 0, 0, 6.00), color=green)

    # Visible DIP pads on the adapter PCB.
    for x in (-3.81, 3.81):
        for y in (-3.81, -1.27, 1.27, 3.81):
            assembly.add(cylinder(1.30, 0.06, x, y, 7.20), color=copper)

    # OPA380 in TI D (SOIC-8): nominal 3.9 x 4.9 mm body, 1.27-mm pitch.
    body = box(3.90, 4.90, 1.55, 0, 0, 7.33)
    try:
        body = body.edges("|Z").fillet(0.18)
    except Exception:
        pass
    assembly.add(body, color=black)

    # Gull-wing leads. Pin 1 is at the lower-left corner in top view.
    for x in (-2.55, 2.55):
        for y in (-1.905, -0.635, 0.635, 1.905):
            assembly.add(box(1.25, 0.42, 0.16, x, y, 7.25), color=silver)

    assembly.add(cylinder(0.42, 0.035, -1.28, -1.55, 8.88), color=marking)

    output = OUT_DIR / "OPA380_SOIC8_ADAPTER_DIP8_PRECISION_SOCKET.step"
    assembly.save(str(output), mode="default")
    return output


def make_bpw34_vertical():
    assembly = cq.Assembly(name="BPW34_VERTICAL_P5.08mm")
    silver = cq.Color(0.72, 0.74, 0.76)
    clear = cq.Color(0.72, 0.78, 0.80, 0.58)
    die = cq.Color(0.10, 0.16, 0.19)
    cathode_mark = cq.Color(0.25, 0.28, 0.30)

    # Leads occupy footprint positions 1 and 3. Position 2 (x=0) is omitted.
    # Centre-to-centre distance is therefore exactly 2 x 2.54 = 5.08 mm.
    for x in (-2.54, 2.54):
        assembly.add(box(0.50, 0.38, 5.30, x, 0, -2.60), color=silver)

    # Upright BPW34 body, based on Vishay drawing 6.544-5315.01-4:
    # 4.65-mm face width, 4.3-mm face height and 2.0-mm package depth.
    # The leads are deliberately formed out to the requested 5.08-mm spacing.
    body = box(4.65, 2.00, 4.30, 0, 0, 2.15)
    try:
        body = body.edges("|Z").fillet(0.25)
    except Exception:
        pass
    assembly.add(body, color=clear)

    # Visible detector die and bond area inside the transparent package.
    assembly.add(box(3.00, 0.12, 3.00, 0, -0.89, 2.80), color=die)
    # Cathode-side stripe, located by footprint pad 1 (left in top/front view).
    assembly.add(box(0.22, 0.08, 3.55, -2.16, -1.06, 2.50), color=cathode_mark)

    output = OUT_DIR / "BPW34_VERTICAL_P5.08mm.step"
    assembly.save(str(output), mode="default")
    return output


if __name__ == "__main__":
    for model in (make_opa380_adapter(), make_bpw34_vertical()):
        print(model)
