#include <psemek/test/test.hpp>

#include <psemek/ecs/container.hpp>
#include <psemek/ecs/declare_uuid.hpp>
#include <psemek/random/generator.hpp>
#include <psemek/random/uniform.hpp>
#include <psemek/log/log.hpp>

using namespace psemek;
using namespace psemek::ecs;

namespace
{

	struct component_1
	{
		int value;

		psemek_ecs_declare_uuid("component_1")
	};

	struct component_2
	{
		int value;

		psemek_ecs_declare_uuid("component_2")
	};

}

test_case(ecs_entity_empty_single)
{
	container container;

	auto h0 = container.create();
	expect(container.alive(h0));

	container.destroy(h0);
	expect(!container.alive(h0));
}

test_case(ecs_entity_empty_multiple)
{
	container container;

	auto h0 = container.create();
	expect(container.alive(h0));

	auto h1 = container.create();
	expect(container.alive(h1));

	auto h2 = container.create();
	expect(container.alive(h2));

	container.destroy(h1);
	expect(!container.alive(h1));

	container.destroy(h0);
	expect(!container.alive(h0));

	container.destroy(h2);
	expect(!container.alive(h2));
}

test_case(ecs_entity_components_single)
{
	container container;

	auto h0 = container.create(component_1{10}, component_2{20});
	expect(container.alive(h0));
	expect_equal(container.get(h0).get<component_1>().value, 10);
	expect_equal(container.get(h0).get<component_2>().value, 20);

	container.destroy(h0);
	expect(!container.alive(h0));

	h0 = container.create(component_2{200}, component_1{100});
	expect(container.alive(h0));
	expect_equal(container.get(h0).get<component_1>().value, 100);
	expect_equal(container.get(h0).get<component_2>().value, 200);
}

test_case(ecs_entity_components_multiple)
{
	container container;

	auto h0 = container.create(component_1{10}, component_2{20});
	expect(container.alive(h0));
	expect_equal(container.get(h0).get<component_1>().value, 10);
	expect_equal(container.get(h0).get<component_2>().value, 20);

	auto h1 = container.create(component_1{30}, component_2{40});
	expect(container.alive(h1));
	expect_equal(container.get(h1).get<component_1>().value, 30);
	expect_equal(container.get(h1).get<component_2>().value, 40);

	auto h2 = container.create(component_1{50}, component_2{60});
	expect(container.alive(h2));
	expect_equal(container.get(h2).get<component_1>().value, 50);
	expect_equal(container.get(h2).get<component_2>().value, 60);

	container.destroy(h0);
	expect(!container.alive(h0));

	container.destroy(h2);
	expect(!container.alive(h2));

	container.destroy(h1);
	expect(!container.alive(h1));
}

test_case(ecs_entity_random)
{
	random::generator rng;
	container container;

	std::vector<std::pair<handle, int>> entities;

	for (int i = 0; i < 1024 * 1024; ++i)
	{
		int value = random::uniform(rng, -1024, 1024);
		handle handle;
		if (random::uniform<bool>(rng))
			handle = container.create(component_1{value});
		else
			handle = container.create(component_1{value}, component_2{-value});
		entities.push_back({handle, value});
	}

	std::shuffle(entities.begin(), entities.end(), rng);

	while (!entities.empty())
	{
		auto entity = entities.back();
		entities.pop_back();

		expect(container.alive(entity.first));
		expect_equal(container.get(entity.first).get<component_1>().value, entity.second);

		container.destroy(entity.first);
		expect(!container.alive(entity.first));
	}
}
