# Path: hotwing_core/coordinate.py
# class Coordinate():
#     """
#     2 Dimensional X-Y coordinate.
# 
#     Args:
#         x (float): X value of coordinate
#         y (float): Y value of coordinate
# 
#     :ivar x: x coordinate
#     :ivar y: y coordinate
#     """
#     def __init__(self, x, y):
#         self.x = x
#         self.y = y
# 
#     @classmethod
#     def calc_dist(cls, a, b):
#         """
#         Calculate the distance between two Coordinates.
# 
#         Args:
#             a (Coordinate): First Coordinate
#             b (Coordinate): Second Coordinate
#         Returns:
#             Float: Distance between c1 and c2
# 
#         """
#         dist = math.sqrt((a.x-b.x)**2+(a.y-b.y)**2)
#         return dist
# 
#     @classmethod
#     def calc_slope(cls, a, b):
#         """
#         Calculate the slope between two Coordinates.
# 
#         Args:
#             a (Coordinate): First Coordinate
#             b (Coordinate): Second Coordinate
#         Returns:
#             Float: Slope between a and b
#         """
#         try:
#             slope = (a.y-b.y) / (a.x-b.x)
#         except ZeroDivisionError:
#             # same x val -> infinite slope
#             slope = float('inf')
#         return slope
# 
#     @classmethod
#     def rotate(cls, origin, coordinate, angle):
#         """
#         Rotate a Coordinate around a point in 2D space.
# 
#         Args:
#             origin (Coordinate) : Defines the point to rotate around
#             coordinate (Coordinate): The Coordinate to rotate
#             angle (Float) : Degrees to rotate Coordinate
# 
#         Returns:
#             Coordinate: New rotated Coordinate
#         """
#         angle = math.radians(angle)
#         ox = origin.x
#         oy = origin.y
#         px = coordinate.x
#         py = coordinate.y
# 
#         qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
#         qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
#         return cls(qx, qy)
# 
#     def __str__(self):
#         return "%.5f, %.5f" % (self.x, self.y)
# 
#     def __repr__(self):
#         return "Coordinate: %.5f, %.5f" % (self.x, self.y)
# 
#     def __eq__(self, other):
#         """
#         Compare two a Coordinate objects
#         """
#         # comparison tolerance - necessary due to the rounding
#         # error with floating point numbers 
#         tolerance = 0.00000001
# 
#         if isinstance(other, self.__class__):
#             if abs(self.x - other.x) >= tolerance:
#                 return False
#             if abs(self.y - other.y) >= tolerance:
#                 return False
#             return True
#         raise NotImplementedError
# 
#     def __ne__(self, other):
#         """
#         Compare two a Coordinate objects
#         """
#         if isinstance(other, self.__class__):
#             return not self.__eq__(other)
#         raise NotImplementedError
# 
#     def __add__(self, other):
#         """
#         Add two a Coordinate objects
#         """
#         if isinstance(other, self.__class__):
#             return Coordinate(self.x+other.x, self.y+other.y)
#         raise NotImplementedError
# 
#     def __sub__(self, other):
#         """
#         Subtract two a Coordinate objects
#         """
#         if isinstance(other, self.__class__):
#             return Coordinate(self.x-other.x, self.y-other.y)
#         raise NotImplementedError
# 
#     def __mul__(self, other):
#         """
#         Multiply two a Coordinate objects
#         """
#         if isinstance(other, self.__class__):
#             return Coordinate(self.x*other.x, self.y*other.y)
#         return Coordinate(self.x*other, self.y*other)
# 
# Path: hotwing_core/utils.py
# def isect_line_plane_v3(p0, p1, p_co, p_no, epsilon=1e-6):
#     """
#     p0, p1: define the line
#     p_co, p_no: define the plane:
#         p_co is a point on the plane (plane coordinate).
#         p_no is a normal vector defining the plane direction;
#              (does not need to be normalized).
# 
#     return a Vector or None (when the intersection can't be found).
#     """
# 
#     u = sub_v3v3(p1, p0)
#     dot = dot_v3v3(p_no, u)
# 
#     if abs(dot) > epsilon:
#         # the factor of the point between p0 -> p1 (0 - 1)
#         # if 'fac' is between (0 - 1) the point intersects with the segment.
#         # otherwise:
#         #  < 0.0: behind p0.
#         #  > 1.0: infront of p1.
#         w = sub_v3v3(p0, p_co)
#         fac = -dot_v3v3(p_no, w) / dot
#         u = mul_v3_fl(u, fac)
#         return add_v3v3(p0, u)
#     else:
#         # The segment is parallel to plane
#         return None


from .coordinate import Coordinate
from .utils import isect_line_plane_v3
import math
class Surface():
    """
    A Surface is a list of Coordinates that when connected together by lines make up the shape of 
    the surface.  A Surface is used to define the top or bottom surfaces of an airfoil.

    Args:
        coordinates (Coordinate[]): list of Coordinates
                        ASSUMED TO BE IN EITHER ASCENDING OR DESCENDING
                        ORDER BASED ON X VALUES

    :ivar coordinates: list of Coordinates that make up surface
    """

    def __init__(self, coordinates=[]):
        self.coordinates = coordinates
        self._remove_duplicate_coordinates()
        self._order_coordinates()

    @property
    def bounds(self):
        """
        Get the bounding box of the Surface

        Returns:
            (Coordinate,Coordinate): Tuple of Coordinates with the min xy and the max xy value
        """
        min_x = min([c.x for c in self.coordinates])
        min_y = min([c.y for c in self.coordinates])
        max_x = max([c.x for c in self.coordinates])
        max_y = max([c.y for c in self.coordinates])

# Path: hotwing_core/coordinate.py
# class Coordinate():
#     """
#     2 Dimensional X-Y coordinate.
# 
#     Args:
#         x (float): X value of coordinate
#         y (float): Y value of coordinate
# 
#     :ivar x: x coordinate
#     :ivar y: y coordinate
#     """
#     def __init__(self, x, y):
#         self.x = x
#         self.y = y
# 
#     @classmethod
#     def calc_dist(cls, a, b):
#         """
#         Calculate the distance between two Coordinates.
# 
#         Args:
#             a (Coordinate): First Coordinate
#             b (Coordinate): Second Coordinate
#         Returns:
#             Float: Distance between c1 and c2
# 
#         """
#         dist = math.sqrt((a.x-b.x)**2+(a.y-b.y)**2)
#         return dist
# 
#     @classmethod
#     def calc_slope(cls, a, b):
#         """
#         Calculate the slope between two Coordinates.
# 
#         Args:
#             a (Coordinate): First Coordinate
#             b (Coordinate): Second Coordinate
#         Returns:
#             Float: Slope between a and b
#         """
#         try:
#             slope = (a.y-b.y) / (a.x-b.x)
#         except ZeroDivisionError:
#             # same x val -> infinite slope
#             slope = float('inf')
#         return slope
# 
#     @classmethod
#     def rotate(cls, origin, coordinate, angle):
#         """
#         Rotate a Coordinate around a point in 2D space.
# 
#         Args:
#             origin (Coordinate) : Defines the point to rotate around
#             coordinate (Coordinate): The Coordinate to rotate
#             angle (Float) : Degrees to rotate Coordinate
# 
#         Returns:
#             Coordinate: New rotated Coordinate
#         """
#         angle = math.radians(angle)
#         ox = origin.x
#         oy = origin.y
#         px = coordinate.x
#         py = coordinate.y
# 
#         qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
#         qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
#         return cls(qx, qy)
# 
#     def __str__(self):
#         return "%.5f, %.5f" % (self.x, self.y)
# 
#     def __repr__(self):
#         return "Coordinate: %.5f, %.5f" % (self.x, self.y)
# 
#     def __eq__(self, other):
#         """
#         Compare two a Coordinate objects
#         """
#         # comparison tolerance - necessary due to the rounding
#         # error with floating point numbers 
#         tolerance = 0.00000001
# 
#         if isinstance(other, self.__class__):
#             if abs(self.x - other.x) >= tolerance:
#                 return False
#             if abs(self.y - other.y) >= tolerance:
#                 return False
#             return True
#         raise NotImplementedError
# 
#     def __ne__(self, other):
#         """
#         Compare two a Coordinate objects
#         """
#         if isinstance(other, self.__class__):
#             return not self.__eq__(other)
#         raise NotImplementedError
# 
#     def __add__(self, other):
#         """
#         Add two a Coordinate objects
#         """
#         if isinstance(other, self.__class__):
#             return Coordinate(self.x+other.x, self.y+other.y)
#         raise NotImplementedError
# 
#     def __sub__(self, other):
#         """
#         Subtract two a Coordinate objects
#         """
#         if isinstance(other, self.__class__):
#             return Coordinate(self.x-other.x, self.y-other.y)
#         raise NotImplementedError
# 
#     def __mul__(self, other):
#         """
#         Multiply two a Coordinate objects
#         """
#         if isinstance(other, self.__class__):
#             return Coordinate(self.x*other.x, self.y*other.y)
#         return Coordinate(self.x*other, self.y*other)
# 
# Path: hotwing_core/utils.py
# def isect_line_plane_v3(p0, p1, p_co, p_no, epsilon=1e-6):
#     """
#     p0, p1: define the line
#     p_co, p_no: define the plane:
#         p_co is a point on the plane (plane coordinate).
#         p_no is a normal vector defining the plane direction;
#              (does not need to be normalized).
# 
#     return a Vector or None (when the intersection can't be found).
#     """
# 
#     u = sub_v3v3(p1, p0)
#     dot = dot_v3v3(p_no, u)
# 
#     if abs(dot) > epsilon:
#         # the factor of the point between p0 -> p1 (0 - 1)
#         # if 'fac' is between (0 - 1) the point intersects with the segment.
#         # otherwise:
#         #  < 0.0: behind p0.
#         #  > 1.0: infront of p1.
#         w = sub_v3v3(p0, p_co)
#         fac = -dot_v3v3(p_no, w) / dot
#         u = mul_v3_fl(u, fac)
#         return add_v3v3(p0, u)
#     else:
#         # The segment is parallel to plane
#         return None


# Path: hotwing_core/surface.py
from .coordinate import Coordinate
from .utils import isect_line_plane_v3
import math

class Surface():
    """
    A Surface is a list of Coordinates that when connected together by lines make up the shape of 
    the surface.  A Surface is used to define the top or bottom surfaces of an airfoil.

    Args:
        coordinates (Coordinate[]): list of Coordinates
                        ASSUMED TO BE IN EITHER ASCENDING OR DESCENDING
                        ORDER BASED ON X VALUES

    :ivar coordinates: list of Coordinates that make up surface
    """

    def __init__(self, coordinates=[]):
        self.coordinates = coordinates
        self._remove_duplicate_coordinates()
        self._order_coordinates()

    @property
    def bounds(self):
        """
        Get the bounding box of the Surface

        Returns:
            (Coordinate,Coordinate): Tuple of Coordinates with the min xy and the max xy value
        """
        min_x = min([c.x for c in self.coordinates])
        min_y = min([c.y for c in self.coordinates])
        max_x = max([c.x for c in self.coordinates])
        max_y = max([c.y for c in self.coordinates])

        return (Coordinate(min_x, min_y), Coordinate(max_x, max_y))