// ============================================================================
// ANSLibsLoader.cpp — Master orchestrator for all dynamic library loading
//
// Coordinates sub-loaders in the correct dependency order:
//   1. DLL search path injection (shared directory)
//   2. OpenCV (CvLoader) — pre-loads opencv_world + companions
//   3. OpenVINO (OvLoader) — pre-loads TBB + openvino.dll
//   4. NVIDIA stack (NvDynLoader) — TRT, cuDNN, CUDA
//   5. ONNX Runtime (EPLoader) — with execution provider selection
//
// Shutdown releases everything in reverse order.
// ============================================================================

#include "ANSLibsLoader.h"
#include "DynLibUtils.h"
#include "CvLoader.h"
#include "OvLoader.h"
#include "NvDynLoader.h"
#include "EPLoader.h"

#include <iostream>
#include <mutex>

namespace ANSCENTER {

// ─────────────────────────────────────────────────────────────────────────────
// Static member definitions
// ─────────────────────────────────────────────────────────────────────────────
bool ANSLibsLoader::s_initialized = false;

static std::mutex g_masterMutex;

// ─────────────────────────────────────────────────────────────────────────────
// Initialize
// ─────────────────────────────────────────────────────────────────────────────
void ANSLibsLoader::Initialize(const std::string& shared_dir, EngineType preferred)
{
    std::lock_guard<std::mutex> lock(g_masterMutex);
    if (s_initialized) return;

    std::cout << "==========================================================\n"
              << "ANSLibsLoader: Initializing all dynamic libraries...\n"
              << "  Shared dir  : " << shared_dir << "\n"
              << "  Engine pref : " << static_cast<int>(preferred) << "\n"
              << "==========================================================" << std::endl;

    // ── STEP 1: Inject shared dir into search path ───────────────────────────
    // This must happen before any DLL loading so all libraries in the shared
    // directory are discoverable.
    DynLib::InjectDllSearchPath(shared_dir);

    // ── STEP 2: Pre-load OpenCV ──────────────────────────────────────────────
    // Must happen before consuming DLLs (ANSODEngine etc.) are loaded, because
    // those are import-linked against opencv_world and need the DLL available.
    CvLoader::Initialize(shared_dir);

    // ── STEP 3: Pre-load OpenVINO + TBB ──────────────────────────────────────
    // OpenVINO is used directly (ov::Core) by many projects (ANSODEngine,
    // ANSLPR, ANSFR, etc.).  Pre-loading ensures the DLLs from the shared
    // directory are resolved before any import-linked consumer is loaded.
    OvLoader::Initialize(shared_dir);

    // ── STEP 4: Load NVIDIA stack ────────────────────────────────────────────
    // Only attempt if the preferred engine is NVIDIA or AUTO_DETECT.
    if (preferred == EngineType::NVIDIA_GPU ||
        preferred == EngineType::AUTO_DETECT)
    {
        NvDynLoader::Initialize();
    }

    // ── STEP 5: Load ONNX Runtime + select execution provider ────────────────
    EPLoader::Initialize(shared_dir, preferred);

    s_initialized = true;

    std::cout << "==========================================================\n"
              << "ANSLibsLoader: Initialization complete.\n"
              << "==========================================================" << std::endl;
}

// ─────────────────────────────────────────────────────────────────────────────
// Shutdown
// ─────────────────────────────────────────────────────────────────────────────
void ANSLibsLoader::Shutdown()
{
    std::lock_guard<std::mutex> lock(g_masterMutex);

    // Shut down in reverse initialization order.
    // Each sub-loader is idempotent and safe to call even if not initialized.
    EPLoader::Shutdown();
    NvDynLoader::Shutdown();
    OvLoader::Shutdown();
    CvLoader::Shutdown();

    s_initialized = false;
}

// ─────────────────────────────────────────────────────────────────────────────
// IsInitialized
// ─────────────────────────────────────────────────────────────────────────────
bool ANSLibsLoader::IsInitialized()
{
    return s_initialized;
}

} // namespace ANSCENTER
