#include "ANSLIB.h"
#include "ANSLibsLoader.h"
#include <memory>
#include <mutex>
#define LOAD_FUNC(name) \
    name##Func = (name##FuncT)GetProcAddress(dllHandle, #name); \
    if (!name##Func) { success = false; }
namespace ANSCENTER
{
    namespace {
        // Function-pointer typedefs for the ANSLicensingSystem C-facade exports.
        // Resolved exactly once during PreloadSharedDllsOnce(); after that the
        // pointers are write-once globals — concurrent reads are race-free.
        using ANSLogger_LogFnT      = void(*)(const char*, const char*, const char*, int);
        using ANSCENTER_IsDbgEnFnT  = int (*)(void);
        using ANSCENTER_DebugLogFnT = void(*)(const char*);
        ANSLogger_LogFnT      g_ANSLogger_LogInfo            = nullptr;
        ANSLogger_LogFnT      g_ANSLogger_LogError           = nullptr;
        ANSLogger_LogFnT      g_ANSLogger_LogFatal           = nullptr;
        ANSCENTER_IsDbgEnFnT  g_ANSCENTER_IsDebugViewEnabled = nullptr;
        ANSCENTER_DebugLogFnT g_ANSCENTER_DebugLog           = nullptr;

        // Pre-load all ANSCORE DLLs from the Shared folder using full paths.
        // This ensures the correct versions are loaded regardless of PATH order
        // (e.g., DLHUB_Runtime_Engine may contain older copies on some machines).
        // Run exactly once per process so LoadLibrary refcounts don't grow per
        // ANSLIB instance. The DLLs are intentionally pinned for process lifetime.
        void PreloadSharedDllsOnce() {
            static std::once_flag s_preloadFlag;
            std::call_once(s_preloadFlag, []() {
                const char* sharedDir = "C:\\ProgramData\\ANSCENTER\\Shared\\";
                const char* preloadDlls[] = {
                    "ANSLicensingSystem.dll",
                    "anslicensing.dll",
                    "ANSMOT.dll",
                    "ANSODEngine.dll",
                    nullptr
                };
                for (int i = 0; preloadDlls[i] != nullptr; i++) {
                    std::string fullPath = std::string(sharedDir) + preloadDlls[i];
                    LoadLibraryA(fullPath.c_str());
                }
                // Ensure all shared DLLs (OpenCV, OpenVINO, TRT, ORT) are pre-loaded
                ANSCENTER::ANSLibsLoader::Initialize();

                // Resolve logging C-facade symbols from the (now-pinned) licensing DLL.
                // Use GetModuleHandleA — the DLL is already in this process's module
                // list courtesy of the LoadLibraryA above, so refcount is unchanged.
                if (HMODULE lic = GetModuleHandleA("ANSLicensingSystem.dll")) {
                    g_ANSLogger_LogInfo            = (ANSLogger_LogFnT)     GetProcAddress(lic, "ANSLogger_LogInfo");
                    g_ANSLogger_LogError           = (ANSLogger_LogFnT)     GetProcAddress(lic, "ANSLogger_LogError");
                    g_ANSLogger_LogFatal           = (ANSLogger_LogFnT)     GetProcAddress(lic, "ANSLogger_LogFatal");
                    g_ANSCENTER_IsDebugViewEnabled = (ANSCENTER_IsDbgEnFnT) GetProcAddress(lic, "ANSCENTER_IsDebugViewEnabled");
                    g_ANSCENTER_DebugLog           = (ANSCENTER_DebugLogFnT)GetProcAddress(lic, "ANSCENTER_DebugLog");
                }
            });
        }
    }

    ANSLIB::ANSLIB() {
        PreloadSharedDllsOnce();

        const char* dllPath = "C:\\ProgramData\\ANSCENTER\\Shared\\ANSODEngine.dll";
        dllHandle = LoadLibraryA(dllPath);
        if (!dllHandle) {
            return;
        }
        bool success = true;
        LOAD_FUNC(CreateANSODHandle_CS);
        LOAD_FUNC(RunInference_CPP);
        LOAD_FUNC(RunInferenceComplete_CPP);
        LOAD_FUNC(OptimizeModelStr_CS);
        LOAD_FUNC(GetEngineType);
        LOAD_FUNC(LoadModelFromFolder);
        LOAD_FUNC(GetActiveRect);
        LOAD_FUNC(DetectMovement);
		LOAD_FUNC(Optimize);
        LOAD_FUNC(GetODParameters);
        LOAD_FUNC(ReleaseANSODHandle);
        LOAD_FUNC(UpdateDetectionMinScore);
        LOAD_FUNC(SetPrompt);
        LOAD_FUNC(SetTracker);
        LOAD_FUNC(SetTrackerParameters);
        loaded = success;
        if (!loaded) {
            FreeLibrary(dllHandle);
            dllHandle = nullptr;
        }
    }

    ANSLIB* ANSLIB::Create() {
        try {
            auto ptr = std::make_unique<ANSLIB>();
            return ptr.release();
        }
        catch (...) { return nullptr; }
    }

    void ANSLIB::Destroy(ANSLIB* instance) {
        std::unique_ptr<ANSLIB> ptr(instance);
    }

    ANSLIB::~ANSLIB() noexcept {
        try {
            if (ANSHandle && ReleaseANSODHandleFunc) {
                ReleaseANSODHandleFunc(&ANSHandle);
                ANSHandle = nullptr;
            }
            if (dllHandle) {
                FreeLibrary(dllHandle);
                dllHandle = nullptr;
            }
        }
        catch (...) {}
    }

    bool ANSLIB::IsLoaded() const {
        return loaded;
    }

    // Public APIs
    int ANSLIB::Initialize(const char* licenseKey,
        const char* modelFilePath,
        const char* modelFileZipPassword,
        float modelThreshold,
        float modelConfThreshold,
        float modelNMSThreshold,
        int modelType,
        int detectionType, int loadEngineOnCreation,std::string& labels)
    {
        if (!loaded) {
            return -1;
        }
        if (CreateANSODHandle_CSFunc) {
            if (ANSHandle && ReleaseANSODHandleFunc) {
                ReleaseANSODHandleFunc(&ANSHandle);
                ANSHandle = nullptr;
            }
            const char* result = CreateANSODHandle_CSFunc(&ANSHandle, licenseKey, modelFilePath, modelFileZipPassword,
                modelThreshold, modelConfThreshold, modelNMSThreshold,
                1, modelType, detectionType, loadEngineOnCreation);
            labels = (result != nullptr) ? std::string(result) : std::string();
            if (labels.empty()) {
                return -1;
            }
            else {
                return 1;
            }
        }
        return 0;
    }

    int ANSLIB::RunInference(cv::Mat cvImage, const char* cameraId, std::vector<ANSCENTER::Object>& detectionResult) {
        if (!loaded) {
            return -1;
        }
        if (RunInference_CPPFunc) {
            try {
                cv::Mat* rawPtr = &cvImage;
                int result = RunInference_CPPFunc(&ANSHandle, &rawPtr, cameraId, detectionResult);
                return result;
            }
            catch (...) {
                return -1;
            }
        }
        return -1;
    }
    int ANSLIB::RunInferenceWithOptions(cv::Mat cvImage, const char* cameraId, const char* activeROIMode, std::vector<ANSCENTER::Object>& detectionResult) {
        if (!loaded) {
            return -1;
        }
        if (RunInferenceComplete_CPPFunc) {
            try {
                cv::Mat* rawPtr = &cvImage;
                int result = RunInferenceComplete_CPPFunc(&ANSHandle, &rawPtr, cameraId, activeROIMode,detectionResult);
                return result;
            }
            catch (...) {
                return -1;
            }
        }
        return -1;
    }
    int ANSLIB::OptimizeModel(const char* modelFilePath, const char* modelFileZipPassword, int modelType, int modelDetectionType,int fp16) {
        if (!loaded) {
            return -1;
        }
        if (OptimizeModelStr_CSFunc) {
            const char* result = OptimizeModelStr_CSFunc(modelFilePath, modelFileZipPassword, modelType, modelDetectionType, fp16);
            if (result) {
                return 1;
            }
            else {
                return -1;
            }
        }
        return -1;
    }

    const char* ANSLIB::CreateANSODHandle_CS(void** Handle, const char* licenseKey, const char* modelFilePath,
        const char* modelFileZipPassword, float modelThreshold, float modelConfThreshold,
        float modelNMSThreshold, int autoDetectEngine, int modelType, int detectionType, int loadEngineOnCreation) {
        return CreateANSODHandle_CSFunc ? CreateANSODHandle_CSFunc(Handle, licenseKey, modelFilePath, modelFileZipPassword,
            modelThreshold, modelConfThreshold, modelNMSThreshold,
            autoDetectEngine, modelType, detectionType, loadEngineOnCreation)
            : "Function not loaded";
    }

    const char* ANSLIB::OptimizeModelStr_CS(const char* modelFilePath, const char* modelFileZipPassword, int modelType, int modelDetectionType,int fp16) {
        return OptimizeModelStr_CSFunc ? OptimizeModelStr_CSFunc(modelFilePath, modelFileZipPassword, modelType, modelDetectionType,fp16)
            : "Function not loaded";
    }

    int ANSLIB::RunInference_CPP(void** Handle, cv::Mat** cvImage, const char* cameraId, std::vector<ANSCENTER::Object>& detectionResult) {
        return RunInference_CPPFunc ? RunInference_CPPFunc(Handle, cvImage, cameraId, detectionResult) : -1;
    }
    int ANSLIB::RunInferenceComplete_CPP(void** Handle, cv::Mat** cvImage, const char* cameraId, const char* activeROIMode, std::vector<ANSCENTER::Object>& detectionResult) {
        return RunInferenceComplete_CPPFunc ? RunInferenceComplete_CPPFunc(Handle, cvImage, cameraId, activeROIMode, detectionResult) : -1;
    }
    int ANSLIB::GetEngineType() {
        if (!loaded || !GetEngineTypeFunc) return -1;
        return GetEngineTypeFunc();
    }

    int ANSLIB::LoadModelFromFolder(const char* licenseKey, const char* modelName, const char* className,
        float detectionScoreThreshold, float modelConfThreshold, float modelMNSThreshold,
        int autoDetectEngine, int modelType, int detectionType, int loadEngineOnCreation, const char* modelFolder,std::string& labelMap)
    {
        if (!loaded || !LoadModelFromFolderFunc) return -1;
        if (ANSHandle && ReleaseANSODHandleFunc) {
            ReleaseANSODHandleFunc(&ANSHandle);
            ANSHandle = nullptr;
        }
        return LoadModelFromFolderFunc(&ANSHandle, licenseKey, modelName, className,
            detectionScoreThreshold, modelConfThreshold, modelMNSThreshold,
            autoDetectEngine, modelType, detectionType, loadEngineOnCreation,modelFolder, labelMap);
    }

    int ANSLIB::GetActiveRect(cv::Mat cvImage, cv::Rect& activeWindow) {
        if (!loaded || !GetActiveRectFunc) return -1;
        return GetActiveRectFunc(&ANSHandle, cvImage, activeWindow);
    }

    int ANSLIB::DetectMovement(cv::Mat image, const char* cameraId, std::vector<Object>& results) {
        if (!loaded || !DetectMovementFunc) return -1;
        return DetectMovementFunc(&ANSHandle, image, cameraId, results);
    }
    int ANSLIB::ReleaseANSODHandle(void** Handle) {
        return ReleaseANSODHandleFunc ? ReleaseANSODHandleFunc(Handle) : -1;
    }
    cv::Rect ANSLIB::GetActiveWindow(cv::Mat cvImage) {
		cv::Rect activeWindow;
		if (GetActiveRect(cvImage, activeWindow) == 0) {
			return activeWindow;
		}
		return cv::Rect(); // Return an empty rectangle if failed
    }
    int ANSLIB::GetODParameters(ANSCENTER::Params& param) {
        if (!loaded || !GetODParametersFunc) return -1;
        return GetODParametersFunc(&ANSHandle, param);
    }
    int ANSLIB::Optimize(bool fp16) {
        if (!loaded || !OptimizeFunc) return -1;
        return OptimizeFunc(&ANSHandle, fp16);
    }
    int ANSLIB::UpdateDetectionMinScore(float scoreThreshold) {
        if (!loaded || !UpdateDetectionMinScoreFunc) return -1;
		return UpdateDetectionMinScoreFunc(&ANSHandle, scoreThreshold);
    }
    int ANSLIB::SetPrompt(const char* text) {
        if (!loaded || !SetPromptFunc) return -1;
        return SetPromptFunc(&ANSHandle, text);
    }
    int ANSLIB::SetTracker(int trackerType, int enableTracker) {
        if (!loaded || !SetTrackerFunc) return -1;
        return SetTrackerFunc(&ANSHandle, trackerType, enableTracker);
    }
    int ANSLIB::SetTrackerParameters(const char* trackerParams) {
        if (!loaded || !SetTrackerParametersFunc) return -1;
        return SetTrackerParametersFunc(&ANSHandle, trackerParams);
    }

    // ------------------------------------------------------------------------
    // Static logging API. Each call ensures the DLL/symbols are resolved (cheap
    // after the first call — std::call_once degenerates to an atomic load) then
    // forwards to the C facade. Null-pointer guard makes missing symbols a
    // silent no-op rather than a crash.
    // ------------------------------------------------------------------------
    int ANSLIB::IsDebugViewEnabled() {
        PreloadSharedDllsOnce();
        return g_ANSCENTER_IsDebugViewEnabled ? g_ANSCENTER_IsDebugViewEnabled() : 0;
    }
    void ANSLIB::DebugLog(const char* preformattedLine) {
        PreloadSharedDllsOnce();
        if (!preformattedLine) return;
        if (g_ANSCENTER_DebugLog) {
            // Preferred path: licensing DLL applies the prefix scrubber and emits.
            g_ANSCENTER_DebugLog(preformattedLine);
            return;
        }
        // Fallback for environments where ANSLicensingSystem.dll is missing:
        // emit raw to DebugView + stderr without filtering. Better than dropping.
        OutputDebugStringA(preformattedLine);
        std::fputs(preformattedLine, stderr);
        std::fflush(stderr);
    }
    void ANSLIB::LogInfo(const char* src, const char* msg, const char* file, int line) {
        PreloadSharedDllsOnce();
        if (g_ANSLogger_LogInfo) g_ANSLogger_LogInfo(src, msg, file, line);
    }
    void ANSLIB::LogError(const char* src, const char* msg, const char* file, int line) {
        PreloadSharedDllsOnce();
        if (g_ANSLogger_LogError) g_ANSLogger_LogError(src, msg, file, line);
    }
    void ANSLIB::LogFatal(const char* src, const char* msg, const char* file, int line) {
        PreloadSharedDllsOnce();
        if (g_ANSLogger_LogFatal) g_ANSLogger_LogFatal(src, msg, file, line);
    }
}