//
//  ExtensionRegistration.hpp
//  SwitchboardSDK
//

#pragma once

// This header is intentionally free of any other switchboard_core includes beyond what the
// macro's expansion needs to compile; extensions already include ExtensionManager.hpp (or a
// header that does) wherever they define their `load()` method.

#if defined(_WIN32)
#define SWITCHBOARD_EXTENSION_ANCHOR_EXPORT __declspec(dllexport)
#else
#define SWITCHBOARD_EXTENSION_ANCHOR_EXPORT
#endif

#define SWITCHBOARD_EXTENSION_CONCAT_IMPL(a, b) a##b
#define SWITCHBOARD_EXTENSION_CONCAT(a, b) SWITCHBOARD_EXTENSION_CONCAT_IMPL(a, b)

// Defined by switchboard_add_extension() as a compile definition equal to the extension's own
// CMake target name (e.g. SwitchboardSileroVAD). Falls back to a placeholder so this header
// still compiles (with a non-unique anchor symbol) if included outside that build path.
#ifndef SWITCHBOARD_EXTENSION_TARGET_NAME
#define SWITCHBOARD_EXTENSION_TARGET_NAME UnknownSwitchboardExtension
#endif

/**
 * @brief Registers an Extension subclass with the ExtensionManager automatically, as soon as
 *        this extension's shared library is loaded into the process — whether it was linked
 *        directly into the consuming binary/app, or dlopen'd at runtime via ExtensionLoader.
 *
 *        Place exactly once per extension, in its main `*Extension.cpp` file, after the
 *        `ClassName` definition (or after including its header):
 *
 *            SWITCHBOARD_REGISTER_EXTENSION(SileroVADExtension)
 *
 *        This removes the need for every consumer (sample apps, tests, per-platform loader
 *        shims) to hand-write a `SileroVADExtension::load()` call at startup — that was the
 *        single largest source of "forgot to load an extension" bugs. `ClassName::load()`
 *        itself is unchanged and still exists (and remains safe to call manually — it's
 *        idempotent via ExtensionManager::registerExtension's own dedupe check), so this is a
 *        drop-in addition, not a breaking change to the extension's public API.
 *
 *        Requires `ClassName` to have a `static void load()` method that performs the actual
 *        registration (every extension already defines one).
 *
 * @note Consumers that statically link this extension must link it via
 *       `switchboard_link_extensions()` (CMake/SwitchboardSDK.cmake), not a bare
 *       `target_link_libraries()`, or the library can be silently dropped from the final
 *       binary's load list on Windows (MSVC only imports a DLL whose import lib actually has a
 *       referenced symbol) — see that function's doc comment for the full explanation.
 */
#define SWITCHBOARD_REGISTER_EXTENSION(ClassName) \
    namespace { \
    struct SWITCHBOARD_EXTENSION_CONCAT(ClassName, AutoRegistrar) { \
        SWITCHBOARD_EXTENSION_CONCAT(ClassName, AutoRegistrar)() { \
            ClassName::load(); \
        } \
    }; \
    static const SWITCHBOARD_EXTENSION_CONCAT(ClassName, AutoRegistrar) SWITCHBOARD_EXTENSION_CONCAT( \
        ClassName, \
        AutoRegistrarInstance \
    ); \
    } \
    SWITCHBOARD_REGISTER_EXTENSION_PLATFORM_HOOKS()

#if !defined(SWITCHBOARD_WEB)
// `sb_extension_load` is kept exported (now an intentional no-op — the registrar above already
// ran registration) purely so ExtensionLoader::loadExtensionLibrary's dlopen/dlsym contract, and
// its "is this a valid Switchboard extension" check, keep working unmodified for extensions that
// are dlopen'd at runtime instead of linked at build time.
//
// `sb_extension_anchor_<TargetName>` exists only to be referenced by switchboard_link_extensions()
// via MSVC's `/INCLUDE` linker switch, forcing this extension's DLL into the consumer's import
// table on Windows even though nothing in the consumer's C++ source calls anything from it.
#define SWITCHBOARD_REGISTER_EXTENSION_PLATFORM_HOOKS() \
    extern "C" void sb_extension_load() {} \
    extern "C" SWITCHBOARD_EXTENSION_ANCHOR_EXPORT void \
        SWITCHBOARD_EXTENSION_CONCAT(sb_extension_anchor_, SWITCHBOARD_EXTENSION_TARGET_NAME)() {}
#else
#define SWITCHBOARD_REGISTER_EXTENSION_PLATFORM_HOOKS()
#endif
