from dataclasses import dataclass, field
import cadquery as Cq
from nhf.build import Model, TargetKind, target, assembly, submodel
from nhf.materials import Role, Material
import nhf.touhou.yasaka_kanako.onbashira as MO
import nhf.utils

@dataclass
class Mirror(Model):
    """
    Kanako's mirror, made of three levels.

    The mirror suface is sandwiched between two layers of wood. As such, its
    dimensions have to sit in between that of the aperature on the surface, and
    the outer walls. The width/height here refers to the outer edge's width and height
    """
    width: float = 100.0
    height: float = 120.0

    inner_gap: float = 3.0
    outer_gap: float = 3.0

    core_thickness: float = 25.4 / 8
    casing_thickness: float = 25.4 / 8

    flange_r0: float = 8.0
    flange_r1: float = 20.0
    flange_y1: float = 12.0
    flange_y2: float = 25.0
    flange_hole_r: float = 8.0
    wing_x1: float = 15.0
    wing_x2: float = 24.0
    wing_r1: float = 10.0
    wing_r2: float = 16.0
    tail_r0: float = 8.0
    tail_r1: float = 13.0
    tail_y1: float = 16.0
    tail_y2: float = 29.0

    # Necklace hole
    hole_diam: float = 5.0

    material_mirror: Material = Material.ACRYLIC_TRANSPARENT
    material_casing: Material = Material.WOOD_BIRCH

    @target(name="core", kind=TargetKind.DXF)
    def profile_core(self) -> Cq.Sketch:
        rx = self.width/2 - self.outer_gap
        ry = self.height/2 - self.outer_gap
        return Cq.Sketch().ellipse(rx, ry)

    def core(self) -> Cq.Workplane:
        return (
            Cq.Workplane()
            .placeSketch(self.profile_core())
            .extrude(self.core_thickness)
        )

    @target(name="casing-bot", kind=TargetKind.DXF)
    def profile_casing_bot(self) -> Cq.Sketch:
        """
        Base of the casing with no holes carved out
        """
        yt = self.height / 2 - self.outer_gap
        yh = (self.flange_y1 + self.flange_y2) / 2
        flange = (
            Cq.Sketch()
            .polygon([
                (self.flange_r0, yt),
                (self.flange_r0, yt + self.flange_y1),
                (self.flange_r1, yt + self.flange_y1),
                (self.flange_r1, yt + self.flange_y2),
                (-self.flange_r1, yt + self.flange_y2),
                (-self.flange_r1, yt + self.flange_y1),
                (-self.flange_r0, yt + self.flange_y1),
                (-self.flange_r0, yt),
            ])
            .push([
                (self.flange_hole_r, yt+yh),
                (-self.flange_hole_r, yt+yh),
            ])
            .circle(self.hole_diam/2, mode="s")
        )
        tail = (
            Cq.Sketch()
            .polygon([
                (+self.tail_r0, -yt),
                (+self.tail_r0, -yt - self.tail_y1),
                (+self.tail_r1, -yt - self.tail_y1),
                (+self.tail_r1, -yt - self.tail_y2),
                (-self.tail_r1, -yt - self.tail_y2),
                (-self.tail_r1, -yt - self.tail_y1),
                (-self.tail_r0, -yt - self.tail_y1),
                (-self.tail_r0, -yt),
            ])
        )
        return (
            Cq.Sketch()
            .ellipse(self.width/2, self.height/2)
            .boolean(flange, mode="a")
            .boolean(tail, mode="a")
            .boolean(self.profile_wing(-1), mode="a")
            .boolean(self.profile_wing(1), mode="a")
        )
    def casing_bot(self) -> Cq.Workplane:
        return (
            Cq.Workplane()
            .placeSketch(self.profile_casing_bot())
            .extrude(self.casing_thickness)
        )
    def profile_wing(self, sign: float=1) -> Cq.Sketch:
        xt = self.width / 2 - self.outer_gap
        return (
            Cq.Sketch()
            .polygon([
                (sign*xt, self.wing_r1),
                (sign*(xt+self.wing_x1), self.wing_r1),
                (sign*(xt+self.wing_x1), self.wing_r2),
                (sign*(xt+self.wing_x2), self.wing_r2),
                (sign*(xt+self.wing_x2), -self.wing_r2),
                (sign*(xt+self.wing_x1), -self.wing_r2),
                (sign*(xt+self.wing_x1), -self.wing_r1),
                (sign*xt, -self.wing_r1),
            ])
        )
    @target(name="casing-mid", kind=TargetKind.DXF)
    def profile_casing_mid(self) -> Cq.Sketch:
        rx = self.width/2 - self.outer_gap
        ry = self.height/2 - self.outer_gap
        return (
            self.profile_casing_bot()
            .ellipse(rx, ry, mode="s")
        )
    def casing_mid(self) -> Cq.Workplane:
        return (
            Cq.Workplane()
            .placeSketch(self.profile_casing_mid())
            .extrude(self.core_thickness)
        )
    @target(name="casing-top", kind=TargetKind.DXF)
    def profile_casing_top(self) -> Cq.Sketch:
        rx = self.width/2 - self.outer_gap - self.inner_gap
        ry = self.height/2 - self.outer_gap - self.inner_gap
        return (
            self.profile_casing_bot()
            .ellipse(rx, ry, mode="s")
        )
    def casing_top(self) -> Cq.Workplane:
        return (
            Cq.Workplane()
            .placeSketch(self.profile_casing_top())
            .extrude(self.casing_thickness)
        )

    @assembly()
    def assembly(self) -> Cq.Assembly:
        return (
            Cq.Assembly()
            .addS(
                self.core(),
                name="core",
                material=self.material_mirror,
                role=Role.DECORATION,
                loc=Cq.Location(0, 0, self.casing_thickness)
            )
            .addS(
                self.casing_bot(),
                name="casing_bot",
                material=self.material_casing,
                role=Role.CASING,
            )
            .addS(
                self.casing_mid(),
                name="casing_mid",
                material=self.material_casing,
                role=Role.CASING,
                loc=Cq.Location(0, 0, self.casing_thickness)
            )
            .addS(
                self.casing_top(),
                name="casing_top",
                material=self.material_casing,
                role=Role.CASING,
                loc=Cq.Location(0, 0, self.core_thickness + self.casing_thickness)
            )
        )