import cadquery2 as cq
from cadquery2 import exporters

# Base for the notebook. Basically a kbd base that extends back
# as much as possible

# Thickness of the outer material
shell_t = 3

# Size of the kbd board
kbd_height = 95.5
kbd_width = 305
kbd_angle = 5

# Size of the whole object
width = kbd_width + 2 * shell_t
height = 159
thickness = 20 + shell_t  # 20 inside

# Insert Positions
ti_radius = 2.35
ti_depth = 6.25

# Positions are determined by measuring the keyboard

kbd_pillar_positions = [
    (18.25, -16),
    (142.5, -25.5),
    (kbd_width - 20, -16),
    (23.5, -79.5),
    (145.5, -82.5),
    (kbd_width - 19, -79.5),
]


# 2-level mounting pillars for the kbd
# Width of these need to be tweaked for
# each specific keyboard
kbd_pillars = (
    cq.Sketch()
    .push(kbd_pillar_positions)
    .circle(2.2, mode="a")
    # Holes for self-tapping screws
    .circle(1.1, mode="s")
)

kbd_lower_pillars = (
    cq.Sketch()
    .push(kbd_pillar_positions)
    .circle(4, mode="a")
    # Holes for self-tapping screws
    .circle(1.1, mode="s")
)

# These are placed where convenient, and are used to join the top and bottom
# parts of the case.
# Measured from top-left corner OUTSIDE
mounting_pillar_positions = [
    (6, -6),
    (6, -40),
    (120, -6),
    (170, -6),
    (width - 6, -6),
    (width - 6, -40),
]

mounting_pillars = (
    cq.Sketch()
    .push(mounting_pillar_positions)
    .trapezoid(12, 12, 90, mode="a")
    .circle(1.8, mode="s")
)

screw_holes = cq.Sketch().push(mounting_pillar_positions).circle(3, mode="a")

# Hole for the USB hub's exposed port
usb_in = cq.Sketch().trapezoid(13, 5.5, 90, mode="a")


def model():
    return (
        cq.Workplane("XY")
        .workplane(offset=thickness / 2)
        .tag("mid_height")
        # Hollow box
        .box(width, height, thickness)
        .edges("|Z")
        .fillet(2)
        .faces(">Z")
        .shell(-shell_t)
        # USB inlet
        .faces(">X")
        .workplane(centerOption="CenterOfBoundBox")
        # The position is arbitrary, based on the components 
        # available, keyboard height, cable length, etc.
        .center(-height / 2 + shell_t + 60, -5)
        .placeSketch(usb_in)
        .cutBlind(-shell_t)
        # Slanted mounting pillars on the kbd top
        .faces(">Z")
        .workplane(centerOption="CenterOfBoundBox")
        # Top-left kbd corner inside the box
        .center(-width / 2 + shell_t, kbd_height - height / 2 + shell_t)
        .transformed(rotate=cq.Vector(kbd_angle, 0, 0))
        # These two offsets push the keyboard "down" into the case
        # and need to be adjusted per-keyboard
        .tag("sloped")
        .workplane(offset=-2.5)
        .placeSketch(kbd_pillars)
        .extrude(-1000)
        .workplaneFromTagged("sloped")
        .workplane(offset=-5.5)
        .placeSketch(kbd_lower_pillars)
        .extrude(-1000)
        # Remove the excess extrusion
        .workplaneFromTagged("mid_height")
        .transformed(offset=cq.Vector(0, 0, -thickness / 2))
        .split(keepTop=True)
        # Slope for the beyboard
        .workplaneFromTagged("sloped")
        .split(keepBottom=True)
        # Pillars to join with top half
        .workplaneFromTagged("mid_height")
        .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox")
        .center(-width / 2, height / 2)
        .placeSketch(mounting_pillars)
        .extrude(thickness)
        # Holes to insert screws from the bottom
        .workplaneFromTagged("mid_height")
        .workplane(offset=-thickness / 2, centerOption="CenterOfBoundBox")
        .center(-width / 2, height / 2)
        .placeSketch(screw_holes)
        # 13 is 20-7 (screw thread length - threaded insert depth)
        .cutBlind(thickness - 13)
    )


if __name__ == "__main__":

    left_cutout = cq.Sketch().polygon(
        [(0, 0), (width / 2, 0), (width / 2, -height), (0, -height), (0, 0)],
        mode="a",
    )

    right_side = (
        model()
        .faces("<Z")
        .workplaneFromTagged("mid_height")
        .transformed(offset=cq.Vector(0, 0, -thickness / 2))
        .center(-width / 2, height / 2)
        .placeSketch(left_cutout)
        .cutBlind(100)
    )

    exporters.export(right_side, "right_side.stl")

    right_cutout = cq.Sketch().polygon(
        [
            (width / 2, 0),
            (width, 0),
            (width, -height),
            (width / 2, -height),
            (width / 2, 0),
        ],
        mode="a",
    )

    left_side = (
        model()
        .faces("<Z")
        .workplaneFromTagged("mid_height")
        .transformed(offset=cq.Vector(0, 0, -thickness / 2))
        .center(-width / 2, height / 2)
        .placeSketch(right_cutout)
        .cutBlind(100)
    )
    exporters.export(left_side, "left_side.stl")

    exporters.export(model(), "model.stl")
