import unittest

from numpy import array
from nose.tools import eq_
from numpy.testing import assert_almost_equal, assert_array_almost_equal

from helix.models.coordinate import Coordinate


class CoordinateTest(unittest.TestCase):
    def test_equality(self):
        a = Coordinate(0, 0, 0)
        b = Coordinate(0, 0, 0)
        assert a == b

    def test_equality_x(self):
        a = Coordinate(0, 0, 0)
        b = Coordinate(1, 0, 0)
        assert a != b

        b = Coordinate(1e-9, 0, 0)
        assert a == b

    def test_equality_y(self):
        a = Coordinate(0, 0, 0)
        b = Coordinate(0, 1, 0)
        assert a != b

        b = Coordinate(0, 1e-7, 0)
        assert a == b

    def test_equality_rotation(self):
        a = Coordinate(0, 0, 0)
        b = Coordinate(0, 0, 1)
        assert a != b

        b = Coordinate(0, 0, 5e-7)
        assert a == b

    def test_subtraction(self):
        a = Coordinate(3, 4, 0)
        b = Coordinate(6, 2, 0)

        eq_(a - b, Coordinate(-3, 2, 0))

    def test_subtraction_maintains_rotation(self):
        a = Coordinate(3, 4, 0.2)
        b = Coordinate(6, 2, 0.2)

        eq_(a - b, Coordinate(-3, 2, 0.2))

    def test_subtraction_different_rotations_throws(self):
        a = Coordinate(3, 4, 0)
        b = Coordinate(6, 2, 0.2)

        with self.assertRaises(ValueError) as context:
            a - b
        assert context.exception

    def test_scale(self):
        eq_(Coordinate(4, 6).scale(0.75, 0.6666667), Coordinate(3, 4))

    def test_abs(self):
        eq_(abs(Coordinate(1, 0, 0)), 1)
        eq_(abs(Coordinate(3, 4, 0)), 5)

    def test_less_than_identical(self):
        a = Coordinate(0, 0, 0)
        b = Coordinate(0, 0, 0)
        assert not a < b
        assert not b < a

    def test_less_than_x_value(self):
        a = Coordinate(0, 0, 0)
        c = Coordinate(1, 0, 0)

        assert a < c
        assert not c < a

    def test_less_than_y_value(self):
        a = Coordinate(0, 0, 0)
        c = Coordinate(1, 0, 0)
        d = Coordinate(0, 1, 0)

        assert a < d
        assert not d < a

        assert c < d
        assert not d < c

    def test_less_than_along_x_access(self):
        c = Coordinate(1, 0, 0)
        d = Coordinate(0, 1, 0)
        e = Coordinate(2, 0, 0)

        assert c < e
        assert e < d

    def test_less_than_diagonal(self):
        d = Coordinate(0, 1, 0)
        e = Coordinate(2, 0, 0)
        f = Coordinate(1, 1, 0)

        assert d < f
        assert e < f

    def test_computes_length(self):
        a = Coordinate(29875.258, -61792.862, 2.534306)
        b = Coordinate(29787.104, -61796.764, 2.534306)

        assert_almost_equal(a.length(), 68635.915, 3)
        assert_almost_equal(b.length(), 68601.105, 3)

    def test_arrays_almost_equal_works(self):
        value = array([
            Coordinate(1.233, 3.444, 0)
        ])
        expected = array([
            Coordinate(1.234, 3.444, 0)
        ])

        assert_array_almost_equal(value, expected, decimal=3)

    def test_repr(self):
        eq_(str(Coordinate(1.233, 3.444, 0)), '{"rotation": 0, "x": 1.233, "y": 3.444}')

    def test_rotate(self):
        a = Coordinate(10, 10, 0)
        b = Coordinate(20, 5, 90)

        eq_(a.rotate(), Coordinate(10, 10, 0))
        eq_(b.rotate(), Coordinate(-5, 20, 0))
