#include <gtest/gtest.h>
#include "doorkeeper/components/tilecoords.hpp"
#include <limits>

TEST(increment, tilecoords) {
	typedef dk::TileCoords<2> tcoords2;
	typedef dk::TileCoords<2>::coords coords2;
	typedef dk::TileCoords<5> tcoords5;
	typedef dk::TileCoords<5>::coords coords5;

	{
		const coords2 max_coords(150, 200);
		tcoords2 test(max_coords);
		EXPECT_EQ(tcoords2(coords2(0), max_coords), test);

		for (auto z = coords2(0).x(); z <= max_coords.x(); ++z) {
			EXPECT_EQ(tcoords2(coords2(z, 0), max_coords), test);
			++test;
		}
		auto test_old = test++;
		EXPECT_EQ(tcoords2(coords2(1, 1), max_coords), test);
		EXPECT_EQ(tcoords2(coords2(0, 1), max_coords), test_old);
	}

	{
		const coords5 max_coords(3);
		tcoords5 test(max_coords);
		for (int z = 0; z < 4 * 4 * 4 * 4 * 4 - 1; ++z) {
			++test;
		}
		EXPECT_EQ(tcoords5(max_coords, max_coords), test);
	}
}

TEST(advance, tilecoords) {
	typedef dk::TileCoords<4> tcoords4;
	typedef dk::TileCoords<4>::coords coords4;

	{
		const coords4 max_coords(3);
		tcoords4 test(max_coords);

		EXPECT_EQ(0, test[0]);
		EXPECT_EQ(0, test[1]);
		EXPECT_EQ(0, test[2]);
		EXPECT_EQ(0, test[3]);

		test += 3;
		EXPECT_EQ(3, test[0]);
		EXPECT_EQ(0, test[1]);
		EXPECT_EQ(0, test[2]);
		EXPECT_EQ(0, test[3]);

		test += 3;
		EXPECT_EQ(2, test[0]);
		EXPECT_EQ(1, test[1]);
		EXPECT_EQ(0, test[2]);
		EXPECT_EQ(0, test[3]);

		test += 20;
		EXPECT_EQ(2, test[0]);
		EXPECT_EQ(2, test[1]);
		EXPECT_EQ(1, test[2]);
		EXPECT_EQ(0, test[3]);
	}

	const auto max_scalar = std::numeric_limits<dk::CoordinateScalarType>::max();
	ASSERT_EQ(std::numeric_limits<int32_t>::max(), max_scalar);
	{
		coords4 max_coords(max_scalar - 10);
		max_coords[1] -= 1;
		max_coords[2] -= 2;
		max_coords[3] -= 3;

		tcoords4 test(coords4(540, 800, 1200, 710), max_coords);
		test += max_scalar;
		EXPECT_EQ(549, test[0]);
		EXPECT_EQ(801, test[1]);
		EXPECT_EQ(1200, test[2]);
		EXPECT_EQ(710, test[3]);
	}

	{
		const coords4 max_coords(max_scalar - 1, 800, max_scalar - 1, max_scalar - 1);

		tcoords4 test(coords4(max_scalar - 1, 800, 1200, 710), max_coords);
		test += max_scalar;
		EXPECT_EQ(2147483646, test[0]);
		EXPECT_EQ(0, test[1]);
		EXPECT_EQ(1201, test[2]);
		EXPECT_EQ(710, test[3]);
	}
}

TEST(back_forward, tilecoords) {
	typedef dk::TileCoords<4> tcoords4;
	typedef dk::TileCoords<4>::coords coords4;

	{
		const coords4 max_coords(32);
		tcoords4 test(max_coords);

		test += 33 * 33 * 2;
		EXPECT_EQ(0, test[0]);
		EXPECT_EQ(0, test[1]);
		EXPECT_EQ(2, test[2]);
		EXPECT_EQ(0, test[3]);

		test += -1;
		EXPECT_EQ(32, test[0]);
		EXPECT_EQ(32, test[1]);
		EXPECT_EQ(1, test[2]);
		EXPECT_EQ(0, test[3]);

		test++;
		test += -33 * 33 * 2;
		EXPECT_EQ(tcoords4(max_coords), test);
	}

	{
		const coords4 max_coords(5300, 2499, 12631, 171244);
		tcoords4 test(max_coords);

		test += 5000000;
		EXPECT_EQ(coords4(1157, 943, 0, 0), test.position());

		test -= 3350000;
		EXPECT_EQ(coords4(1389, 311, 0, 0), test.position());

		test += 50000000 - 5000000 + 3350000;
		EXPECT_EQ(coords4(968, 1932, 3, 0), test.position());

		test -= 900;
		EXPECT_EQ(coords4(68, 1932, 3, 0), test.position());

		test += 5300 + 1;
		EXPECT_EQ(coords4(68, 1933, 3, 0), test.position());

		test -= -5302;
		EXPECT_EQ(coords4(69, 1934, 3, 0), test.position());
	}
}

TEST(to_index, tilecoords) {
	typedef dk::TileCoords<2> tcoords2;
	typedef dk::TileCoords<2>::coords coords2;

	{
		const coords2 max_coords(1000);
		tcoords2 test(max_coords);

		EXPECT_EQ(0, dk::to_index(test));

		++test;
		EXPECT_EQ(1, dk::to_index(test));

		--test;
		EXPECT_EQ(0, dk::to_index(test));

		test += 1;
		EXPECT_EQ(1, dk::to_index(test));

		test += 1;
		EXPECT_EQ(2, dk::to_index(test));

		test += 1501;
		EXPECT_EQ(1503, dk::to_index(test));
	}
}
