#ifndef CCOMPONENTFACTORY_HPP
#define CCOMPONENTFACTORY_HPP

#include <cstddef>
#include <memory>
#include <stdexcept>
#include <typeindex>
#include <utility>
#include "../Component/CAbstractComponent.hpp"

//TODO modifier pour la scène soit obligatoire, sans scène pas d'entité, sans entité pas de composants 
namespace CosmicCore
{
    class CEntity;
    class CComponentFactory {
    public:
        using ComponentCreator = std::function<std::shared_ptr<CAbstractComponent>(std::weak_ptr<CEntity>&, const std::vector<std::any>&)>;
        using ResourceComponentCreator = std::function<std::shared_ptr<CAbstractComponent>(const std::vector<std::any>&)>;

        static CComponentFactory& instance();
        
        static void registerStandardProvidedAbstractComponents()
        {
            //auto& ins = instance();
            //ins.registerComponent<CAbstractComponent>();//todo
        }

        template <typename T, typename... Args>
        void registerComponent() {
            creators[typeid(T)] = [](std::weak_ptr<CEntity> entity, const std::vector<std::any>& args) {
                return createComponent<T, Args...>(entity, args, std::index_sequence_for<Args...>{});
            };
        }

        template <typename T, typename... Args>
        void registerResourceComponent() {
            resourceCreators[typeid(T)] = [](const std::vector<std::any>& args) {
                return createComponent<T, Args...>(args, std::index_sequence_for<Args...>{});
            };
        }

        template <typename T>
        std::shared_ptr<T> create(std::weak_ptr<CEntity> entity, const std::vector<std::any>& params) {
            auto it = creators.find(typeid(T));
            if (it != creators.end()) {
                return std::dynamic_pointer_cast<T>(it->second(entity, params));
            }
            throw std::runtime_error("Component not registered");
        }

        template <typename T>
        std::shared_ptr<T> createResource(const std::vector<std::any>& params) {
            //ajouter au cache des resources pour réutilisation
            auto it = resourceCreators.find(typeid(T));
            if (it != resourceCreators.end()) {
                return std::dynamic_pointer_cast<T>(it->second(params));
            }
            throw std::runtime_error("Resource Component not registered: " + std::string(typeid(T).name()));
        }

        std::unordered_map<std::type_index, ComponentCreator>& getRegisteredComponents(){return creators;};

        std::unordered_map<std::type_index, ResourceComponentCreator>& getRegisteredResourceComponents(){return resourceCreators;};

    private:
        std::unordered_map<std::type_index, ComponentCreator> creators;
        std::unordered_map<std::type_index, ResourceComponentCreator> resourceCreators;


        template <typename T, typename... Args, std::size_t... Is>
        static std::shared_ptr<T> createComponent(std::weak_ptr<CEntity>& entity, const std::vector<std::any>& args,
                                                         std::index_sequence<Is...>) {
            try {
                return std::make_shared<T>(entity, std::any_cast<Args>(args[Is])...);
            } catch (const std::bad_any_cast& e) {
                throw std::runtime_error("Type mismatch in component arguments: " + std::string(e.what()));
            }
        }
        template <typename T, typename... Args, std::size_t... Is>
        static std::shared_ptr<T> createComponent(const std::vector<std::any>& args,
                                                         std::index_sequence<Is...>) {
            try {
                return std::make_shared<T>(std::any_cast<Args>(args[Is])...);
            } catch (const std::bad_any_cast& e) {
                throw std::runtime_error("Type mismatch in component arguments: " + std::string(e.what()));
            }
        }

    };

    extern CComponentFactory& globalComponentFactory;
}


#endif
