#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>

using namespace psemek;
using namespace psemek::ecs;

namespace
{

	struct component_small
	{
		int value;

		psemek_ecs_declare_uuid("component_small")
	};

	struct component_big
	{
		int values[16];

		psemek_ecs_declare_uuid("component_big")
	};

	struct component_noncopyable
	{
		std::unique_ptr<int> value;

		psemek_ecs_declare_uuid("component_noncopyable")
	};

	struct component_counter
	{
		std::shared_ptr<int> value;

		psemek_ecs_declare_uuid("component_counter")
	};

}

test_case(ecs_component_order)
{
	container container;

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

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

	auto h2 = container.create(component_small{30});
	expect(container.alive(h0));
	expect(container.alive(h1));
	expect(container.alive(h2));
	expect_equal(container.get(h0).get<component_small>().value, 10);
	expect_equal(container.get(h1).get<component_small>().value, 20);
	expect_equal(container.get(h2).get<component_small>().value, 30);
}

test_case(ecs_component_noncopyable)
{
	container container;

	auto h0 = container.create(component_noncopyable{std::make_unique<int>(10)});
	expect(container.alive(h0));
	expect_equal(*container.get(h0).get<component_noncopyable>().value, 10);

	auto h1 = container.create(component_noncopyable{std::make_unique<int>(20)});
	expect(container.alive(h0));
	expect(container.alive(h1));
	expect_equal(*container.get(h0).get<component_noncopyable>().value, 10);
	expect_equal(*container.get(h1).get<component_noncopyable>().value, 20);

	container.destroy(h0);
	expect(!container.alive(h0));
	expect(container.alive(h1));
	expect_equal(*container.get(h1).get<component_noncopyable>().value, 20);
}

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

	std::vector<handle> entities;

	for (int i = 0; i < 1024 * 1024; ++i)
		entities.push_back(container.create(component_counter{std::make_shared<int>(42)}));

	container.apply<component_counter>([&](component_counter const & component){
		expect_equal(component.value.use_count(), 1);
	});

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

	for (int i = 0; i < entities.size() / 2; ++i)
		container.destroy(entities[i]);

	container.apply<component_counter>([&](component_counter const & component){
		expect_equal(component.value.use_count(), 1);
	});
}
