from dataclasses import dataclass, field
from typing import Tuple
import cadquery as Cq
from nhf import Item, Role
from nhf.parts.box import Hole, MountingBox
import nhf.utils

@dataclass(frozen=True)
class ArduinoUnoR3(Item):
    # From datasheet
    mass: float = 25.0
    length: float = 68.6
    width: float = 53.4

    # with clearance
    base_height: float = 5.0
    # aesthetic only for illustrating clearance
    total_height: float = 50.0
    roof_height: float = 20.0

    # This is labeled in mirrored coordinates from top down (i.e. unmirrored from bottom up)
    holes: list[Tuple[float, float]] = field(default_factory=lambda: [
        (15.24, 2.54),
        (15.24 - 1.270, 50.80), # x coordinate not labeled on schematic
        (66.04, 17.78),
        (66.04, 45.72),
    ])
    hole_diam: float = 3.0

    @property
    def name(self) -> str:
        return "Arduino Uno R3"

    @property
    def role(self) -> Role:
        return Role.ELECTRONIC

    def generate(self) -> Cq.Assembly:
        sketch = (
            Cq.Sketch()
            .polygon([
                (0,0),
                (self.length, 0),
                (self.length, self.width),
                (0,self.width)
            ])
            .push([(x, self.width - y) for x,y in self.holes])
            .circle(self.hole_diam / 2, mode='s')
        )
        # pillar thickness
        t = 3.0
        pillar_height = self.total_height - self.base_height
        pillar = Cq.Solid.makeBox(
            t, t, pillar_height
        )
        roof = Cq.Solid.makeBox(
            self.length, self.width, t
        )
        result = (
            Cq.Workplane('XY')
            .placeSketch(sketch)
            .extrude(self.base_height)
            .union(pillar.located(Cq.Location((0, 0, self.base_height))))
            .union(pillar.located(Cq.Location((self.length - t, 0, self.base_height))))
            .union(pillar.located(Cq.Location((self.length - t, self.width - t, self.base_height))))
            .union(pillar.located(Cq.Location((0, self.width - t, self.base_height))))
            .union(roof.located(Cq.Location((0, 0, self.total_height - t))))
        )
        plane = result.copyWorkplane(Cq.Workplane('XY'))
        for i, (x, y) in enumerate(self.holes):
            plane.moveTo(x, self.width - y).tagPlane(f"conn{i}", direction='-Z')
        return result


@dataclass(frozen=True)
class BatteryBox18650(Item):
    """
    A number of 18650 batteries in series
    """
    mass: float = 17.4 + 68.80 * 3
    length: float = 75.70
    width_base: float = 61.46 - 18.48 - 20.18 * 2
    battery_dist: float = 20.18
    height: float = 19.66
    # space from bottom to battery begin
    thickness: float = 1.66
    battery_diam: float = 18.48
    battery_height: float = 68.80
    n_batteries: int = 3

    def __post_init__(self):
        assert 2 * self.thickness < min(self.length, self.height)

    @property
    def name(self) -> str:
        return f"BatteryBox 18650*{self.n_batteries}"

    @property
    def role(self) -> Role:
        return Role.ELECTRONIC

    def generate(self) -> Cq.Workplane:
        width = self.width_base + self.battery_dist * (self.n_batteries - 1) + self.battery_diam
        return (
            Cq.Workplane('XY')
            .box(
                length=self.length,
                width=width,
                height=self.height,
                centered=(True, True, False),
            )
            .copyWorkplane(Cq.Workplane('XY', origin=(0, 0, self.thickness)))
            .box(
                length=self.length - self.thickness*2,
                width=width - self.thickness*2,
                height=self.height - self.thickness,
                centered=(True, True, False),
                combine='cut',
            )
            .copyWorkplane(Cq.Workplane('XY', origin=(-self.battery_height/2, 0, self.thickness + self.battery_diam/2)))
            .rarray(
                xSpacing=1,
                ySpacing=self.battery_dist,
                xCount=1,
                yCount=self.n_batteries,
                center=True,
            )
            .cylinder(
                radius=self.battery_diam/2,
                height=self.battery_height,
                direct=(1, 0, 0),
                centered=(True, True, False),
                combine=True,
            )
        )