import numpy as np
from enum import Enum
from typing import Tuple, Dict, List
from numpy.typing import NDArray

from matplotlib import (
    pyplot as plt,
)
from matplotlib.pyplot import Figure, Axes
from matplotlib.lines import Line2D
from matplotlib.patches import Circle


from pycivil.EXAGeometry.geometry import Point2d


TpLine2D = Tuple[NDArray[np.double], NDArray[np.double]]

def twoP2N(p1: Point2d, p2: Point2d) -> TpLine2D:
    return np.array([p1.x, p2.x]), np.array([p1.y, p2.y])

class Basic2DType(Enum):
    LINE2D = 1,
    CIRCLE2D = 2,
    RECTANGLE2D = 3,

class Plotter:
    def __init__(self, axes: Axes) -> None:
        self.__id: int = 0
        self.__axes: Axes = axes
        self.__obj_tp: Dict[int, Basic2DType] = {}
        self.__lines: Dict[int, Tuple[Point2d, Point2d]]= {}
        self.__circles: Dict[int, Tuple[Point2d, float]]= {}

    def __new_id(self) -> int:
        self.__id += 1
        return self.__id

    def add_line(self, p1: Point2d, p2: Point2d) -> int:
        new_id = self.__new_id()
        self.__lines[new_id] = (p1, p2)
        self.__obj_tp[new_id] = Basic2DType.LINE2D
        return new_id

    def add_circle(self, center: Point2d, radius: float) -> int:
        new_id = self.__new_id()
        self.__circles[new_id] = (center, radius)
        self.__obj_tp[new_id] = Basic2DType.CIRCLE2D
        return new_id

    def rotate(self, items: List[int], angle: float, center: Point2d) -> None:
        for i in items:
            if self.__obj_tp.get(i) == Basic2DType.LINE2D:
                points = self.__lines.get(i)
                assert points is not None
                points[0].rotate(angle, center)
                points[1].rotate(angle, center)
            elif self.__obj_tp.get(i) == Basic2DType.CIRCLE2D:
                circle = self.__circles.get(i)
                assert circle is not None
                circle[0].rotate(angle, center)
            else:
                raise ValueError(f"Object with id={id} is unknown type !!!")

    def plot(self) -> Axes:

        for k in self.__lines.keys():
            np_data = twoP2N(self.__lines[k][0], self.__lines[k][1])
            self.__axes.add_line(Line2D(np_data[0], np_data[1]))

        for k in self.__circles.keys():
            self.__axes.add_artist(
                Circle(
                    (self.__circles[k][0].x,self.__circles[k][0].y),
                    self.__circles[k][1], fill=False),
            )

        return self.__axes

# EXAMPLE of use
# fig: Figure = plt.figure()
# ax: Axes = fig.add_subplot(1, 1, 1)
# ax.set_aspect("equal", "datalim")
# pl = Plotter(ax)
# #################################
# #           GEOM OP
# ################################
# l1 = pl.add_line(Point2d(0.0, 0.0), Point2d(1.0, 1.0))
# l2 = pl.add_line(Point2d(1.0, 0.0), Point2d(0.0, 1.0))
# c1 = pl.add_circle(Point2d(0.5, 0.5), 0.25)
# pl.rotate([l1, l2], 45, Point2d(0.5, 0.5))
# pl.rotate([c1], 45, Point2d(0.0, 0.0))
# #################################
# plt.autoscale()
# pl.plot()
#
#
# plt.show()
