#pragma once

#include <psemek/ecs/handle.hpp>
#include <psemek/util/exception.hpp>
#include <psemek/util/to_string.hpp>
#include <psemek/util/type_name.hpp>

#include <typeindex>

namespace psemek::ecs
{

	struct component_not_found_exception
		: util::exception
	{
		component_not_found_exception(std::type_info const & type, handle const & handle, std::string const & description, util::stacktrace stacktrace = {})
			: util::exception(util::to_string("Component ", util::type_name(type), " not found for entity ", handle, " <", description, ">"), std::move(stacktrace))
			, type_(type)
			, handle_(handle)
		{}

		std::type_info const & type() const
		{
			return type_;
		}

		ecs::handle const & handle() const
		{
			return handle_;
		}

	private:
		std::type_info const & type_;
		ecs::handle handle_;
	};

	struct entity_not_cloneable
		: util::exception
	{
		entity_not_cloneable(handle const & entity, std::vector<std::type_index> non_copyable_components, util::stacktrace stacktrace = {})
			: util::exception(make_message(entity, non_copyable_components), std::move(stacktrace))
			, entity_(entity)
			, non_copyable_components_(std::move(non_copyable_components))
		{}

		handle const & entity() const
		{
			return entity_;
		}

		std::vector<std::type_index> const & non_copyable_components() const
		{
			return non_copyable_components_;
		}

	private:
		handle entity_;
		std::vector<std::type_index> non_copyable_components_;

		static std::string make_message(handle const & entity, std::vector<std::type_index> const & non_copyable_components)
		{
			std::ostringstream os;
			os << "entity " << entity << " is not cloneable because components ";
			bool first = true;
			for (auto const & type : non_copyable_components)
			{
				if (!first) os << ", ";
				first = false;
				os << util::type_name(type);
			}
			os << " are not copy constructible";
			return os.str();
		}
	};

}
