#ifndef WORLD_COMPONENT_REGISTRY_HPP_
#define WORLD_COMPONENT_REGISTRY_HPP_

#include <memory>
#include <cstddef>
#include <functional>
#include <vector>

#include <WorldComponent/WorldComponent.hpp>
#include <AtomicDataStore/AtomicDataStore.hpp>
#include <Utilities/Utilities.hpp>

/**
 * @brief The registry of world components.
 */
class WorldComponentRegistry
{
private:
    typedef std::function<std::unique_ptr<WorldComponent>(AtomicDataStore &manager, const std::string &baseDir)> Factory;
    
    std::vector<Factory> components;

public:
    template<typename TComponent>
    void registerComponent()
    {
        size_t index = components.size();
        TComponent::compIndex = index;
        components.push_back([index](AtomicDataStore &manager, const std::string &baseDir)
        {
            size_t colonPos = TComponent::compName.find(':');
            std::string path = formatString(
                "$+/comp_$+.$+.dat",
                baseDir,
                TComponent::compName.substr(0, colonPos),
                TComponent::compName.substr(colonPos+1)
            );

            std::vector<uint8_t> bytes;
            manager.read(path, bytes);

            std::unique_ptr<TComponent> result = std::make_unique<TComponent>(bytes.data(), bytes.size());
            result->dataFilePath = path;
            result->nodeComponentIndex = index;
            return result;
        });
    }

    /**
     * @brief Register all components.
     */
    void registerAllComponents();

    /**
     * @brief Load components into the vector.
     * 
     * @param manager The world data manager from which we will load.
     * @param baseDir The base directory of the node.
     * @param outComps The vector of components to output to.
     */
    void loadComponents(
        AtomicDataStore &manager,
        const std::string &baseDir,
        std::vector<std::unique_ptr<WorldComponent>> &outComps
    );
};

extern WorldComponentRegistry worldComponentRegistry;

#endif