import json
from math import hypot
import math
from helix.functions import fequal


class Coordinate(object):
    def __init__(self, x, y, rotation=0., calculate_rounding=True):
        self.x = x
        self.y = y
        self.rotation = rotation

        if calculate_rounding:
            self.__rounded_x = round(x, 3)
            self.__rounded_y = round(y, 3)
        else:
            self.__rounded_x = x
            self.__rounded_y = y

    def __eq__(self, other):
        if self is other:
            return True
        if not isinstance(other, self.__class__):
            return False
        if fequal(self.rotation, other.rotation, delta=1e-3):
            return fequal(self.x, other.x, delta=1e-3) and fequal(self.y, other.y, delta=1e-3)
        return False

    @property
    def dictionary(self):
        return {"x": self.x, "y": self.y, "rotation": self.rotation}

    def __repr__(self):
        return json.dumps(self.dictionary, sort_keys=True)

    def __sub__(self, other):
        if fequal(self.rotation, other.rotation, delta=1e-1):
            return Coordinate(self.x - other.x, self.y - other.y, self.rotation)
        else:
            raise ValueError

    def __add__(self, other):
        if abs(self.rotation - other.rotation) < 1e-3:
            return Coordinate(self.x + other.x, self.y + other.y, self.rotation, calculate_rounding=False)
        else:
            raise ValueError

    def __abs__(self):
        return self.length()

    def __lt__(self, other):
        if self == other:
            return False
        if self.y < other.y:
            return True
        elif other.y < self.y:
            return False
        return self.x < other.x

    def __round__(self, n=0):
        return Coordinate(round(self.x, n), round(self.y, n), self.rotation)

    def __hash__(self):
        return hash(self.__rounded_x) ^ hash(self.__rounded_y) ^ hash(self.rotation)

    # Returns a Coordinate based on self, rotated by self's rotation
    def rotate(self):
        rotation = math.radians(self.rotation)
        x = self.x * math.cos(rotation) - self.y * math.sin(rotation)
        y = self.x * math.sin(rotation) + self.y * math.cos(rotation)
        return Coordinate(x, y)

    # Returns a Coordinate based on self, rotated by self's negative rotation
    def unrotate(self):
        rotation = math.radians(-self.rotation)
        x = self.x * math.cos(rotation) - self.y * math.sin(rotation)
        y = self.x * math.sin(rotation) + self.y * math.cos(rotation)
        return Coordinate(x, y)

    def scale(self, x, y):
        return Coordinate(self.x * x, self.y * y, self.rotation)

    def neg_translate(self, other):
        return Coordinate(self.x - other.x, self.y - other.y, self.rotation)

    def length(self):
        return hypot(self.x, self.y)
