import cadquery2 as cq
from cadquery2 import exporters
from math import floor, atan, pi

# Naming convention
# _l means Y
# _w means X
# _h means Z


shell_t = 2  # Shell thickness
front_h = 25  # height at the front (near user)
back_h = 40  # height at the back (away from user)
width = 290  # width of the whole thing
fillet_s = 4  # Fillet of case sides

kbd_base_l = 150  # length of the front half of the case
screen_base_l = 100  # length of the back half of the case
kbd_angle = atan((back_h - front_h) / kbd_base_l) * 180 / pi
print("kbd_angle=", kbd_angle)

# Added an extra 1mm on each direction for tolerance to make sure it slots in
kbd_w = 202  # width of the keyboard itself
kbd_l = 127  # "length" of the keyboard
kbd_h = 4  # depth of the keyboard holder cutoff
kbd_fillet = 10  # width of the kbd corner fillet

# battery dimensions (mx7)
bat_w = 60
bat_l = 140
bat_h = 22

# width, length VISIBLE WITHIN THE BEZEL
screen_w = 223
screen_l = 57
screen_left_margin = 10  # Distance from the left of the case to screen cutout
# width, length, height INCLUDING_BEZEL
display_w = 233
display_l = 65
display_h = 5
# Distance from the left of the case to display end (7 is bezel width on that side)
display_left_margin = screen_left_margin - 7
# Distance from the right of the case to display end
display_right_margin = width - display_left_margin - display_w
# Distance from the top of the case to display top
display_top_margin = (screen_base_l - display_l) / 2
display_bottom_margin = display_top_margin  # Symmetrical

kbd_cutout = (
    cq.Sketch().trapezoid(kbd_w, kbd_l, 90).reset().vertices().fillet(kbd_fillet)
)

bat_holder_top = (
    cq.Sketch()
    .trapezoid(bat_w + 2 * shell_t, bat_h + 2 * shell_t, 90)
    .trapezoid(bat_w, bat_h, 90, mode="s")
    .trapezoid(bat_w + 10, bat_h - 10, 90, mode="s")
)

# Base to hold (magnetically?) the keyboard (half the case)
kbd_base = (
    cq.Workplane("left")
    .lineTo(0, kbd_base_l)
    .lineTo(front_h, kbd_base_l)
    .lineTo(back_h, 0)
    .close()
    .extrude(width)
    .edges("+Z")
    .fillet(fillet_s)
    .edges("+Y")
    .fillet(fillet_s)
    # Cut kbd holder area from top face
    .faces(">Z")
    .workplane(centerOption="CenterOfBoundBox")
    .center((screen_w - width) / 2 + screen_left_margin, 0)
    .placeSketch(kbd_cutout)
    .cutBlind(-kbd_h)
    # Make it hollow
    .faces("<Y")
    .shell(-shell_t)
    # Add battery holder
    .faces("<Y")
    .workplane(centerOption="CenterOfBoundBox", offset=-1)
    # The final constants are fudging because it impinges on the surface
    .center(
        -(width - bat_w - 2 * shell_t) / 2 + 1,
        (back_h - bat_h - 2 * shell_t) / 2 - 0.25,
    )
    .transformed(rotate=cq.Vector(-kbd_angle, 0, 0))
    .placeSketch(bat_holder_top)
    # The 3 is fudging so the bottom of the holder doesn't
    # impinge on the bottom face
    .extrude(-(bat_l - 3 * fillet_s))
)

# Slots for ventilation, applied at the bottom of the screen base
ventilacion = (
    cq.Sketch()  # slots are 3mm separated 5
    .rarray(8, 6, floor(width / 9), 1)
    .trapezoid(3, 0.8 * screen_base_l, 90, mode="a")
    .reset()
    .vertices()
    .fillet(1)
)

screen_cutout = (
    cq.Sketch().trapezoid(screen_w, screen_l, 90, mode="a").reset().vertices().fillet(1)
)

shelf_cutout = (
    cq.Sketch()
    .trapezoid(width - 2 * shell_t, screen_l, 90, mode="a")
    .reset()
    .vertices()
    .fillet(1)
)

screen_lateral_stop = (
    cq.Sketch()
    .rarray(display_l - 2 * shell_t, 1, 2, 1)
    .trapezoid(2 * shell_t, display_h + 2 * shell_t, 90, mode="a")
)

screen_vertical_stop = cq.Sketch().trapezoid(width, 2 * shell_t, 90, mode="a")

# Holder for the screen (other half of the case)
screen_base = (
    # Basic filleted box shape
    cq.Workplane("bottom")
    .lineTo(-width, 0)
    .lineTo(-width, back_h)
    .lineTo(0, back_h)
    .close()
    .extrude(screen_base_l)
    .edges("|Y and <Z")
    .fillet(fillet_s)
    .faces(">Y")
    .shell(-shell_t)
    # Account for outer shell thickness in offset
    .faces(">Z")
    .workplane(offset=-display_h - shell_t)
    # Add friction shelve for screen assembly
    .lineTo(-width, 0)
    .lineTo(-width, -screen_base_l)
    .lineTo(0, -screen_base_l)
    .close()
    .extrude(-shell_t)
    # Cutout visible screen area from top face
    .faces(">Z")
    .workplane(centerOption="CenterOfBoundBox")
    .center((width - screen_w) / 2 - screen_left_margin, 0)
    .placeSketch(screen_cutout)
    .cutBlind(-shell_t)
    # Cut screen "shelf" to allow for cable routing
    .faces(">Z")
    .workplane(centerOption="CenterOfBoundBox", offset=-display_h - shell_t)
    .placeSketch(shelf_cutout)
    .cutBlind(-shell_t)
    # Stop the screen from sliding back and forward
    .faces(">Y")
    .workplane(centerOption="CenterOfBoundBox")
    .center(0, back_h / 2 - display_h - shell_t)
    .placeSketch(screen_vertical_stop)
    .extrude(-display_bottom_margin)
    .faces("<Y")
    .workplane(centerOption="CenterOfBoundBox")
    .center(0, back_h / 2 - display_h - shell_t)
    .placeSketch(screen_vertical_stop)
    .extrude(-display_top_margin)
    # Stop the screen from sliding left and right
    .faces(">X")
    .workplane(centerOption="CenterOfBoundBox")
    .center(0, back_h / 2 - display_h - shell_t)
    .placeSketch(screen_lateral_stop)
    .extrude(-display_left_margin)
    .faces("<X")
    .workplane(centerOption="CenterOfBoundBox")
    .center(0, back_h / 2 - display_h - shell_t)
    .placeSketch(screen_lateral_stop)
    .extrude(-display_right_margin)
    # Place ventilation grid in bottom face
    .faces("<Z")
    .workplane(centerOption="CenterOfBoundBox")
    .placeSketch(ventilacion)
    .cutBlind(-5)
)

exporters.export(screen_base, "screen_base.stl")
exporters.export(kbd_base, "kbd_base.stl")
