import math

import cadquery as cq
from ocp_vscode import *

import localselectors


class SFU1204:
    def __init__(self, base, l):
        self.w = base.workplane()
        self.len = l

    def ballscrew(self):
        profile = [
            (0, 0),
            (0, 8 / 2),
            (15, 8 / 2),
            (15, 10 / 2),
            (15 + 16, 10 / 2),
            (15 + 39, 10 / 2),
            (15 + 39, 12 / 2),
            (self.len - 10, 12 / 2),
            (self.len - 10, 8 / 2),
            (self.len, 8 / 2),
            (self.len, 0),
        ]
        w = self.w.moveTo(0, 0)
        for v in profile[1:]:
            w = w.lineTo(v[1], v[0])
        w = w.close()
        return w.revolve()

    def nut(self, x):
        w = self.w.transformed(offset=(0, x, 0), rotate=(90, 0, 0))
        n = (
            w.sketch()
            .circle(40 / 2)
            .circle(12 / 2, mode="s")
            .rect(42, 30, mode="i")
            .finalize()
            .extrude(10)
            .copyWorkplane(w)
            .transformed(offset=(0, 0, 10))
            .workplane()
            .tag("nut_face")
            .sketch()
            .circle(22 / 2)
            .circle(12 / 2, mode="s")
            .finalize()
            .extrude(25)
        )
        hole_r = 32 / 2
        hole_pts = [
            (
                hole_r * math.cos(angle * math.pi / 180),
                hole_r * math.sin(angle * math.pi / 180),
            )
            for angle in [-45, 0, 45, 180 - 45, 180, 180 + 45]
        ]
        n = n.workplaneFromTagged("nut_face").pushPoints(hole_pts).hole(4.8)
        return n

    def nutHoles(self, solid, x):
        w = self.w.transformed(offset=(0, x, 0), rotate=(90, 0, 0))
        hole_r = 32 / 2
        hole_pts = [
            (
                hole_r * math.cos(angle * math.pi / 180),
                hole_r * math.sin(angle * math.pi / 180),
            )
            for angle in [-45, 0, 45, 180 - 45, 180, 180 + 45]
        ]
        solid = solid.copyWorkplane(w).pushPoints(hole_pts).hole(4.8)
        return solid

    def fixedBearing(self):
        d1 = 12
        L = 25
        L1 = 5
        L2 = 29
        L3 = 5
        C1 = 13
        C2 = 6
        B = 60
        H = 43
        b = 30
        h = 25
        B1 = 34
        H1 = 32.5
        E = 18
        P = 46
        d2 = 5.5
        X = 6.6
        Y = 10.8
        Z = 1.5
        w = self.w.transformed(offset=(0, 39 + 15, 0), rotate=(-90, 180, 0))
        w2 = w.transformed(offset=(0, h - H1 / 2, 0))
        w3 = w.transformed(offset=(0, E / 2, 0))

        bearing = (
            w.rect(B1, B1)
            .extrude(L1 + L)
            .copyWorkplane(w2)
            .rect(B, H1)
            .extrude(L)
            .copyWorkplane(w.transformed(rotate=(180, 0, 0)))
            .hole(d1)
            .copyWorkplane(w3.transformed(rotate=(180, 0, 0)))
            .rect(P, E, forConstruction=True)
            .vertices()
            .hole(d2)
        )
        # TODO the holes on the top surface

        return bearing

    def fixedBearingHoles(self, solid):
        d1 = 12
        L = 25
        L1 = 5
        L2 = 29
        L3 = 5
        C1 = 13
        C2 = 6
        B = 60
        H = 43
        b = 30
        h = 25
        B1 = 34
        H1 = 32.5
        E = 18
        P = 46
        d2 = 5.5
        X = 6.6
        Y = 10.8
        Z = 1.5

        w = self.w.transformed(offset=(0, 39 + 15, -E / 2), rotate=(-90, 0, 0))
        return (
            solid.copyWorkplane(w).rect(P, E, forConstruction=True).vertices().hole(d2)
        )

    def floatingBearing(self):
        d1 = 10
        L = 20
        B = 60
        H = 43
        b = 30
        h = 25
        B1 = 34
        H1 = 32.5
        E = 18
        P = 46
        d2 = 5.5
        X = 6.6
        Y = 10.8
        Z = 1.5

        w = self.w.transformed(
            offset=(0, self.len - 10 + 7 / 2 - L / 2, 0), rotate=(-90, 0, 0)
        )
        b = (
            w.rect(B1, B1)
            .extrude(L)
            .copyWorkplane(w.transformed(offset=(0, 0, L), rotate=(0, 0, 180)))
            .workplane()
            .tag("floating_far_end")
            .copyWorkplane(w)
            .center(0, h - H1 / 2)
            .rect(B, H1)
            .extrude(L)
            .workplaneFromTagged("floating_far_end")
            .hole(d1)
            .workplaneFromTagged("floating_far_end")
            .center(0, -E / 2)
            .rect(P, E, forConstruction=True)
            .vertices()
            .hole(d2)
            .copyWorkplane(w)
            .circle(30 / 2)
            .extrude(L / 2 - 7 / 2, combine="s")
            .workplaneFromTagged("floating_far_end")
            .transformed(rotate=(180, 0, 0))
            .circle(30 / 2)
            .extrude(L / 2 - 7 / 2, combine="s")
        )
        return b

    def floatingBearingHoles(self, solid):
        d1 = 10
        L = 20
        B = 60
        H = 43
        b = 30
        h = 25
        B1 = 34
        H1 = 32.5
        E = 18
        P = 46
        d2 = 5.5
        X = 6.6
        Y = 10.8
        Z = 1.5

        w = self.w.transformed(offset=(0, self.len - 10 + L - 20, 0), rotate=(90, 0, 0))
        solid = (
            solid.copyWorkplane(w)
            .center(0, -E / 2)
            .rect(P, E, forConstruction=True)
            .vertices()
            .hole(d2)
        )
        return solid


# it's oriented along the y axis of the given base plane
class HGR20:
    def __init__(self, base, l):
        self.base = base.workplane()
        self.l = l

    def holes(self, w):
        pts = []
        x = 20
        while x < self.l + 1e-6:
            pts.append((x - self.l / 2, 0))
            x += 60
        w = (
            w.copyWorkplane(self.base)
            .transformed(offset=(0, 0, 17))
            .pushPoints(pts)
            .hole(5)
        )
        return w

    def rail_sketch(s=None):
        # pixel coordinates based on a picture in the Hiwin's
        # linear rail guidebook

        hgr_profile = [
            (0, 0),
            (37, 0),
            (37 + 6, 6),
            (96 - 6, 6),
            (96, 0),
            (186 - 17, 0),
            (186, 17),
            (186, 76),
            (129, 133),
            (129, 59 + 133),
            (129 + 20, 59 + 133 + 20),
            # (31, 129 + 20 + 31, 133 + 20 + 31),
            (186 - 6, 316 - 42 - 25 - 6),
            (186, 316 - 42 - 25),
            (186, 316 - 42),
            (186 - 6, 316 - 42 + 6 - 1),
            (31, 186 - 6 - 31, 316 - 6 - 1),
            (143 + 6, 316 - 6),
            (143, 316),
            (0, 316),
        ]
        scale = (10 / 186 * 17 / 316) ** 0.5
        scaled_profile = [tuple((scale * v for v in vals)) for vals in hgr_profile]
        if s is None:
            s = cq.Sketch()
        s = s.segment((0, 0), scaled_profile[1])
        # TODO do arcs correctly
        for v in scaled_profile[2:]:
            if len(v) == 2:
                s = s.segment(v)
            else:
                s = s.segment(v[1:])
        for v in scaled_profile[-2:0:-1]:
            if len(v) == 2:
                s = s.segment((-v[0], v[1]))
            else:
                s = s.segment((-v[1], v[2]))
        s = s.segment((0, 0))
        return s

    def rail(self):  # workplane or face to use, length of rail
        # TODO rotate w
        xy = cq.Workplane().copyWorkplane(self.base)
        w = xy.transformed((90, 90, 0), (-self.l / 2, 0, 0))
        w = w.placeSketch(HGR20.rail_sketch().assemble())
        w = w.extrude(self.l)

        x = 20
        pts = []
        while x < self.l:
            pts.append((x - self.l / 2, 0))
            x += 60
        # print("points", pts)
        w = (
            w.copyWorkplane(xy).transformed(offset=(0, 0, 17)).pushPoints(pts).hole(5)
        )  # TODO replace with counterbore and get proper diameter
        return w

    W = 44
    H = 30
    H1 = 4.6
    L = 77.5
    L1 = 50.1  # for HGH20CA, TODO deal with variations
    B = 32
    C = 36
    l = 5  # M5x6
    T = 6

    def carriage(self, x):  # HGH20CA
        W = 44
        H = 30
        H1 = 4.6
        L = 77.5
        L1 = 50.1  # for HGH20CA, TODO deal with variations
        B = 32
        C = 36
        l = 5  # M5x6
        T = 6
        xy = cq.Workplane().copyWorkplane(self.base)
        w = xy.transformed((90, 90, 0), (-self.l / 2 + x, 0, 0)).tag("endplane")
        # TODO

        s = (
            cq.Sketch()
            .segment((W / 2, H1), (W / 2, H))
            .segment((-W / 2, H))
            .segment((-W / 2, H1))
            .segment((W / 2, H1))
            .assemble()
        )
        s = s.face(HGR20.rail_sketch().assemble(), mode="s")
        s2 = (
            cq.Sketch()
            .segment((W / 2, H1), (W / 2, H - 3))
            .segment((-W / 2, H - 3))
            .segment((-W / 2, H1))
            .segment((W / 2, H1))
            .assemble()
        )
        s2 = s2.face(HGR20.rail_sketch().assemble(), mode="s")

        pts = []

        def savePointsF(l, p):
            l.append(p)
            return p

        # origin = w.workplane().plane.toWorldCoords(cq.Vector(0, 0, 0))
        # norm = w.workplane().plane.toWorldCoords(cq.Vector(0, 1, 0))
        # norm = norm - origin
        # print(norm)
        # FINALLY GOT THIS WORKING
        w = (
            w.transformed((0, 0, 0), (0, 0, L / 2 - L1 / 2))
            .placeSketch(s)
            .extrude(L1)
            .workplaneFromTagged("endplane")
            .placeSketch(s2)
            .extrude(L)
            # .faces(cq.DirectionMinMaxSelector(norm, True))
            .faces(">y")
            .workplane()
            .transformed((0, 0, 0), (L / 2 + x, 0, 0))
            .rect(C, B)
            .tag("mountingholes")
            .vertices()  # carriage mounting holes are here!
            # .extrude(20)
            .hole(l, T)
        )
        return w

    def carriageHoles(self, w, x):
        W = 44
        H = 30
        H1 = 4.6
        L = 77.5
        L1 = 50.1  # for HGH20CA, TODO deal with variations
        B = 32
        C = 36
        l = 5  # M5x6
        T = 6
        xy = cq.Workplane().copyWorkplane(self.base).transformed(offset=(0, 0, H))
        holes = (
            xy.transformed((0, 0, 0), (-self.l / 2 + L / 2 + x, 0, 0))
            .rect(C, B)
            .vertices()
            .cylinder(1000, l / 2)
        )
        w = w.cut(holes)
        return w


class Plate:
    # TODO add standard thicknesses here
    pass


def inch(x):
    return 25.4 * x


def tubing(l, x, y, w, workplane):
    return workplane.placeSketch(
        cq.Sketch().rect(x, y).rect(x - 2 * w, y - 2 * w, mode="s")
    ).extrude(l)


if __name__ == "__main__":
    plate = (
        cq.Workplane("XY")
        .rect(200, 200)
        .extrude(10)
        .faces(">Z")
        .workplane()
        .tag("railmount")
        .end()
    )

    hgr20 = HGR20(plate.workplaneFromTagged("railmount"), 300)
    plate2 = hgr20.holes(plate)
    hgrtest = hgr20.rail()
    carriage = hgr20.carriage(0)
    plate3 = (
        cq.Workplane("XY")
        .copyWorkplane(carriage.workplaneFromTagged("mountingholes"))
        .rect(200, 200)
        .extrude(5)
    )
    plate3 = hgr20.carriageHoles(plate3, 0)
    tube1 = tubing(500, inch(2), inch(1), inch(0.062), cq.Workplane("XZ"))

    ball_screw = SFU1204(cq.Workplane(), 500)

    # show_object(plate2)
    # show_object(hgrtest)
    # show_object(carriage)
    # show_object(plate3)
    # show_object(tube1)

    show_object(ball_screw.ballscrew())
    show_object(ball_screw.nut(200))
    nut_holder = (
        cq.Workplane("XZ")
        .transformed(offset=(0, 0, -200 - 50))
        .rect(40, 30)
        .extrude(50)
        .faces(">z")
        .hole(12.4)
    )
    nut_holder = ball_screw.nutHoles(nut_holder, 200)
    fixed = ball_screw.fixedBearing()
    show_object(nut_holder)
    show_object(fixed)
    bearing_holder = (
        cq.Workplane("XZ")
        .transformed(offset=(0, 0, 0))
        .rect(60, 80)
        .extrude(20)
        .faces(">z")
        .hole(12.4)
    )
    bearing_holder = ball_screw.fixedBearingHoles(bearing_holder)
    show_object(bearing_holder)
    bearing2 = ball_screw.floatingBearing()
    show_object(bearing2)
    bearing_holder2 = (
        cq.Workplane("XZ")
        .transformed(offset=(0, 0, -540))
        .rect(60, 80)
        .extrude(20)
        .faces(">z")
        .hole(12.4)
    )
    bearing_holder2 = ball_screw.floatingBearingHoles(bearing_holder2)
    show_object(bearing_holder2)

    # show_object(cq.Workplane().placeSketch(HGR20.rail_sketch().assemble()).extrude(10))