//
//  ExtensionManager.hpp
//  SwitchboardSDK
//
//  Created by Balazs Kiss on 30/08/2024.
//

#pragma once

#include <switchboard/export.h>
#include <switchboard_core/Extension.hpp>
#include <map>
#include <string>
#include <vector>

namespace switchboard {

class ExtensionLoader;

/**
 * @brief Prefix applied to the registered name of a dynamically loaded extension, so a client can
 *        tell it from a compiled-in one. Strip it for display, but note the prefixed name is what
 *        initializeExtension, deinitializeExtension and the `loadExtension` action take.
 */
constexpr const char* DYNAMIC_EXTENSION_NAME_PREFIX = "dynamic-";

/**
 * @brief Manages the extensions of the Switchboard SDK.
 */
class SWITCHBOARDSDK_EXPORT ExtensionManager final {
public:
    /**
     * @brief Gets the shared instance of the ExtensionManager.
     */
    static ExtensionManager& getInstance();

    /**
     * @brief Registers an extension.
     *
     * A dynamically loaded extension is registered under DYNAMIC_EXTENSION_NAME_PREFIX + its name.
     * Its own getName() is unchanged, so the two differ for the rest of its life.
     *
     * @param extension The extension to register.
     */
    void registerExtension(std::shared_ptr<Extension> extension);

    /**
     * @brief Initializes an extension with the given configuration.
     *
     * @param extensionName The name of the extension to initialize.
     * @param config The configuration parameters for the extension.
     * 
     * @return Result<void> Success if the extension was initialized, error otherwise.
     */
    Result<void> initializeExtension(const std::string& extensionName, const SBAnyMap& config);

    /**
     * @brief Deinitializes an extension.
     *
     * @param extensionName The name of the extension to deinitialize.
     *
     * @return Result<void> Success if the extension was deinitialized, error otherwise.
     */
    Result<void> deinitializeExtension(const std::string& extensionName);

    /**
     * @brief Deinitializes all registered extensions.
     */
    void deinitializeAllExtensions();

    /**
     * @brief Returns the names of all currently registered (loaded) extensions,
     *        both compiled-in and dynamically loaded, in sorted order.
     *
     * @return The registered extension names.
     */
    std::vector<std::string> getExtensionNames() const;

private:
    // Only the loader may open the window below.
    friend class ExtensionLoader;

    /** @brief Opens the window in which registrations come from a library. Not reentrant. */
    void beginDynamicExtensionLoad();

    /** @brief Closes the window. @return The prefixed name registered, empty if none was. */
    std::string endDynamicExtensionLoad();

    static ExtensionManager* sharedInstance;

    std::map<std::string, std::shared_ptr<Extension>> extensions;

    bool dynamicExtensionLoadInProgress = false;
    std::string nameRegisteredDuringLoad;
};

}
