"""Test the Vector class"""
from random import Random
import pytest
from rubato.utils.vector import Vector
# pylint: disable=redefined-outer-name


@pytest.fixture()
def v1():
    return Vector(1, 1)


@pytest.fixture()
def v34():
    return Vector(3, 4)


def test_init(v1, v34):
    assert v1.x == 1
    assert v1.y == 1
    assert v34.x == 3
    assert v34.y == 4

    with pytest.raises(TypeError):
        Vector(1, "2")


def test_mag(v1, v34):
    assert v34.magnitude == 5
    assert v34.mag_sq == 25
    v34.magnitude = 10
    assert v34.magnitude == 10
    assert v34.x == 6
    assert v34.y == 8

    v = Vector()
    assert v.magnitude == 0
    v.magnitude = 0
    assert v.magnitude == 0

    assert v1.mag_sq == 2


def test_angle(v1, v34):
    assert v1.angle == 135
    assert v34.angle == 143.13010235415598


def test_rationalized_mag(v1, v34):
    assert v1.rationalized_mag == "√2"
    assert v1.rationalized_mag_vector == Vector(1, 2)
    assert v1.rationalized_unit == "<1/1√2, 1/1√2>"
    assert v34.rationalized_unit == "<3/5, 4/5>"


def test_unit(v34):
    unit = v34.unit()
    assert unit.x == 3 / 5
    assert unit.y == 4 / 5
    assert Vector().unit().x == 0
    assert Vector().unit().y == 0

    v34.normalize()
    assert unit == v34


def test_tuple(v1, v34):
    assert v1.to_tuple() == (1, 1)
    assert v34.to_tuple() == (3, 4)


def test_dot(v1, v34):
    assert v1.dot(v34) == 7
    assert v34.dot(v1) == 7


def test_cross(v1, v34):
    assert v1.cross(v34) == 1
    assert v34.cross(v1) == -1


def test_perpendicular(v1, v34):
    assert v1.perpendicular() == Vector(1, -1)
    assert v34.perpendicular(2) == Vector(8, -6)


def test_clamp(v1, v34):
    assert v1.clamp(2, 2) == Vector(2, 2)
    assert v34.clamp(1, 2) == Vector(2, 2)
    assert v34.clamp(Vector(1, 4), Vector(2, 4), True) == Vector(2, 4)


def test_rotate(v1):
    assert v1.rotate(90) == Vector(1, -1)
    assert v1.rotate(45) == Vector(1.4142135624, 0)


def test_to_int(v1, v34):
    v1 += 0.1
    assert not isinstance(v1.x, int)
    assert not isinstance(v1.y, int)

    v1 = v1.to_int()
    assert isinstance(v1.x, int)
    assert isinstance(v1.y, int)

    v34 += 0.1
    v34 = v34.tuple_int()
    assert isinstance(v34[0], int)
    assert isinstance(v34[1], int)


def test_clone(v1, v34):
    v3 = v1.clone()
    assert v3 == v1
    assert v3 is not v1

    v3 = v34.clone()
    assert v3 == v34
    assert v3 is not v34


def test_lerp(v1, v34):
    assert v1.lerp(v34, 0.5) == Vector(2, 2.5)
    assert v34.lerp(v1, 0.5) == Vector(2, 2.5)


def test_round(v1):
    v1 += 0.1111111111
    assert v1.round(1) == Vector(1.1, 1.1)
    assert v1.round(0) == Vector(1, 1)
    assert v1.round(1) == Vector(1.1, 1.1)
    assert v1.round(2) == Vector(1.11, 1.11)


def test_ceil(v1):
    v1 += 0.1111111111
    assert v1.ceil() == Vector(2, 2)


def test_floor(v1):
    v1 += 0.1111111111
    assert v1.floor() == Vector(1, 1)


def test_abs():
    v = Vector(-1, -1)
    assert v.abs() == Vector(1, 1)


def test_dir_to(v1, v34):
    assert v1.dir_to(v34) == Vector(0.5547001962, 0.8320502943)
    assert v34.dir_to(v1) == Vector(-0.5547001962, -0.8320502943)


def test_distance_between(v1, v34):
    assert v1.distance_between(v34) == 3.605551275463989
    assert v34.distance_between(v1) == 3.605551275463989


def test_from_radial():
    assert Vector.from_radial(1, 0) == Vector(0, -1)
    assert Vector.from_radial(1, 90) == Vector(1, 0)
    assert Vector.from_radial(1, 180) == Vector(0, 1)
    assert Vector.from_radial(1, 270) == Vector(-1, 0)
    assert Vector.from_radial(1, 360) == Vector(0, -1)


def test_clamp_mag(v34):
    v = Vector.clamp_magnitude(v34, 2)
    assert v.magnitude == 2


def test_angle_between(v1, v34):
    assert Vector.angle_between(v1, v34) == 8.1301023542


def test_random(monkeypatch):
    random = Random(1)
    monkeypatch.setattr("rubato.utils.vector.random", random)
    v = Vector.random_inside_unit_circle()
    assert v.x == 0.7474634342
    assert v.y == -0.6643029539


def test_quick_vectors():
    # pylint: disable=comparison-with-callable
    assert Vector.zero == Vector()
    assert Vector.one == Vector(1, 1)
    assert Vector.up == Vector(0, -1)
    assert Vector.down == Vector(0, 1)
    assert Vector.left == Vector(-1, 0)
    assert Vector.right == Vector(1, 0)
    assert Vector.infinity == Vector(float("inf"), float("inf"))


def test_eq():
    assert Vector(1, 1) == Vector(1, 1)
    assert Vector(1, 1) != Vector(1, 2)
    assert Vector(1, 1) != 1


def test_inequalities():
    assert Vector(1, 1) < Vector(2, 2)
    assert Vector(4, 3) > Vector(2, 2)
    assert Vector(1, 1) <= Vector(1, 1)
    assert Vector(4, 3) >= Vector(1, 1)

    # pylint: disable=unneeded-not
    assert not Vector(1, 1) < 2
    assert not Vector(4, 3) > 1
    assert not Vector(1, 1) <= 1
    assert not Vector(4, 3) >= 1


def test_str(v1):
    assert str(v1) == "<1, 1>"
    assert "rubato.Vector(1, 1) at" in repr(v1)


def test_math(v1, v34):
    vc = v34.clone()
    vc **= 3
    assert vc == Vector(27, 64)
    assert v34**2 == Vector(9, 16)
    assert v34**v34 == Vector(27, 256)

    vc = v34.clone()
    vc *= 4
    assert vc == Vector(12, 16)
    assert v1 * 2 == Vector(2, 2)
    assert 2 * v1 == Vector(2, 2)
    assert v34 * v1 == Vector(3, 4)

    vc = v34.clone()
    vc /= 2
    assert vc == Vector(1.5, 2)
    assert v1 / 2 == Vector(0.5, 0.5)
    assert 2 / v1 == Vector(2, 2)
    assert v1 / v34 == Vector(1 / 3, 1 / 4)

    vc = v34.clone()
    vc //= 2
    assert vc == Vector(1, 2)
    assert v1 // 2 == Vector(0, 0)
    assert 2 // v1 == Vector(2, 2)
    assert v1 // v34 == Vector(0, 0)

    vc = v34.clone()
    vc += v1
    assert vc == Vector(4, 5)
    assert v1 + v34 == Vector(4, 5)
    assert v1 + 2 == Vector(3, 3)
    assert 2 + v1 == Vector(3, 3)

    vc = v34.clone()
    vc -= v1
    assert vc == Vector(2, 3)
    assert v1 - v34 == Vector(-2, -3)
    assert 2 - v1 == Vector(1, 1)
    assert v1 - 2 == Vector(-1, -1)

    assert -v1 == Vector(-1, -1)

    assert hash(v1) == hash((1, 1))
    assert hash(v34) == hash((3, 4))


def test_iter(v1):
    assert list(v1) == [1, 1]
