#include <scene/3d/spatial.h>
#define MAX_ENTITIES 1000
#define MAX_COMPONENTS 16

class NewWorld: public Spatial {
	GDCLASS(NewWorld, Spatial)
	class EntityManager {
		List<int> available_entities;
		int entity_count;
		uint32_t signatures[MAX_ENTITIES];
	public:
		EntityManager()
		{
			int e;
			for (e = 0; e < MAX_ENTITIES; e++) {
				available_entities.push_back(e);
				signatures[e] = 0U;
			}
			entity_count = 0;
		}
		int create_entity()
		{
			int ret;
			if (entity_count >= MAX_ENTITIES || available_entities.size() == 0)
				return -1;
			ret = available_entities.front()->get();
			available_entities.pop_front();
			entity_count++;
			return ret;
		}
		void destroy_entity(int e)
		{
			if (e >= MAX_ENTITIES)
				return;
			entity_count--;
			available_entities.push_back(e);
			signatures[e] = 0U;
		}
		inline void set_signature(int e, uint32_t sig)
		{
			signatures[e] = sig;
		}
		inline uint32_t get_signature(int e)
		{
			return signatures[e];
		}
	};
	class IComponentArray {
	public:
		virtual ~IComponentArray() = default;
		virtual void destroyed(int entity) = 0;
	};
	template <typename T>
	class ComponentArray: public IComponentArray {
		Vector<T> component_array;
		HashMap<int, size_t> entity_to_index;
		HashMap<size_t, int> index_to_entity;
		size_t size;
	public:
		void insert_data(int entity, T component)
		{
			if (entity_to_index.has(entity))
				return;
			size_t new_index = size;
			entity_to_index[entity] = new_index;
			index_to_entity[new_index] = entity;
			component_array[new_index] = component;
			size++;
		}
		void remove_data(int entity)
		{
			if (!entity_to_index.has(entity))
				return;
			size_t removed_index = entity_to_index[entity];
			size_t last_index = size - 1;

			component_array[removed_index] = component_array[last_index];

			int last_entity = index_to_entity[last_index];
			entity_to_index[last_entity] = removed_index;
			index_to_entity[removed_index] = last_entity;
			entity_to_index.erase(entity);
			index_to_entity.erase(last_index);
			size--;
		}
		T *get(int entity)
		{
			if (!entity_to_index.has(entity)) {
				return NULL;
			}
			return &component_array[entity_to_index[entity]];
		}
		void destroyed(int entity) override
		{
			remove_data(entity);
		}
	};
#define __REFLECT_NAME(N) #N
	class ComponentManager {
		HashMap<const char *, int> component_types{};
		HashMap<const char *, IComponentArray *> component_arrays{};
		int next_component_type{};
		template <typename T>
		ComponentArray<T> *get_component_array()
		{
			const char* type_name = __REFLECT_NAME(T);
			if (!component_types.has(type_name))
				return NULL;
			return static_cast<ComponentArray<T> >(component_arrays[type_name]);
		}
	public:
		template<typename T>
		void register_component()
		{
			const char* type_name = __REFLECT_NAME(T);
			component_types[type_name] = next_component_type;
			component_arrays[type_name] = memnew(ComponentArray<T>());
			next_component_type++;
		}
		template<typename T>
		int get_component_type()
		{
			const char* type_name = __REFLECT_NAME(T);
			return component_types[type_name] *
				component_types.has(type_name) -
				 1 * (!component_types.has(type_name));
		}
		template<typename T>
		void add_component(int entity, T component)
		{
			get_component_array<T>()->insert_data(entity, component);
		}
		template<typename T>
		void remove_component(int entity)
		{
			get_component_array<T>()->remove(entity);
		}
		template<typename T>
		T &get_component(int entity)
		{
			get_component_array<T>()->get_data(entity);
		}
		void destroyed(int entity)
		{
			const char * const *e = component_arrays.next(NULL);
			while (e) {
				component_arrays[*e]->destroyed(entity);
				e = component_arrays.next(e);
			}
		}

	};
	class System {
	public:
		Vector<uint32_t> entities;
	};
	class SystemManager {
		HashMap<const char *, System *> systems;
		HashMap<const char *, uint32_t> signatures;
	public:
		template<typename T>
		T *register_system()
		{
			const char* type_name = __REFLECT_NAME(T);
			T * system = memnew(T);
			systems[type_name] = system;
			return system;
		}
		template<typename T>
		void set_signature(uint32_t signature)
		{
			const char* type_name = __REFLECT_NAME(T);
			signatures[type_name] = signature;
		}
		void destroyed(int entity)
		{
			const char * const * e = systems.next(NULL);
			while (e) {
				systems[*e]->entities.erase(entity);
				e = systems.next(e);
			}
		}
		void signature_changed(int entity, uint32_t signature)
		{
			const char * const * e = systems.next(NULL);
			while (e) {
				if ((signature & signatures[*e]) == signatures[*e])
					systems[*e]->entities.push_back(entity);
				else
					systems[*e]->entities.erase(entity);
				e = systems.next(e);
			}
		}
	};
	ComponentManager cman;
	EntityManager eman;
	SystemManager sman;
};

