import numpy as np
import solid as scad
from . import primitives as slp
import solidLib as sl

from typing import Union, Iterable

# import .primitives as slp


def grid(
    x_dim: int,
    y: int,
    w: float = 3,
    h: float = 1.5,
    fillet: bool = False,
):
    """generate a grid of (flattened) triangles with or without fillet

    the generated grid is x by y in size with a gridspace of size dim,
    the triangles used for the base structure is given by their height and width

    .. image:: img/grid.png
       :alt: 2x3 grid with defaults
    """

    if w > h * 2:
        out = scad.polygon([[0, 0], [w / 2, 0], [w / 2 - h, h], [0, h]])
    else:
        out = scad.polygon([[0, 0], [w / 2, 0], [0, h]])
    out = scad.linear_extrude(sl.settings.raster)(out)
    out = scad.rotate([90, 0, 90])(out)

    if fillet:
        # scallop = scad.cube([dim - 2 * h - (w - 2 * h), 0.0001, 0.0001])
        # scallop = scad.minkowski()(scallop, scad.sphere(r=h, segments=segments))
        scallop = slp.pill(
            [
                [0, 0, 0],
                [sl.settings.raster - 2 * h - (w - 2 * h), 0, 0],
                # [0,0,h], [dim - 2 * h - (w - 2 * h),0,h]
            ],
            h,
            segments=sl.settings.segments,
        )
        scallop += scad.rotate((0, -90, 0))(scallop) + scad.translate([sl.settings.raster - w, 0, 0])(
            scad.rotate([0, -90, 0])(scallop)
        )
        scallop = scad.translate((w / 2, w / 2, h))(scallop)
        out -= scallop

    out += scad.translate((0, sl.settings.raster, 0))(scad.rotate((0, 0, -90))(out))
    out += scad.translate((sl.settings.raster, sl.settings.raster, 0))(scad.rotate((0, 0, 180))(out))

    result = out
    for dx in range(x_dim):
        for dy in range(y):
            result += scad.translate((sl.settings.raster * dx, sl.settings.raster * dy, 0))(out)

    return result


def grid_inv(
    x: float,
    y: float,
    w: float = 3,
    h: float = 1.5,
    fillet: bool = False,
):
    g = grid(x, y, w, h, fillet, sl.settings.raster, sl.settings.segments)
    return scad.cube([x * sl.settings.raster, y * sl.settings.raster, h]) - g


def bin(x: float, y: float, h: float = 67, wall: float = 2):
    """
    standardized container

    .. image:: img/bin.png
    """

    class dims:
        r_in = 2
        r_out = r_in + 2

    bulk = slp.rfcube([sl.settings.raster * x, sl.settings.raster * y, h], dims.r_out)
    hole = slp.rcube([sl.settings.raster * x - wall * 2, sl.settings.raster * y - wall * 2, h], dims.r_in)
    hole = scad.translate([wall, wall, wall])(hole)

    wall_o = scad.cylinder(r=dims.r_out, h=dims.r_out, segments=sl.settings.segments)

    g = grid(x, y)

    goverlay = grid(x, y, w=2 + 2 * wall, h=dims.r_in, fillet=True)
    goverlay = scad.translate([0, 0, wall])(goverlay)
    goverlay = scad.intersection()(goverlay, hole)

    return bulk - hole - g + goverlay


def toolinlay1(
    height: float,
    *args,
    r_tool: float = 7,
    inset: float = 4,
    maxh: float = 67,
):
    """toolinlay1

    creates a tooltray for alex container bins (2x3)

    .. image:: img/toolinlay1.png

    :param height: height of inset above lower bin
    :param inset: depth of inset
    """
    x = 2
    y = 3
    wall = 2
    r_in = 2
    tool_ins = 3

    base = slp.rfcube([x * sl.settings.raster, y * sl.settings.raster, height + inset], r_in + wall)

    toolindent = 0.1
    toolindent_w = 2
    toolbase_l = sl.settings.raster * x - 2 * wall - 2 * r_tool
    toolbase_t = scad.cube([toolbase_l * (1 - toolindent), 0.001, tool_ins])
    toolbase_b = scad.translate([toolbase_l * (1 - toolindent), 0, 0])(
        scad.cube([toolbase_l * toolindent - toolindent_w, 0.001, tool_ins])
    )
    toolcutout = scad.minkowski()(
        toolbase_t, scad.sphere(r=r_tool, segments=sl.settings.segments)
    ) + scad.minkowski()(
        toolbase_b, scad.sphere(r=r_tool + toolindent_w, segments=sl.settings.segments)
    )

    toolcutout = scad.translate(
        [r_tool + wall, r_tool + wall + toolindent_w, maxh - tool_ins]
    )(toolcutout)

    toolcutouts = []
    for i in range((sl.settings.raster * y - 3 * wall) // (r_tool * 2 + 2 * toolindent_w)):
        toolcutouts.append(
            scad.translate([0, i * (2 * r_tool + 2 * toolindent_w + 1.4), 0])(
                scad.color("yellow")(toolcutout)
            )
        )

    lower = bin(2, 3, h=maxh - height, dim=sl.settings.raster)
    lower = scad.color("red", 0.5)(lower)
    base = scad.translate([0, 0, maxh - height - inset])(base)

    return base - lower - toolcutouts


def toolinlay2(
    height: float,
    r_tool: Union[float, Iterable[float]] = 7,
    inset: float = 4,
    inset_tool: float = 3,
    maxh: float = 67,
    wall: float = 2,
):
    """toolinlay2

    creates a tooltray for alex container bins (2x3)

    .. image:: img/toolinlay2.png

    :param height: height of inset above lower bin
    :param r_tool: toolspace radius as float or list of floats
    :param inset: depth of toolinlay inset
    :param inset_tool: depth of additional toolspace inset
    :param maxh: system height restriction
    """
    x_dim = 2
    y_dim = 3
    r_in = 2

    base = slp.rfcube([x_dim * sl.settings.raster, y_dim * sl.settings.raster, height + inset], r_in + wall)

    toolindent = 0.1
    toolindent_w = 2

    toolcutouts = []
    tool_rs = []
    if not isinstance(r_tool, list):
        tool_rs = [r_tool] * (
            (sl.settings.raster * x_dim - 3 * wall) // (r_tool * 2 + 2 * toolindent_w) - 1
        )
    else:
        tool_rs = r_tool

    toolbase_l = sl.settings.raster * y_dim - 2 * wall - 2 * np.max(tool_rs)
    for i, r in enumerate(tool_rs):
        toolbase_t = scad.cube([0.001, toolbase_l * (1 - toolindent), inset_tool])
        # toolbase_b = scad.translate([0, toolbase_l * (1 - toolindent), 0])(
        # scad.cube([0.001, toolbase_l * toolindent - toolindent_w, tool_ins])
        # )
        toolcutout = slp.pill(
            np.array([[0, 0, 0], [0, toolbase_l, 0], [0, toolbase_l, 10], [0, 0, 10]])
            + [0, 0, -np.max(tool_rs) + r]
            + [i * (toolindent_w + 1.4) + np.sum(tool_rs[0:i]) * 2, 0, 0],
            r,
        )

        toolcutout = scad.translate(
            (
                np.max(tool_rs) + wall + toolindent_w,
                np.max(tool_rs) + wall,
                maxh - inset_tool,
            )
        )(toolcutout)
        toolcutouts.append(scad.color("yellow")(toolcutout))

    toolcutouts.append(
        slp.pill(
            np.array(
                [
                    [0, 0, 0],
                    [0, toolindent * toolbase_l, 0],
                    [sl.settings.raster * x_dim - 2 * wall - 2 * 9, 0, 0],
                    [sl.settings.raster * x_dim - 2 * wall - 2 * 9, toolindent * toolbase_l, 0],
                    [0, 0, 10],
                    [0, toolindent * toolbase_l, 10],
                    [sl.settings.raster * x_dim - 2 * wall - 2 * 9, 0, 10],
                    [sl.settings.raster * x_dim - 2 * wall - 2 * 9, toolindent * toolbase_l, 10],
                ]
            )
            + [9 + wall, 9 + wall, maxh - inset_tool],
            9,
        )
    )
    toolcutouts.append(
        scad.translate((wall * 2, wall * 2, 0))(
            slp.rfcube(
                [
                    toolindent * toolbase_l + 2 * 9,
                    toolindent * toolbase_l + 2 * 9 - wall,
                    100,
                ],
                9 - wall,
            )
        )
    )

    lower = bin(2, 3, h=maxh - height, wall=2.2)
    lower = scad.color("red", 0.5)(lower)
    base = scad.translate([0, 0, maxh - height - inset])(base)

    return base - lower - toolcutouts
