#ifndef COMPONENTEDITORREGISTRY_HPP
#define COMPONENTEDITORREGISTRY_HPP
#pragma once
#include <imgui.h>
#include <flecs.h>
#include <typeindex>
#include <unordered_map>
#include <functional>
#include <memory>
#include <string>
#include <cstring>

// Forward declarations
class IComponentEditor;

/**
 * Base interface for component editors
 */
class IComponentEditor {
public:
    virtual ~IComponentEditor() = default;

    /**
     * Render the editor UI
     * @param entity The flecs entity being edited
     * @param component Pointer to the component data
     * @return true if the component was modified
     */
    virtual bool render(flecs::entity entity, void* component) = 0;

    /**
     * Get the type of component this editor handles
     * @return type_index of the component
     */
    virtual std::type_index getType() const = 0;
};

/**
 * Template base class for component editors
 * @tparam T The component type to edit
 */
template<typename T>
class ComponentEditor : public IComponentEditor {
public:
    bool render(flecs::entity entity, void* component) override {
        return renderComponent(entity, *static_cast<T*>(component));
    }

    std::type_index getType() const override {
        return std::type_index(typeid(T));
    }

protected:
    /**
     * Implement this method to render the editor for your component
     * @param entity The flecs entity being edited
     * @param component Reference to the component data
     * @return true if the component was modified
     */
    virtual bool renderComponent(flecs::entity entity, T& component) = 0;
};

/**
 * Registry for component editors
 * Manages the registration and rendering of editors for different component types
 */
class ComponentEditorRegistry {
public:
    ComponentEditorRegistry() = default;
    ~ComponentEditorRegistry() = default;

    // Delete copy constructor and assignment
    ComponentEditorRegistry(const ComponentEditorRegistry&) = delete;
    ComponentEditorRegistry& operator=(const ComponentEditorRegistry&) = delete;

    // Allow move operations
    ComponentEditorRegistry(ComponentEditorRegistry&&) = default;
    ComponentEditorRegistry& operator=(ComponentEditorRegistry&&) = default;

    /**
     * Register an editor for a specific component type using type_index
     * @param typeIndex The type_index of the component
     * @param editor The editor instance
     */
    void registerEditor(std::type_index typeIndex, std::unique_ptr<IComponentEditor> editor) {
        m_editors_by_type[typeIndex] = std::move(editor);
    }

    /**
     * Register an editor for a component type using template
     * @tparam T The component type
     * @param editor The editor instance
     */
    template<typename T>
    void registerEditor(std::unique_ptr<ComponentEditor<T>> editor) {
        m_editors_by_type[std::type_index(typeid(T))] = std::move(editor);
    }

    /**
     * Render a component editor for the given component
     * @tparam T The component type
     * @param entity The flecs entity
     * @param component Reference to the component data
     * @return true if the component was modified
     */
    template<typename T>
    bool renderComponent(flecs::entity entity, T& component) {
        auto it = m_editors_by_type.find(std::type_index(typeid(T)));
        if (it != m_editors_by_type.end()) {
            return it->second->render(entity, &component);
        }

        // If no specific editor, show a default editor
        return renderDefaultEditor(entity, component);
    }

    /**
     * Check if a component type has a registered editor
     * @tparam T The component type
     * @return true if an editor is registered
     */
    template<typename T>
    bool hasEditor() const {
        return m_editors_by_type.find(std::type_index(typeid(T))) != m_editors_by_type.end();
    }

private:
    /**
     * Default editor for components without custom editors
     * Displays raw component data as text
     */
    template<typename T>
    bool renderDefaultEditor(flecs::entity entity, T& component) {
        if (ImGui::CollapsingHeader("Component (No Editor)", ImGuiTreeNodeFlags_DefaultOpen)) {
            ImGui::Indent();

            // Get component name from type info
            const char* typeName = typeid(T).name();

            // Simple demangling for common compilers
            #ifdef _MSC_VER
            // MSVC: Remove "struct " or "class " prefix
            std::string name = typeName;
            if (name.find("struct ") == 0) name = name.substr(7);
            if (name.find("class ") == 0) name = name.substr(6);
            ImGui::Text("Type: %s", name.c_str());
            #else
            ImGui::Text("Type: %s", typeName);
            #endif

            ImGui::Text("Size: %zu bytes", sizeof(T));
            ImGui::Text("Address: %p", &component);

            // Show raw bytes option
            if (ImGui::TreeNode("Show Raw Data")) {
                unsigned char* bytes = reinterpret_cast<unsigned char*>(&component);
                for (size_t i = 0; i < sizeof(T); ++i) {
                    ImGui::Text("%02X ", bytes[i]);
                    if ((i + 1) % 16 == 0 && i + 1 < sizeof(T)) {
                        ImGui::NewLine();
                    } else if ((i + 1) % 8 == 0) {
                        ImGui::SameLine();
                        ImGui::Text(" ");
                    } else {
                        ImGui::SameLine();
                    }
                }
                ImGui::TreePop();
            }

            ImGui::Unindent();
        }
        return false;
    }

    // Store editors by C++ type
    std::unordered_map<std::type_index, std::unique_ptr<IComponentEditor>> m_editors_by_type;
};

/**
 * Helper macro to create a simple component editor
 * Use this for quick creation of editors for simple components
 */
#define CREATE_SIMPLE_EDITOR(ComponentType, ...) \
class SimpleEditor_##ComponentType : public ComponentEditor<ComponentType> { \
protected: \
    bool renderComponent(flecs::entity entity, ComponentType& comp) override { \
        bool modified = false; \
        __VA_ARGS__ \
        return modified; \
    } \
}; \
auto register_##ComponentType = []() { \
    return std::make_unique<SimpleEditor_##ComponentType>(); \
}
#endif // COMPONENTEDITORREGISTRY_HPP
