import argparse

from build123d import (
    Align,
    Axis,
    Box,
    BuildPart,
    BuildSketch,
    Circle,
    Location,
    Locations,
    Mode,
    Plane,
    Rectangle,
    add,
    export_step,
    export_stl,
    extrude,
    fillet,
    import_step,
    offset,
)

oc_slot_lock = import_step("openconnect_slot_negative_lock.step")
oc_slot_lock = oc_slot_lock.rotate(Axis.Y, 180)
oc_slot_lock.move(Location((0, 0, oc_slot_lock.bounding_box().size.Z)))
oc_slot = import_step("openconnect_slot_negative.step")
oc_slot = oc_slot.rotate(Axis.Y, 180)
oc_slot.move(Location((0, 0, oc_slot.bounding_box().size.Z)))


def build(diameter: float, height: float):
    d = diameter
    h = height

    with BuildPart() as p:
        with BuildSketch(Plane.ZX.offset(-h / 2)) as sketch1:
            Circle(radius=d / 2)
            with Locations((-d / 2, 0)):
                Rectangle(2 * 3.2, d, mode=Mode.SUBTRACT)
        extrude(amount=1)
        with BuildSketch(Plane.ZX.offset(-h / 2)):
            add(sketch1)
            offset(amount=-1.5, mode=Mode.SUBTRACT)
        extrude(amount=h)

        with BuildSketch(Plane.XY) as cutout:
            with Locations((0, 5)):
                Rectangle(4, h)
                fillet(cutout.vertices(), radius=2)
        extrude(amount=d, mode=Mode.SUBTRACT)

        _e = p.faces().sort_by(Axis.Y)[-1].edges().filter_by(Axis.Z)
        fillet(_e, radius=2)

        with Locations((0, 0, -d / 2 + 1)):
            Box(28, h, 3.2, align=(Align.CENTER, Align.CENTER, Align.MIN))

        # fillet edges between cylinder and mounting plate
        _e1 = p.edges().filter_by(Axis.Y).group_by(Axis.Z)[1:3]
        fillet(_e1, radius=1.5)

        # fillet bottom edges
        _e2 = p.faces().sort_by(Axis.Y)[0:2].edges()
        fillet(_e2, radius=1)

        # fillet slot and top edges
        _e3 = p.faces().sort_by(Axis.Y)[-1].edges()
        fillet(_e3, radius=0.74)

        for i in range(int(h // 28)):
            loc = Location((0, h / 2 - 14 - 28 * i, -d / 2 + 1))
            _oc = oc_slot.moved(loc) if i > 0 else oc_slot_lock.moved(loc)
            add(_oc, mode=Mode.SUBTRACT)

    assert p.part
    return p.part


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--diameter", type=int, default=28 * 2 - 2)
    parser.add_argument("--height", type=int, default=5 * 28)
    parser.add_argument("--export-step", action="store_true")
    parser.add_argument("--preview", action="store_true")
    args = parser.parse_args()

    part = build(args.diameter, args.height)

    name = f"trash-bag-dispenser_d{args.diameter}xh{args.height}"

    if args.preview:
        from ocp_vscode import show_object

        print("Showing preview of {name}, remove --preview to export.")
        show_object(part)
    elif args.export_step:
        export_step(part, f"{name}.step")
    else:
        export_stl(part, f"{name}.stl")
