#pragma once

#include <psemek/util/to_string.hpp>
#include <psemek/util/type_name.hpp>
#include <psemek/util/function.hpp>
#include <psemek/util/range.hpp>
#include <psemek/util/hash.hpp>
#include <psemek/util/hstring.hpp>
#include <psemek/util/hash_table.hpp>
#include <psemek/util/exception.hpp>

#include <cstdint>
#include <typeindex>
#include <tuple>
#include <vector>
#include <memory>
#include <string>
#include <string_view>
#include <functional>
#include <optional>

namespace psemek::util
{

	struct ecs;

	namespace ecs_detail
	{

		using species_handle = std::uint16_t;

		// Entity id within it's species
		using entity_id = std::uint32_t;

		using entity_version = std::uint32_t;

		// From highest to lowest bits:
		//    12: species id
		//    20: entity id (within species)
		//    32: entity version
		using entity_handle = std::uint64_t;

		struct unpacked_handle
		{
			species_handle species;
			entity_id entity;
			entity_version version;
		};

		inline unpacked_handle unpack(entity_handle h)
		{
			return {
				h >> 52,
				(h >> 32) & 0xFFFFFu,
				h & 0xFFFFFFFFu
			};
		}

		inline entity_handle pack(species_handle species, entity_id id, entity_version version)
		{
			return (static_cast<entity_handle>(species) << 52) | (static_cast<entity_handle>(id) << 32) | static_cast<entity_handle>(version);
		}

		struct species_base
		{
			species_base(ecs * ecs, std::string name, species_handle id)
				: ecs_(ecs)
				, name_(std::move(name))
				, id_(id)
			{}

			std::string_view name() const { return name_; }

			virtual void * get_species_component(std::type_index component_type) = 0;
			virtual void * get_entity_component(std::type_index component_type) = 0;

			template <typename Component>
			Component * get_species_component()
			{
				return reinterpret_cast<Component *>(get_species_component(typeid(Component)));
			}

			template <typename Component>
			typename Component::data * get_entity_component()
			{
				return reinterpret_cast<typename Component::data *>(get_entity_component(typeid(Component)));
			}

			virtual entity_id entity_count() const = 0;

			virtual entity_handle add_entity() = 0;
			virtual void remove_entity(entity_handle h) = 0;

			virtual bool entity_active(entity_handle h) const = 0;

			template <typename Behavior, typename ... Args>
			void apply(Behavior & behavior, Args const & ... args)
			{
				if (!check_forbidden(behavior, 0))
					return;
				apply_impl(behavior, static_cast<typename Behavior::component_types *>(nullptr), std::make_index_sequence<std::tuple_size_v<typename Behavior::component_types>>{}, args...);
			}

			template <typename Behavior, typename ... Args>
			void apply_single(Behavior & behavior, entity_id entity, Args const & ... args)
			{
				if (!check_forbidden(behavior, 0))
					return;
				apply_single_impl(behavior, entity, static_cast<typename Behavior::component_types *>(nullptr), std::make_index_sequence<std::tuple_size_v<typename Behavior::component_types>>{}, args...);
			}

			template <typename Behavior, typename ... Args>
			void apply_species(Behavior & behavior, Args const & ... args)
			{
				if (!check_forbidden(behavior, 0))
					return;
				apply_species_impl(behavior, static_cast<typename Behavior::component_types *>(nullptr), std::make_index_sequence<std::tuple_size_v<typename Behavior::component_types>>{}, args...);
			}

			virtual ~species_base() {}

			virtual entity_id const * get_free_list() const = 0;
			virtual entity_id list_size() const = 0;

			virtual entity_version const * get_version_list() const = 0;

		protected:
			ecs * ecs_;
			std::string name_;
			species_handle id_;

			template <typename Behavior>
			bool check_forbidden(Behavior &, typename Behavior::forbidden_component_types * helper)
			{
				return check_forbidden_impl(helper);
			}

			template <typename Behavior>
			bool check_forbidden(Behavior &, ...)
			{
				return true;
			}

			template <typename ... Components>
			bool check_forbidden_impl(std::tuple<Components...> *)
			{
				return (!get_species_component<Components>() && ...);
			}

			template <typename Behavior, typename ... Components, std::size_t ... Is, typename ... Args>
			void apply_impl(Behavior & behavior, std::tuple<Components...> *, std::index_sequence<Is...>, Args const & ... args)
			{
				std::tuple<typename Components::data * ...> cptrs;

				((std::get<Is>(cptrs) = get_entity_component<Components>()), ...);

				auto all_nonzero = [](auto * ... ptrs)
				{
					return ((ptrs != nullptr) && ...);
				};

				if (!std::apply(all_nonzero, cptrs))
					return;

				typename Behavior::context ctx{*ecs_};
				ctx.species.value = id_;

				((std::get<Components *>(ctx.components) = get_species_component<Components>()), ...);

				behavior.begin({id_}, *std::get<Components *>(ctx.components)...);

				auto visit = [&](auto * ... ptrs)
				{
					if constexpr (std::is_invocable_v<Behavior, typename Components::data & ..., typename Behavior::context const &, Args const & ...>)
					{
						behavior(*ptrs..., ctx, args...);
					}
					else
					{
						behavior(*ptrs..., args...);
					}
				};

				auto increment = [](auto * & ... ptrs)
				{
					((++ptrs), ...);
				};

				auto list = get_free_list();

				if (list)
				{
					// sparse
					auto const size = list_size();
					auto version = get_version_list();
					for (entity_id i = 0; i < size; ++i)
					{
						if (*list == i)
						{
							ctx.entity.value = pack(id_, i, *version);
							ctx.remove = false;
							std::apply(visit, cptrs);
							if (ctx.remove)
								remove_entity(ctx.entity.value);
						}
						std::apply(increment, cptrs);
						++list;
						++version;
					}
				}
				else
				{
					// packed
					for (std::size_t i = 0; i < entity_count();)
					{
						ctx.entity.value = pack(id_, i, 0);
						ctx.remove = false;
						std::apply(visit, cptrs);
						if (ctx.remove)
							remove_entity(ctx.entity.value);
						else
						{
							std::apply(increment, cptrs);
							++i;
						}
					}
				}
			}

			template <typename Behavior, typename ... Components, std::size_t ... Is, typename ... Args>
			void apply_single_impl(Behavior & behavior, entity_id entity, std::tuple<Components...> *, std::index_sequence<Is...>, Args const & ... args)
			{
				std::tuple<typename Components::data * ...> cptrs;

				((std::get<Is>(cptrs) = get_entity_component<Components>()), ...);

				auto all_nonzero = [](auto * ... ptrs)
				{
					return ((ptrs != nullptr) && ...);
				};

				if (!std::apply(all_nonzero, cptrs))
					return;

				((std::get<Is>(cptrs) += entity), ...);

				auto version = get_version_list();

				typename Behavior::context ctx{*ecs_};
				ctx.species.value = id_;
				ctx.entity.value = pack(id_, entity, version ? version[entity] : 0);

				((std::get<Components *>(ctx.components) = get_species_component<Components>()), ...);

				behavior.begin({id_}, *std::get<Components *>(ctx.components)...);

				auto visit = [&](auto * ... ptrs)
				{
					if constexpr (std::is_invocable_v<Behavior, typename Components::data & ..., typename Behavior::context const &, Args const & ...>)
					{
						behavior(*ptrs..., ctx, args...);
					}
					else
					{
						behavior(*ptrs..., args...);
					}
				};

				std::apply(visit, cptrs);
			}

			template <typename Behavior, typename ... Components, std::size_t ... Is, typename ... Args>
			void apply_species_impl(Behavior & behavior, std::tuple<Components...> *, std::index_sequence<Is...>, Args const & ... args)
			{
				std::tuple<Components * ...> cptrs;

				((std::get<Is>(cptrs) = get_species_component<Components>()), ...);

				auto all_nonzero = [](auto * ... ptrs)
				{
					return ((ptrs != nullptr) && ...);
				};

				if (!std::apply(all_nonzero, cptrs))
					return;

				typename Behavior::context ctx{*ecs_};
				ctx.species.value = id_;

				auto visit = [&](auto * ... ptrs)
				{
					if constexpr (std::is_invocable_v<Behavior, Components & ..., typename Behavior::context const &, Args const & ...>)
					{
						behavior(*ptrs..., ctx, args...);
					}
					else
					{
						behavior(*ptrs..., args...);
					}
				};

				std::apply(visit, cptrs);
			}
		};

		template <typename ... Components>
		struct species_impl_base
			: species_base
		{
			template <typename ... Args>
			species_impl_base(ecs * ecs, std::string name, species_handle id, Args && ... components)
				: species_base(ecs, std::move(name), id)
				, species_components_{std::forward<Args>(components)...}
			{}

			using species_base::get_species_component;
			using species_base::get_entity_component;

			void * get_species_component(std::type_index component_type) override
			{
				return get_species_component_impl(component_type, std::make_index_sequence<sizeof...(Components)>{});
			}

			void * get_entity_component(std::type_index component_type) override
			{
				return get_entity_component_impl(component_type, std::make_index_sequence<sizeof...(Components)>{});
			}

		protected:
			std::tuple<Components...> species_components_;
			std::tuple<std::vector<typename Components::data>...> entity_components_;

			template <std::size_t ... I>
			void * get_species_component_impl(std::type_index component_type, std::index_sequence<I...>)
			{
				void * result = nullptr;

				((result = (component_type == typeid(std::tuple_element_t<I, std::tuple<Components...>>)) ? &std::get<I>(species_components_) : result), ...);
				return result;
			}

			template <std::size_t ... I>
			void * get_entity_component_impl(std::type_index component_type, std::index_sequence<I...>)
			{
				void * result = nullptr;

				((result = (component_type == typeid(std::tuple_element_t<I, std::tuple<Components...>>)) ? std::get<I>(entity_components_).data() : result), ...);
				return result;
			}
		};

		template <typename ... Components>
		struct sparse_species_impl final
			: species_impl_base<Components...>
		{
			using species_impl_base<Components...>::species_impl_base;

			static constexpr entity_id null = static_cast<entity_id>(-1);

			entity_id entity_count() const override
			{
				return entity_count_;
			}

			entity_handle add_entity() override
			{
				return add_entity_impl(std::make_index_sequence<sizeof...(Components)>{});
			}

			void remove_entity(entity_handle h) override
			{
				auto id = unpack(h).entity;
				remove_entity_impl(id, std::make_index_sequence<sizeof...(Components)>{});
				list_[id] = list_head_;
				list_head_ = id;
				destroyed_at_[id] = ++version_;
				--entity_count_;
			}

			bool entity_active(entity_handle h) const override
			{
				auto u = unpack(h);
				return list_[u.entity] == u.entity && u.version >= destroyed_at_[u.entity];
			}

			entity_id const * get_free_list() const override
			{
				return list_.data();
			}

			entity_id list_size() const override
			{
				return list_.size();
			}

			entity_version const * get_version_list() const override
			{
				return created_at_.data();
			}

		private:
			std::vector<entity_id> list_;
			entity_id list_head_ = null;
			entity_id entity_count_ = 0;
			std::vector<entity_version> created_at_;
			std::vector<entity_version> destroyed_at_;
			entity_version version_ = 0;

			template <std::size_t ... I>
			entity_handle add_entity_impl(std::index_sequence<I...>)
			{
				if (list_head_ == null)
				{
					std::size_t const old_size = list_.size();
					std::size_t const new_size = std::max<std::size_t>(16, old_size * 2);

					list_.resize(new_size);
					created_at_.resize(new_size, 0);
					destroyed_at_.resize(new_size, 0);
					for (std::size_t i = old_size; i + 1 < new_size; ++i)
						list_[i] = i + 1;
					list_[new_size - 1] = null;
					list_head_ = old_size;

					((std::get<I>(this->entity_components_).resize(new_size)), ...);
				}

				auto id = list_head_;

				((std::get<I>(this->entity_components_)[id] = {}), ...);

				list_head_ = list_[list_head_];
				list_[id] = id;
				entity_count_++;
				created_at_[id] = version_;
				return pack(this->id_, id, version_);
			}

			template <std::size_t ... I>
			void remove_entity_impl(entity_id id, std::index_sequence<I...>)
			{
				((std::get<I>(this->entity_components_)[id] = {}), ...);
			}
		};

		template <typename ... Components>
		struct packed_species_impl final
			: species_impl_base<Components...>
		{
			using species_impl_base<Components...>::species_impl_base;

			entity_id entity_count() const override
			{
				return std::get<0>(this->entity_components_).size();
			}

			entity_handle add_entity() override
			{
				return add_entity_impl(std::make_index_sequence<sizeof...(Components)>{});
			}

			void remove_entity(entity_handle h) override
			{
				remove_entity_impl(h, std::make_index_sequence<sizeof...(Components)>{});
			}

			bool entity_active(entity_handle h) const override
			{
				return unpack(h).entity < entity_count();
			}

			entity_id const * get_free_list() const override
			{
				return nullptr;
			}

			entity_id list_size() const override
			{
				return 0;
			}

			entity_version const * get_version_list() const override
			{
				return nullptr;
			}

		private:

			template <std::size_t ... I>
			entity_handle add_entity_impl(std::index_sequence<I...>)
			{
				entity_id id = entity_count();
				((std::get<I>(this->entity_components_).emplace_back()), ...);
				return pack(this->id_, id, 0);
			}

			template <std::size_t ... I>
			void remove_entity_impl(entity_handle h, std::index_sequence<I...>)
			{
				auto u = unpack(h);
				if (u.entity + 1 != entity_count())
				{
					(std::swap(std::get<I>(this->entity_components_)[u.entity], std::get<I>(this->entity_components_).back()), ...);
				}

				((std::get<I>(this->entity_components_).pop_back()), ...);
			}
		};

	}

	struct ecs
	{
		struct species_handle
		{
			ecs_detail::species_handle value;

			friend auto operator <=> (species_handle const &, species_handle const &) = default;
		};

		struct entity_handle
		{
			ecs_detail::entity_handle value;

			friend auto operator <=> (entity_handle const &, entity_handle const &) = default;
		};

		template <typename ... Components>
		struct without
		{
			using forbidden_component_types = std::tuple<Components...>;
		};

		template <typename ... Components>
		struct behavior
		{
			using component_types = std::tuple<Components...>;
			using component_ptrs = std::tuple<Components *...>;

			struct context
			{
				struct ecs & ecs;
				species_handle species;
				entity_handle entity;

				component_ptrs components;

				mutable bool remove = false;

				context(struct ecs & ecs)
					: ecs(ecs)
				{}

				template <typename Component>
				Component & get() const
				{
					return *std::get<Component *>(components);
				}
			};

			void begin(species_handle, Components const & ...) {}
		};

		template <typename ... Components>
		struct species_behavior
		{
			using component_types = std::tuple<Components...>;
			using component_ptrs = std::tuple<Components *...>;

			struct context
			{
				struct ecs & ecs;
				species_handle species;

				context(struct ecs & ecs)
					: ecs(ecs)
				{}
			};
		};

		struct species_iterator
		{
			species_iterator(ecs_detail::species_handle h)
				: h_(h)
			{}

			species_handle operator*() const
			{
				return species_handle{h_};
			}

			species_iterator & operator++()
			{
				++h_;
				return *this;
			}

			friend bool operator == (species_iterator const & it1, species_iterator const & it2)
			{
				return it1.h_ == it2.h_;
			}

			friend bool operator != (species_iterator const & it1, species_iterator const & it2)
			{
				return !(it1 == it2);
			}

		private:
			ecs_detail::species_handle h_;
		};

		enum class policy
		{
			sparse,
			packed,
		};

		template <typename ... Components>
		species_handle register_species(std::string name, policy p, Components && ... components);

		util::range<species_iterator> species() const
		{
			return {species_iterator{0}, species_iterator{species_.size()}};
		}

		std::string_view species_name(species_handle species) const { return species_[species.value]->name(); }

		std::optional<species_handle> find_species_by_name(std::string_view const & name) const;

		static species_handle entity_species(entity_handle entity) { return {ecs_detail::unpack(entity.value).species}; }

		template <typename ... Components>
		entity_handle add_entity(species_handle species, typename Components::data ... components);
		ecs_detail::entity_id entity_count(species_handle species) const;
		void remove_entity(entity_handle entity);
		bool entity_active(entity_handle entity);

		template <typename Component>
		Component & get(species_handle species);

		template <typename Component>
		Component const & get(species_handle species) const;

		template <typename Component>
		typename Component::data & get(entity_handle entity);

		template <typename Component>
		typename Component::data const & get(entity_handle entity) const;

		template <typename Component>
		Component * get_if(species_handle species);

		template <typename Component>
		Component const * get_if(species_handle species) const;

		template <typename Component>
		typename Component::data * get_if(entity_handle entity);

		template <typename Component>
		typename Component::data const * get_if(entity_handle entity) const;

		template <typename Behavior, typename ... Args>
		void apply(Behavior && behavior, Args const & ... args);

		template <typename Behavior, typename ... Args>
		void apply(Behavior && behavior, species_handle species, Args const & ... args);

		template <typename Behavior, typename ... Args>
		void apply(Behavior && behavior, entity_handle entity, Args const & ... args);

		template <typename Behavior, typename ... Args>
		void apply_species(Behavior && behavior, Args const & ... args);

		template <typename Behavior, typename ... Args>
		void apply_species(Behavior && behavior, species_handle species, Args const & ... args);

		template <typename Event, typename Processor>
		void register_processor(Processor && processor);

		template <typename Event, typename Behavior>
		void register_behavior(Behavior && behavior);

		template <typename Event, typename Behavior>
		void register_behavior(Behavior && behavior, species_handle species);

		template <typename Event, typename Behavior>
		void register_species_behavior(Behavior && behavior);

		template <typename Event, typename Behavior>
		void register_species_behavior(Behavior && behavior, species_handle species);

		template <typename Behavior>
		void register_constructor(Behavior && behavior);

		template <typename Behavior>
		void register_constructor(Behavior && behavior, species_handle species);

		template <typename Behavior>
		void register_species_constructor(Behavior && behavior);

		template <typename Behavior>
		void register_destructor(Behavior && behavior);

		template <typename Behavior>
		void register_destructor(Behavior && behavior, species_handle species);

		template <typename Event>
		void event(Event const & event);

	private:
		std::vector<std::unique_ptr<ecs_detail::species_base>> species_;
		util::hash_map<util::hstring, ecs_detail::species_handle> species_by_name_;

		util::hash_map<std::type_index, std::vector<util::function<void(void const *)>>> event_subscribers_;

		std::vector<util::function<void(species_handle)>> species_constructors_;

		std::vector<util::function<void(entity_handle)>> constructors_;
		util::hash_map<ecs_detail::species_handle, std::vector<util::function<void(entity_handle)>>> private_constructors_;

		std::vector<util::function<void(entity_handle)>> destructors_;
		util::hash_map<ecs_detail::species_handle, std::vector<util::function<void(entity_handle)>>> private_destructors_;
	};

	template <typename ... Components>
	ecs::species_handle ecs::register_species(std::string name, policy p, Components && ... components)
	{
		species_handle result{species_.size()};
		species_by_name_[name] = result.value;
		if (p == policy::sparse)
			species_.push_back(std::make_unique<ecs_detail::sparse_species_impl<std::remove_cvref_t<Components>...>>(this, std::move(name), result.value, std::forward<Components>(components)...));
		else
			species_.push_back(std::make_unique<ecs_detail::packed_species_impl<std::remove_cvref_t<Components>...>>(this, std::move(name), result.value, std::forward<Components>(components)...));
		for (auto const & ctor : species_constructors_)
			ctor(result);
		return result;
	}

	inline std::optional<ecs::species_handle> ecs::find_species_by_name(std::string_view const & name) const
	{
		if (auto it = species_by_name_.find(name); it != species_by_name_.end())
			return species_handle{it->second};
		return std::nullopt;
	}

	template <typename ... Components>
	inline ecs::entity_handle ecs::add_entity(species_handle species, typename Components::data ... components)
	{
		entity_handle entity{species_[species.value]->add_entity()};
		((get<Components>(entity) = std::move(components)), ...);
		for (auto const & ctor : constructors_)
			ctor(entity);
		for (auto const & ctor : private_constructors_[species.value])
			ctor(entity);
		return entity;
	}

	inline ecs_detail::entity_id ecs::entity_count(species_handle species) const
	{
		return species_[species.value]->entity_count();
	}

	inline void ecs::remove_entity(entity_handle entity)
	{
		for (auto const & dtor : destructors_)
			dtor(entity);
		for (auto const & dtor : private_destructors_[ecs_detail::unpack(entity.value).species])
			dtor(entity);
		species_[ecs_detail::unpack(entity.value).species]->remove_entity(entity.value);
	}

	inline bool ecs::entity_active(entity_handle entity)
	{
		return species_[ecs_detail::unpack(entity.value).species]->entity_active(entity.value);
	}

	template <typename Component>
	Component & ecs::get(species_handle species)
	{
		auto p = species_[species.value]->get_species_component<Component>();
		if (!p)
			throw util::exception(util::to_string("Component ", type_name<Component>(), " is not present in species ", species_[species.value]->name()));
		return *p;
	}

	template <typename Component>
	Component const & ecs::get(species_handle species) const
	{
		return const_cast<Component const &>(const_cast<ecs *>(this)->get<Component>(species));
	}

	template <typename Component>
	typename Component::data & ecs::get(entity_handle entity)
	{
		auto u = ecs_detail::unpack(entity.value);
		auto p = species_[u.species]->get_entity_component<Component>();
		if (!p)
			throw util::exception(util::to_string("Component ", type_name<Component>(), " is not present in species ", species_[u.species]->name()));
		return p[u.entity];
	}

	template <typename Component>
	typename Component::data const & ecs::get(entity_handle entity) const
	{
		return const_cast<typename Component::data const &>(const_cast<ecs *>(this)->get<Component>(entity));
	}

	template <typename Component>
	Component * ecs::get_if(species_handle species)
	{
		return species_[species.value]->get_species_component<Component>();
	}

	template <typename Component>
	Component const * ecs::get_if(species_handle species) const
	{
		return const_cast<typename Component::data const *>(const_cast<ecs *>(this)->get<Component>(species));
	}

	template <typename Component>
	typename Component::data * ecs::get_if(entity_handle entity)
	{
		auto u = ecs_detail::unpack(entity.value);
		auto p = species_[u.species]->get_entity_component<Component>();
		if (p)
			return p + u.entity;
		return p;
	}

	template <typename Component>
	typename Component::data const * ecs::get_if(entity_handle entity) const
	{
		return const_cast<typename Component::data const *>(const_cast<ecs *>(this)->get_if<Component>(entity));
	}

	template <typename Behavior, typename ... Args>
	void ecs::apply(Behavior && behavior, Args const & ... args)
	{
		for (auto & s : species_)
			s->apply(behavior, args...);
	}

	template <typename Behavior, typename ... Args>
	void ecs::apply(Behavior && behavior, species_handle species, Args const & ... args)
	{
		species_[species.value]->apply(behavior, args...);
	}

	template <typename Behavior, typename ... Args>
	void ecs::apply(Behavior && behavior, entity_handle entity, Args const & ... args)
	{
		auto id = ecs_detail::unpack(entity.value);
		species_[id.species]->apply_single(behavior, id.entity, args...);
	}

	template <typename Behavior, typename ... Args>
	void ecs::apply_species(Behavior && behavior, Args const & ... args)
	{
		for (auto & s : species_)
			s->apply_species(behavior, args...);
	}

	template <typename Behavior, typename ... Args>
	void ecs::apply_species(Behavior && behavior, species_handle species, Args const & ... args)
	{
		species_[species.value]->apply_species(behavior, args...);
	}

	template <typename Event, typename Processor>
	void ecs::register_processor(Processor && processor)
	{
		event_subscribers_[typeid(Event)].push_back([this, processor = std::move(processor)](void const * event) mutable {
			processor(*static_cast<Event const *>(event));
		});
	}

	template <typename Event, typename Behavior>
	void ecs::register_behavior(Behavior && behavior)
	{
		event_subscribers_[typeid(Event)].push_back([this, behavior = std::move(behavior)](void const * event) mutable {
			apply(behavior, *static_cast<Event const *>(event));
		});
	}

	template <typename Event, typename Behavior>
	void ecs::register_behavior(Behavior && behavior, species_handle species)
	{
		event_subscribers_[typeid(Event)].push_back([this, species, behavior = std::move(behavior)](void const * event) mutable {
			apply(behavior, species, *static_cast<Event const *>(event));
		});
	}

	template <typename Event, typename Behavior>
	void ecs::register_species_behavior(Behavior && behavior)
	{
		event_subscribers_[typeid(Event)].push_back([this, behavior = std::move(behavior)](void const * event) mutable {
			apply_species(behavior, *static_cast<Event const *>(event));
		});
	}

	template <typename Event, typename Behavior>
	void ecs::register_species_behavior(Behavior && behavior, species_handle species)
	{
		event_subscribers_[typeid(Event)].push_back([this, species, behavior = std::move(behavior)](void const * event) mutable {
			apply_species(behavior, species, *static_cast<Event const *>(event));
		});
	}

	template <typename Behavior>
	void ecs::register_constructor(Behavior && behavior)
	{
		constructors_.push_back([this, behavior = std::move(behavior)](entity_handle entity) mutable {
			apply(behavior, entity);
		});
	}

	template <typename Behavior>
	void ecs::register_constructor(Behavior && behavior, species_handle species)
	{
		private_constructors_[species.value].push_back([this, behavior = std::move(behavior)](entity_handle entity) mutable {
			apply(behavior, entity);
		});
	}

	template <typename Behavior>
	void ecs::register_species_constructor(Behavior && behavior)
	{
		species_constructors_.push_back([this, behavior = std::move(behavior)](species_handle species) mutable {
			apply_species(behavior, species);
		});
	}

	template <typename Behavior>
	void ecs::register_destructor(Behavior && behavior)
	{
		destructors_.push_back([this, behavior = std::move(behavior)](entity_handle entity) mutable {
			apply(behavior, entity);
		});
	}

	template <typename Behavior>
	void ecs::register_destructor(Behavior && behavior, species_handle species)
	{
		private_destructors_[species.value].push_back([this, behavior = std::move(behavior)](entity_handle entity) mutable {
			apply(behavior, entity);
		});
	}

	template <typename Event>
	void ecs::event(Event const & event)
	{
		if (auto it = event_subscribers_.find(typeid(Event)); it != event_subscribers_.end())
			for (auto const & b : it->second)
				b(std::addressof(event));
	}

}

namespace std
{

	template <>
	struct hash<::psemek::util::ecs::species_handle>
	{
		std::uint64_t operator()(::psemek::util::ecs::species_handle h) const
		{
			return std::hash<::psemek::util::ecs_detail::species_handle>()(h.value);
		}
	};

	template <>
	struct hash<::psemek::util::ecs::entity_handle>
	{
		std::uint64_t operator()(::psemek::util::ecs::entity_handle h) const
		{
			return std::hash<::psemek::util::ecs_detail::entity_handle>()(h.value);
		}
	};

}
