#include <psemek/test/test.hpp>

#include <psemek/math/vector.hpp>
#include <psemek/math/math.hpp>

using namespace psemek::math;

test_case(math_vector_empty)
{
	vector<int, 0> pi;
	vector<float, 0> pf;

	static_assert(std::is_same_v<decltype(pi)::scalar_type, int>);
	static_assert(std::is_same_v<decltype(pf)::scalar_type, float>);

	expect_equal(pi.dimension(), 0);
	expect_equal(pf.dimension(), 0);
	expect_throw(pi[0], detail::empty_array_exception);
	expect_throw(pf[0], detail::empty_array_exception);
}

test_case(math_vector_init)
{
	vector<int, 2> pi{1, 2};
	vector<float, 3> pf{3.f, 4.f, 5.f};

	static_assert(std::is_same_v<decltype(pi)::scalar_type, int>);
	static_assert(std::is_same_v<decltype(pf)::scalar_type, float>);

	expect_equal(pi[0], 1);
	expect_equal(pi[1], 2);
	expect_equal(pf[0], 3.f);
	expect_equal(pf[1], 4.f);
	expect_equal(pf[2], 5.f);
}

test_case(math_vector_dynamic)
{
	vector<int, dynamic> pi1(std::size_t(15));
	vector<int, dynamic> pi2{1, 2, 3, 4, 5};

	static_assert(std::is_same_v<decltype(pi1)::scalar_type, int>);
	static_assert(std::is_same_v<decltype(pi2)::scalar_type, int>);

	expect_equal(pi1.dimension(), 15);
	expect_equal(pi2.dimension(), 5);

	expect_equal(pi2[0], 1);
	expect_equal(pi2[1], 2);
	expect_equal(pi2[2], 3);
	expect_equal(pi2[3], 4);
	expect_equal(pi2[4], 5);
}

test_case(math_vector_deduce)
{
	vector pi{1, 2};
	vector pf{3.f, 4.f, 5.f};

	static_assert(std::is_same_v<decltype(pi)::scalar_type, int>);
	static_assert(std::is_same_v<decltype(pf)::scalar_type, float>);

	expect_equal(pi[0], 1);
	expect_equal(pi[1], 2);
	expect_equal(pf[0], 3.f);
	expect_equal(pf[1], 4.f);
	expect_equal(pf[2], 5.f);
}

test_case(math_vector_arithmetic)
{
	vector vi1{1, 2};
	vector vi2{3, 4};

	expect_equal(-vi1, (vector{-1, -2}));
	expect_equal(5 * vi1, (vector{5, 10}));
	expect_equal(vi1 * 5, (vector{5, 10}));
	expect_equal(vi1 + vi2, (vector{4, 6}));
	expect_equal(vi1 - vi2, (vector{-2, -2}));
	expect_equal(2 * vi1 - 3 * vi2, (vector{-7, -8}));

	vector vf1{5.f, 6.f, 7.f};
	vector vf2{8.f, 9.f, 10.f};

	expect_equal(-vf1, (vector{-5.f, -6.f, -7.f}));
	expect_equal(5.f * vf1, (vector{25.f, 30.f, 35.f}));
	expect_equal(vf1 * 5.f, (vector{25.f, 30.f, 35.f}));
	expect_equal(vf1 + vf2, (vector{13.f, 15.f, 17.f}));
	expect_equal(vf1 - vf2, (vector{-3.f, -3.f, -3.f}));
	expect_equal(2.f * vf1 - 3.f * vf2, (vector{-14.f, -15.f, -16.f}));

	vector<float, dynamic> vd1{-1.f, -2.f, -3.f};
	vector<float, dynamic> vd2{-4.f, -5.f, -6.f};

	expect_equal(-vd1, (vector<float, dynamic>{1.f, 2.f, 3.f}));
	expect_equal(5.f * vd1, (vector<float, dynamic>{-5.f, -10.f, -15.f}));
	expect_equal(vd1 * 5.f, (vector<float, dynamic>{-5.f, -10.f, -15.f}));
	expect_equal(vd1 + vd2, (vector<float, dynamic>{-5.f, -7.f, -9.f}));
	expect_equal(vd1 - vd2, (vector<float, dynamic>{3.f, 3.f, 3.f}));
	expect_equal(2.f * vd1 - 3.f * vd2, (vector<float, dynamic>{10.f, 11.f, 12.f}));
}

test_case(math_vector_dot)
{
	vector vi1{1, 2};
	vector vi2{3, 4};

	expect_equal((dot(vi1, vi2)), 11);

	vector vf1{5.f, 6.f, 7.f};
	vector vf2{8.f, 9.f, 10.f};

	expect_equal((dot(vf1, vf2)), 164.f);

	vector<float, dynamic> vd1{-1.f, -2.f, -3.f};
	vector<float, dynamic> vd2{-4.f, -5.f, -6.f};

	expect_equal((dot(vd1, vd2)), 32.f);
}

test_case(math_vector_det)
{
	vector vi1{1, 2};
	vector vi2{3, 4};

	expect_equal((det(vi1, vi2)), -2);

	vector vf1{5.f, 6.f, 7.f};
	vector vf2{8.f, 9.f, 10.f};
	vector vf3{11.f, 12.f, 14.f};

	expect_equal((det(vf1, vf2, vf3)), -3.f);
}

test_case(math_vector_compare)
{
	vector vi1{1, 2};
	vector vi2{3, 4};

	expect_equal(vi1, vi1);
	expect_equal(vi2, vi2);
	expect_different(vi1, vi2);
	expect_different(vi2, vi1);
	expect_less(vi1, vi2);
	expect_lequal(vi1, vi2);
	expect_greater(vi2, vi1);
	expect_gequal(vi2, vi1);
}

test_case(math_vector_cast)
{
	vector pi{1, 2};
	vector pf{3.f, 4.f, 5.f};
	vector<int, dynamic> pd{-1.f, -2.f, -3.f};

	expect_equal(cast<float>(pi), (vector{1.f, 2.f}));
	expect_equal(cast<int>(pf), (vector{3, 4, 5}));
	expect_equal(cast<int>(pd), (vector<int, dynamic>{-1, -2, -3}));
}

test_case(math_vector_ort)
{
	vector v1{1.f, 2.f};
	vector v2{3.f, 4.f, 5.f};
	vector<int, dynamic> v3{-1.f, -2.f, -3.f};

	expect_small((dot(v1, ort(v1))), 1e-6);
	expect_small((dot(v2, ort(v2))), 1e-6);
	expect_small((dot(v3, ort(v3))), 1e-6);
}

test_case(math_vector_cross)
{
	vector v1{1.f, 2.f, 3.f};
	vector v2{4.f, 5.f, 6.f};

	expect_small((dot(v1, cross(v1, v2))), 1e-6);
	expect_small((dot(v2, cross(v1, v2))), 1e-6);
}

test_case(math_vector_length)
{
	vector v{5.f, 12.f};

	expect_equal(length_sqr(v), 169.f);
	expect_close(length(v), 13.f, 1e-6);
}

test_case(math_vector_lerp)
{
	vector v1{1.f, 2.f, 3.f};
	vector v2{5.f, 6.f, 7.f};

	expect_equal((lerp(v1, v2, 0.25f)), (vector{2.f, 3.f, 4.f}));
	expect_equal((lerp(v1, v2, 0.5f )), (vector{3.f, 4.f, 5.f}));
	expect_equal((lerp(v1, v2, 0.75f)), (vector{4.f, 5.f, 6.f}));
}

test_case(math_vector_slerp)
{
	vector v1{1.f, 0.f};
	vector v2{0.f, 1.f};

	for (int i = 0; i <= 9; ++i)
	{
		// 10-degree steps
		auto s = slerp(v1, v2, i / 9.f);
		expect_close((length(s)), 1.f, 1e-6);
		expect_close((angle(s)), rad(10.f * i), 1e-6);
		expect_gequal((det(v1, s)), 0.f);
		expect_gequal((det(s, v2)), 0.f);
	}
}
