#include <psemek/test/test.hpp>

#include <psemek/util/ecs.hpp>

namespace
{

	using namespace psemek::util;

	struct test_component_1
	{
		int species_value_1;

		struct data
		{
			int entity_value_1;
		};
	};

	struct test_component_2
	{
		int species_value_2;

		struct data
		{
			int entity_value_2;
		};
	};

	struct test_behavior_1
		: ecs::behavior<test_component_1>
	{
		int call_count = 0;
		int value_sum = 0;

		void operator()(test_component_1::data & c)
		{
			call_count += 1;
			value_sum += c.entity_value_1;
		}
	};

	struct test_behavior_12
		: ecs::behavior<test_component_1, test_component_2>
	{
		int call_count = 0;
		int value_sum_1 = 0;
		int value_sum_2 = 0;

		void operator()(test_component_1::data & c1, test_component_2::data & c2)
		{
			call_count += 1;
			value_sum_1 += c1.entity_value_1;
			value_sum_2 += c2.entity_value_2;
		}
	};

	struct test_behavior_species_12
		: ecs::behavior<test_component_1, test_component_2>
	{
		int expected_value_1;
		int expected_value_2;

		int call_count = 0;

		void operator()(test_component_1::data &, test_component_2::data &, context const & ctx)
		{
			expect_equal(ctx.get<test_component_1>().species_value_1, expected_value_1);
			expect_equal(ctx.get<test_component_2>().species_value_2, expected_value_2);

			++call_count;
		}
	};

}

test_case(util_ecs_species__impl_sparse_component)
{
	using namespace psemek::util::ecs_detail;

	psemek::util::ecs ecs;

	sparse_species_impl<test_component_1> species(&ecs, "species 1", 0, test_component_1{10});

	expect_different_ptr(species.get_species_component<test_component_1>(), nullptr);
	expect_equal(species.get_species_component<test_component_1>()->species_value_1, 10);
	expect_equal_ptr(species.get_species_component<test_component_2>(), nullptr);

	sparse_species_impl<test_component_2> species_2(&ecs, "species 2", 1, test_component_2{20});

	expect_equal_ptr(species_2.get_species_component<test_component_1>(), nullptr);
	expect_different_ptr(species_2.get_species_component<test_component_2>(), nullptr);
	expect_equal(species_2.get_species_component<test_component_2>()->species_value_2, 20);

	sparse_species_impl<test_component_1, test_component_2> species_12(&ecs, "species 12", 2, test_component_1{100}, test_component_2{200});

	expect_different_ptr(species_12.get_species_component<test_component_1>(), nullptr);
	expect_equal(species_12.get_species_component<test_component_1>()->species_value_1, 100);
	expect_different_ptr(species_12.get_species_component<test_component_2>(), nullptr);
	expect_equal(species_12.get_species_component<test_component_2>()->species_value_2, 200);
}

test_case(util_ecs_species__impl_packed_component)
{
	using namespace psemek::util::ecs_detail;

	psemek::util::ecs ecs;

	packed_species_impl<test_component_1> species(&ecs, "species 1", 0, test_component_1{10});

	expect_different_ptr(species.get_species_component<test_component_1>(), nullptr);
	expect_equal(species.get_species_component<test_component_1>()->species_value_1, 10);
	expect_equal_ptr(species.get_species_component<test_component_2>(), nullptr);

	packed_species_impl<test_component_2> species_2(&ecs, "species 2", 1, test_component_2{20});

	expect_equal_ptr(species_2.get_species_component<test_component_1>(), nullptr);
	expect_different_ptr(species_2.get_species_component<test_component_2>(), nullptr);
	expect_equal(species_2.get_species_component<test_component_2>()->species_value_2, 20);

	packed_species_impl<test_component_1, test_component_2> species_12(&ecs, "species 12", 2, test_component_1{100}, test_component_2{200});

	expect_different_ptr(species_12.get_species_component<test_component_1>(), nullptr);
	expect_equal(species_12.get_species_component<test_component_1>()->species_value_1, 100);
	expect_different_ptr(species_12.get_species_component<test_component_2>(), nullptr);
	expect_equal(species_12.get_species_component<test_component_2>()->species_value_2, 200);
}

test_case(util_ecs_species__impl_sparse_entity)
{
	using namespace psemek::util::ecs_detail;

	psemek::util::ecs ecs;

	sparse_species_impl<test_component_1> species(&ecs, "species", 0, test_component_1{10});

	expect_equal(species.entity_count(), 0);

	int const N = 10;

	for (std::size_t i = 0; i < N; ++i)
		species.add_entity();

	expect_equal(species.entity_count(), N);

	test_component_1::data * cptr_1 = species.get_entity_component<test_component_1>();

	expect_different(cptr_1, nullptr);

	for (std::size_t i = 0; i < N; ++i)
		cptr_1[i].entity_value_1 = i;

	for (std::size_t i = 0; i < N; ++i)
		species.add_entity();

	expect_equal(species.entity_count(), 2 * N);

	cptr_1 = species.get_entity_component<test_component_1>();

	expect_different(cptr_1, nullptr);

	for (std::size_t i = 0; i < N; ++i)
		expect_equal(cptr_1[i].entity_value_1, i);
}

test_case(util_ecs_species__impl_packed_entity)
{
	using namespace psemek::util::ecs_detail;

	psemek::util::ecs ecs;

	packed_species_impl<test_component_1> species(&ecs, "species", 0, test_component_1{10});

	expect_equal(species.entity_count(), 0);

	int const N = 10;

	for (std::size_t i = 0; i < N; ++i)
		species.add_entity();

	expect_equal(species.entity_count(), N);

	test_component_1::data * cptr_1 = species.get_entity_component<test_component_1>();

	expect_different(cptr_1, nullptr);

	for (std::size_t i = 0; i < N; ++i)
		cptr_1[i].entity_value_1 = i;

	for (std::size_t i = 0; i < N; ++i)
		species.add_entity();

	expect_equal(species.entity_count(), 2 * N);

	cptr_1 = species.get_entity_component<test_component_1>();

	expect_different(cptr_1, nullptr);

	for (std::size_t i = 0; i < N; ++i)
		expect_equal(cptr_1[i].entity_value_1, i);
}

test_case(util_ecs_entity_sparse)
{
	using namespace psemek;

	util::ecs ecs;

	auto species = ecs.register_species("species", util::ecs::policy::sparse, test_component_1{10});

	expect_equal(ecs.entity_count(species), 0);

	auto entity = ecs.add_entity(species);

	ecs.get<test_component_1>(entity).entity_value_1 = 100;

	expect_equal(ecs.get<test_component_1>(entity).entity_value_1, 100);

	expect_throw(ecs.get<test_component_2>(entity), std::exception);
}

test_case(util_ecs_entity_packed)
{
	using namespace psemek;

	util::ecs ecs;

	auto species = ecs.register_species("species", util::ecs::policy::packed, test_component_1{10});

	expect_equal(ecs.entity_count(species), 0);

	auto entity = ecs.add_entity(species);

	ecs.get<test_component_1>(entity).entity_value_1 = 100;

	expect_equal(ecs.get<test_component_1>(entity).entity_value_1, 100);

	expect_throw(ecs.get<test_component_2>(entity), std::exception);
}

test_case(util_ecs_behavior_1_sparse)
{
	using namespace psemek;

	util::ecs ecs;

	auto species = ecs.register_species("species", util::ecs::policy::sparse, test_component_1{10});

	expect_equal(ecs.entity_count(species), 0);

	int N = 100;

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species);
		ecs.get<test_component_1>(entity).entity_value_1 = i;
	}

	test_behavior_1 behavior_1;

	ecs.apply(behavior_1);

	expect_equal(behavior_1.call_count, N);
	expect_equal(behavior_1.value_sum, (N * (N - 1)) / 2);

	test_behavior_12 behavior_12;

	ecs.apply(behavior_12);

	expect_equal(behavior_12.call_count, 0);
	expect_equal(behavior_12.value_sum_1, 0);
	expect_equal(behavior_12.value_sum_2, 0);
}

test_case(util_ecs_behavior_1_packed)
{
	using namespace psemek;

	util::ecs ecs;

	auto species = ecs.register_species("species", util::ecs::policy::packed, test_component_1{10});

	expect_equal(ecs.entity_count(species), 0);

	int N = 100;

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species);
		ecs.get<test_component_1>(entity).entity_value_1 = i;
	}

	test_behavior_1 behavior_1;

	ecs.apply(behavior_1);

	expect_equal(behavior_1.call_count, N);
	expect_equal(behavior_1.value_sum, (N * (N - 1)) / 2);

	test_behavior_12 behavior_12;

	ecs.apply(behavior_12);

	expect_equal(behavior_12.call_count, 0);
	expect_equal(behavior_12.value_sum_1, 0);
	expect_equal(behavior_12.value_sum_2, 0);
}

test_case(util_ecs_behavior_1__2_sparse)
{
	using namespace psemek;

	util::ecs ecs;

	auto species_1 = ecs.register_species("species 1", util::ecs::policy::sparse, test_component_1{10});
	auto species_2 = ecs.register_species("species 2", util::ecs::policy::sparse, test_component_2{20});

	int N = 100;

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species_1);
		ecs.get<test_component_1>(entity).entity_value_1 = i;
	}

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species_2);
		ecs.get<test_component_2>(entity).entity_value_2 = -i;
	}

	test_behavior_1 behavior;

	ecs.apply(behavior);

	expect_equal(behavior.call_count, N);
	expect_equal(behavior.value_sum, (N * (N - 1)) / 2);

	test_behavior_12 behavior_12;

	ecs.apply(behavior_12);

	expect_equal(behavior_12.call_count, 0);
	expect_equal(behavior_12.value_sum_1, 0);
	expect_equal(behavior_12.value_sum_2, 0);
}

test_case(util_ecs_behavior_1__2_packed)
{
	using namespace psemek;

	util::ecs ecs;

	auto species_1 = ecs.register_species("species 1", util::ecs::policy::packed, test_component_1{10});
	auto species_2 = ecs.register_species("species 2", util::ecs::policy::packed, test_component_2{20});

	int N = 100;

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species_1);
		ecs.get<test_component_1>(entity).entity_value_1 = i;
	}

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species_2);
		ecs.get<test_component_2>(entity).entity_value_2 = -i;
	}

	test_behavior_1 behavior;

	ecs.apply(behavior);

	expect_equal(behavior.call_count, N);
	expect_equal(behavior.value_sum, (N * (N - 1)) / 2);

	test_behavior_12 behavior_12;

	ecs.apply(behavior_12);

	expect_equal(behavior_12.call_count, 0);
	expect_equal(behavior_12.value_sum_1, 0);
	expect_equal(behavior_12.value_sum_2, 0);
}

test_case(util_ecs_behavior_12_sparse)
{
	using namespace psemek;

	util::ecs ecs;

	auto species = ecs.register_species("species", util::ecs::policy::sparse, test_component_1{10}, test_component_2{20});

	int N = 100;

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species);
		ecs.get<test_component_1>(entity).entity_value_1 = i;
		ecs.get<test_component_2>(entity).entity_value_2 = -i;
	}

	test_behavior_1 behavior;

	ecs.apply(behavior);

	expect_equal(behavior.call_count, N);
	expect_equal(behavior.value_sum, (N * (N - 1)) / 2);

	test_behavior_12 behavior_12;

	ecs.apply(behavior_12);

	expect_equal(behavior_12.call_count, N);
	expect_equal(behavior_12.value_sum_1, (N * (N - 1)) / 2);
	expect_equal(behavior_12.value_sum_2, - (N * (N - 1)) / 2);
}

test_case(util_ecs_behavior_12_packed)
{
	using namespace psemek;

	util::ecs ecs;

	auto species = ecs.register_species("species", util::ecs::policy::packed, test_component_1{10}, test_component_2{20});

	int N = 100;

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species);
		ecs.get<test_component_1>(entity).entity_value_1 = i;
		ecs.get<test_component_2>(entity).entity_value_2 = -i;
	}

	test_behavior_1 behavior;

	ecs.apply(behavior);

	expect_equal(behavior.call_count, N);
	expect_equal(behavior.value_sum, (N * (N - 1)) / 2);

	test_behavior_12 behavior_12;

	ecs.apply(behavior_12);

	expect_equal(behavior_12.call_count, N);
	expect_equal(behavior_12.value_sum_1, (N * (N - 1)) / 2);
	expect_equal(behavior_12.value_sum_2, - (N * (N - 1)) / 2);
}

test_case(util_ecs_behavior_species_sparse)
{
	using namespace psemek;

	util::ecs ecs;

	auto species = ecs.register_species("species", util::ecs::policy::sparse, test_component_1{10}, test_component_2{20});

	int N = 100;

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species);
		ecs.get<test_component_1>(entity).entity_value_1 = 0;
		ecs.get<test_component_2>(entity).entity_value_2 = 0;
	}

	test_behavior_species_12 behavior;
	behavior.expected_value_1 = 10;
	behavior.expected_value_2 = 20;

	ecs.apply(behavior);

	expect_equal(behavior.call_count, N);
}

test_case(util_ecs_behavior_species_packed)
{
	using namespace psemek;

	util::ecs ecs;

	auto species = ecs.register_species("species", util::ecs::policy::packed, test_component_1{10}, test_component_2{20});

	int N = 100;

	for (int i = 0; i < N; ++i)
	{
		auto entity = ecs.add_entity(species);
		ecs.get<test_component_1>(entity).entity_value_1 = 0;
		ecs.get<test_component_2>(entity).entity_value_2 = 0;
	}

	test_behavior_species_12 behavior;
	behavior.expected_value_1 = 10;
	behavior.expected_value_2 = 20;

	ecs.apply(behavior);

	expect_equal(behavior.call_count, N);
}
