import numpy as np

from geometer import (Circle, Line, Point, Transformation, TransformationCollection, angle, reflection, rotation,
                      scaling, translation)


class TestTransformation:
    def test_from_points(self):
        p1 = Point(0, 0)
        p2 = Point(1, 0)
        p3 = Point(0, 1)
        p4 = Point(3, 5)
        l = Line(p1, p3)

        M = Transformation.from_points(
            (p1, p1 + Point(1, 1)),
            (p2, p2 + Point(1, 1)),
            (p3, p3 + Point(1, 1)),
            (p4, p4 + Point(1, 1)),
        )

        assert M * p3 == Point(1, 2)
        assert (M * l).contains(M * p1)
        assert (M * l).contains(M * p3)

    def test_translation(self):
        p = Point(0, 1)
        t = translation(0, -1)
        assert t * p == Point(0, 0)

        l = Line(Point(0, 0, 1), Point(1, 0, 1))
        t = translation(0, 0, -1)
        assert t * l == Line(Point(0, 0, 0), Point(1, 0, 0))

    def test_inverse(self):
        E = Transformation(np.eye(4))
        M = rotation(np.pi, axis=Point(0, 1, 0))
        assert M.inverse() * M == E

    def test_pow(self):
        t = translation(1, 2)

        assert t ** 0 == Transformation(np.eye(3))
        assert t ** 1 == t
        assert t ** 2 == translation(2, 4)
        assert t ** 3 == translation(3, 6)
        assert t ** (-2) == translation(-2, -4)

    def test_rotation(self):
        p = Point(0, 1)
        t = rotation(-np.pi)
        assert t * p == Point(0, -1)

        p = Point(1, 0, 0)
        t = rotation(-np.pi / 2, axis=Point(0, 0, 1))
        assert t * p == Point(0, 1, 0)

        p = Point(-1, 1, 0)
        a = np.pi / 7
        t = rotation(a, axis=Point(1, 1, 2))
        assert np.isclose(angle(p, t * p), a)

    def test_scaling(self):
        p = Point(1, 1, 2)
        s = scaling(3, -4.5, 5)

        assert s * p == Point(3, -4.5, 10)

    def test_reflection(self):
        p = Point(-1, 1)
        r = reflection(Line(1, -1, 1))

        assert r * p == Point(0, 0)

    def test_from_points_and_conics(self):
        c1 = Circle()
        p1 = Point(0, -1)
        p2 = Point(0, 1)
        p3 = Point(1, 0)

        c2 = Circle(Point(0, 2), 2)
        q1 = Point(0, 0)
        q2 = Point(0, 4)
        q3 = Point(2, 2)

        t = Transformation.from_points_and_conics([p1, p2, p3], [q1, q2, q3], c1, c2)

        assert t * p1 == q1
        assert t * p2 == q2
        assert t * p2 == q2
        assert t.apply(c1) == c2


class TestTransformationCollection:
    def test_inverse(self):
        E = TransformationCollection([np.eye(4)] * 10)
        M = TransformationCollection([rotation(np.pi, axis=Point(0, 1, 0))] * 10)
        assert M.inverse() * M == E

    def test_pow(self):
        t = TransformationCollection([translation(1, 2)] * 10)

        assert t ** 0 == TransformationCollection([np.eye(3)] * 10)
        assert t ** 1 == t
        assert t ** 2 == TransformationCollection([translation(2, 4)] * 10)
        assert t ** 3 == TransformationCollection([translation(3, 6)] * 10)
        assert t ** (-2) == TransformationCollection([translation(-2, -4)] * 10)
