#ifndef EDITSCENE_COMPONENTREGISTRY_HPP
#define EDITSCENE_COMPONENTREGISTRY_HPP
#pragma once
#include <typeindex>
#include <unordered_map>
#include <memory>
#include <functional>
#include <vector>
#include <set>
#include <string>
#include <flecs.h>
#include "ComponentEditor.hpp"

/**
 * Function type for adding a component to an entity
 */
using ComponentAdder = std::function<void(flecs::entity)>;

/**
 * Function type for removing a component from an entity
 */
using ComponentRemover = std::function<void(flecs::entity)>;

/**
 * Function type for checking if an entity has a component
 */
using ComponentChecker = std::function<bool(flecs::entity)>;

/**
 * Registry for component editors and their add/remove functions
 */
class ComponentRegistry {
public:
	struct ComponentInfo {
		const char *name;
		const char *group;  // Group name for menu organization (nullptr = "Miscellaneous")
		std::unique_ptr<IComponentEditor> editor;
		ComponentAdder adder;
		ComponentRemover remover;
		ComponentChecker checker;  // Checks if entity has this component
	};

	ComponentRegistry() = default;
	~ComponentRegistry() = default;

	// Delete copy
	ComponentRegistry(const ComponentRegistry &) = delete;
	ComponentRegistry &operator=(const ComponentRegistry &) = delete;

	// Allow move
	ComponentRegistry(ComponentRegistry &&) = default;
	ComponentRegistry &operator=(ComponentRegistry &&) = default;

	/**
	 * Register a component type with its editor and add/remove functions
	 * @param name Display name for the component
	 * @param group Group name for menu organization (nullptr for "Miscellaneous")
	 * @param editor The editor for this component
	 * @param adder Function to add the component to an entity
	 * @param remover Function to remove the component from an entity
	 */
	template<typename T>
	void registerComponent(const char *name, const char *group,
		               std::unique_ptr<ComponentEditor<T>> editor,
		               ComponentAdder adder, ComponentRemover remover)
	{
		ComponentInfo info;
		info.name = name;
		info.group = group ? group : "Miscellaneous";
		info.editor = std::move(editor);
		info.adder = adder;
		info.remover = remover;
		info.checker = [](flecs::entity e) { return e.has<T>(); };
		m_components[std::type_index(typeid(T))] = std::move(info);
	}
	
	/**
	 * Backward-compatible overload (defaults to "Miscellaneous" group)
	 */
	template<typename T>
	void registerComponent(const char *name,
		               std::unique_ptr<ComponentEditor<T>> editor,
		               ComponentAdder adder, ComponentRemover remover)
	{
		registerComponent<T>(name, nullptr, std::move(editor), adder, remover);
	}

	/**
	 * Check if a component type is registered
	 */
	template<typename T>
	bool hasComponent() const
	{
		return m_components.find(std::type_index(typeid(T))) !=
		       m_components.end();
	}

	/**
	 * Render a component editor
	 */
	template<typename T>
	bool render(flecs::entity entity, T &component)
	{
		auto it = m_components.find(std::type_index(typeid(T)));
		if (it != m_components.end() && it->second.editor) {
			return it->second.editor->render(entity, &component);
		}
		return false;
	}

	/**
	 * Add a component to an entity
	 */
	template<typename T>
	void addComponent(flecs::entity entity)
	{
		auto it = m_components.find(std::type_index(typeid(T)));
		if (it != m_components.end() && it->second.adder) {
			it->second.adder(entity);
		}
	}

	/**
	 * Remove a component from an entity
	 */
	template<typename T>
	void removeComponent(flecs::entity entity)
	{
		auto it = m_components.find(std::type_index(typeid(T)));
		if (it != m_components.end() && it->second.remover) {
			it->second.remover(entity);
		}
	}

	/**
	 * Check if entity has a specific component type
	 */
	bool entityHasComponent(flecs::entity entity, const std::type_index &type) const
	{
		auto it = m_components.find(type);
		if (it != m_components.end() && it->second.checker) {
			return it->second.checker(entity);
		}
		return false;
	}

	/**
	 * Add a component by type_index
	 */
	void addComponentByType(flecs::entity entity, const std::type_index &type)
	{
		auto it = m_components.find(type);
		if (it != m_components.end() && it->second.adder) {
			it->second.adder(entity);
		}
	}

	/**
	 * Remove a component by type_index
	 */
	void removeComponentByType(flecs::entity entity, const std::type_index &type)
	{
		auto it = m_components.find(type);
		if (it != m_components.end() && it->second.remover) {
			it->second.remover(entity);
		}
	}

	/**
	 * Get component name for a type
	 */
	const char *getComponentName(const std::type_index &type) const
	{
		auto it = m_components.find(type);
		if (it != m_components.end()) {
			return it->second.name;
		}
		return "Unknown";
	}

	/**
	 * Get all registered component types
	 */
	std::vector<std::type_index> getRegisteredTypes() const
	{
		std::vector<std::type_index> types;
		for (const auto &pair : m_components) {
			types.push_back(pair.first);
		}
		return types;
	}

	/**
	 * Iterate over all registered components
	 */
	template<typename Func>
	void forEach(Func&& func) const
	{
		for (const auto& pair : m_components) {
			func(pair.first, pair.second);
		}
	}

	/**
	 * Get the number of registered components
	 */
	size_t getComponentCount() const { return m_components.size(); }
	
	/**
	 * Get all unique group names
	 */
	std::vector<std::string> getGroups() const {
		std::set<std::string> groups;
		for (const auto& pair : m_components) {
			groups.insert(pair.second.group);
		}
		return std::vector<std::string>(groups.begin(), groups.end());
	}
	
	/**
	 * Get all component types in a specific group
	 */
	std::vector<std::type_index> getComponentsInGroup(const char* group) const {
		std::vector<std::type_index> types;
		const char* groupName = group ? group : "Miscellaneous";
		for (const auto& pair : m_components) {
			if (std::string(pair.second.group) == groupName) {
				types.push_back(pair.first);
			}
		}
		return types;
	}
	
	/**
	 * Iterate over all registered components in a specific group
	 */
	template<typename Func>
	void forEachInGroup(const char* group, Func&& func) const {
		const char* groupName = group ? group : "Miscellaneous";
		for (const auto& pair : m_components) {
			if (std::string(pair.second.group) == groupName) {
				func(pair.first, pair.second);
			}
		}
	}

private:
	std::unordered_map<std::type_index, ComponentInfo> m_components;
};

#endif // EDITSCENE_COMPONENTREGISTRY_HPP
