import math
import itertools

from dataclasses import dataclass, KW_ONLY, replace

from .gerber_statements import *


class GraphicPrimitive:
    _ : KW_ONLY
    polarity_dark : bool = True


def rotate_point(x, y, angle, cx=None, cy=None):
    if cx is None:
        return (x, y)
    else:
        return (cx + (x - cx) * math.cos(angle) - (y - cy) * math.sin(angle),
                cy + (x - cx) * math.sin(angle) + (y - cy) * math.cos(angle))


@dataclass
class Circle(GraphicPrimitive):
    x : float
    y : float
    r : float # Here, we use radius as common in modern computer graphics, not diameter as gerber uses.

    def bounds(self):
        return ((self.x-self.r, self.y-self.r), (self.x+self.r, self.y+self.r))


@dataclass
class Obround(GraphicPrimitive):
    x : float
    y : float
    w : float
    h : float
    rotation : float # radians!

    def decompose(self):
        ''' decompose obround to two circles and one rectangle '''

        cx = self.x + self.w/2
        cy = self.y + self.h/2

        if self.w > self.h:
            x = self.x + self.h/2
            yield Circle(x, cy, self.h/2)
            yield Circle(x + self.w, cy, self.h/2)
            yield Rectangle(x, self.y, self.w - self.h, self.h)

        elif self.h > self.w:
            y = self.y + self.w/2
            yield Circle(cx, y, self.w/2)
            yield Circle(cx, y + self.h, self.w/2)
            yield Rectangle(self.x, y, self.w, self.h - self.w)

        else:
            yield Circle(cx, cy, self.w/2)

    def bounds(self):
        return ((self.x-self.w/2, self.y-self.h/2), (self.x+self.w/2, self.y+self.h/2))


@dataclass
class ArcPoly(GraphicPrimitive):
    """ Polygon whose sides may be either straight lines or circular arcs """

    # list of (x : float, y : float) tuples. Describes closed outline, i.e. first and last point are considered
    # connected.
    outline : [(float,)]
    # list of radii of segments, must be either None (all segments are straight lines) or same length as outline.
    # Straight line segments have None entry.
    arc_centers : [(float,)]

    @property
    def segments(self):
        return itertools.zip_longest(self.outline[:-1], self.outline[1:], self.radii or [])

    def bounds(self):
        for (x1, y1), (x2, y2), radius in self.segments:
            return 

    def __len__(self):
        return len(self.outline)

    def __bool__(self):
        return bool(len(self))


@dataclass
class Line(GraphicPrimitive):
    x1 : float
    y1 : float
    x2 : float
    y2 : float
    width : float

    # FIXME bounds

@dataclass
class Arc(GraphicPrimitive):
    x1 : float
    y1 : float
    x2 : float
    y2 : float
    cx : float
    cy : float
    flipped : bool
    width : float

    # FIXME bounds

@dataclass
class Rectangle(GraphicPrimitive):
    # coordinates are center coordinates
    x : float
    y : float
    w : float
    h : float
    rotation : float # radians, around center!

    def bounds(self):
        return ((self.x, self.y), (self.x+self.w, self.y+self.h))

    @property
    def center(self):
        return self.x + self.w/2, self.y + self.h/2


class RegularPolygon(GraphicPrimitive):
    x : float
    y : float
    r : float
    n : int
    rotation : float # radians!

    def decompose(self):
        ''' convert n-sided gerber polygon to normal Region defined by outline '''

        delta = 2*math.pi / self.n

        yield Region([
                (self.x + math.cos(self.rotation + i*delta) * self.r,
                 self.y + math.sin(self.rotation + i*delta) * self.r)
                for i in range(self.n) ])