""" A class to define tension vectors in CellFIT"""

import itertools
from math import pi, sin, cos, isclose

from . import edge


class TensionVector:
    id_iter = itertools.count()

    def __init__(self, edge):
        self._magnitude = 0
        self._direction = 0
        self._corresponding_edge = edge
        self._label = next(TensionVector.id_iter)

    @property
    def magnitude(self):
        """ Magnitude of the tension vector

        :return: magnitude
        """
        return self._magnitude

    @magnitude.setter
    def magnitude(self, value):
        """ Set the magnitude of the tension vector to a value

        :param value: value of the magnitude that is being set
        :type value: float
        :return: None
        """
        self._magnitude = value

    @property
    def direction(self, units='rad'):
        """ returns the direction of the tension vector in radians (or degrees) from the horizontal

        :param units: units for the direction, either 'rad' or 'deg'
        :type units: str
        :raises ValueError: is units is not 'rad' or 'deg
        :return: direction
        :rtype: float
        """

        if units == 'rad':
            return self._direction
        elif units == 'deg':
            return self._direction * 180 / pi
        else:
            raise ValueError("units should be 'deg' or 'rad'. Your units were {}.".format(units))

    @direction.setter
    def direction(self, value, units='rad'):
        """ sets the direction of the tension vector with an input of radians (default) or degrees

        :param value: direction of the vector in radians from the horizontal
        :type value: float
        :param units: units for the direction, either 'rad' or 'deg'
        :type units: str
        :raises ValueError: is units is not 'rad' or 'deg
        :return: None
        """
        if units == 'rad':
            self._direction = value
        elif units == 'deg':
            self._direction = value * pi / 180
        else:
            raise ValueError("units should be 'deg' or 'rad'. Your units were {}.".format(units))

    @property
    def corresponding_edge(self):
        """ The corresponding Edge for this tension vector

        :return: corresponding edge
        :rtype: edge.Edge
        """

        return self._corresponding_edge

    @corresponding_edge.setter
    def corresponding_edge(self, edge):
        """ assigns a corresponding Edge for this tension vector

        :param edge: the corresponding edge for this tension vector
        :return: None
        """

        if isinstance(edge, edge.Edge):
            self._corresponding_edge = edge
        else:
            raise TypeError('corresponding_edge should be of type Edge. Instead, it was of type {}'.format(type(
                edge)))

    @property
    def label(self):
        """ returns the label (id number) for this tension vector

        :return: label
        :rtype: int
        """

        return self._label

    @property
    def x_component(self):
        """ returns the x-component of the tension vector

        :return: x-component of the vector
        :rtype: float
        """

        return self._magnitude * cos(self._direction)

    @property
    def y_component(self):
        """ returns the y-component of the tension vector

        :return: y-component of the vector
        :rtype: float
        """

        return self._magnitude * sin(self._direction)

    def __eq__(self, other):
        return isclose(self._magnitude, other.magnitude) and isclose(self._direction, other.direction)

    def __str__(self):
        return str(self._magnitude) + ' @ ' + str(self._direction) + ' radians'

    def __hash__(self):
        return hash(str(self))

    def __repr__(self):
        return repr('TensionVector({})'.format(self._corresponding_edge))
