#ifndef ANSLIB_H
#define ANSLIB_H
#define ANSLIB_API __declspec(dllexport)
#define CUSTOM_API __declspec(dllexport)
#pragma once
#include <windows.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/cuda.hpp>
#include <cstdio>
enum DetectionType {
	CLASSIFICATION = 0,
	DETECTION = 1,
	SEGMENTATION = 2,
	FACEDETECTOR = 3,
	FACERECOGNIZER = 4,
	LICENSEPLATE = 5,
	TEXTSCENSE = 6,
	KEYPOINT = 7,
	OBB = 8 // Oriented Bounding Box
};
enum ModelType {
    TENSORFLOW = 0,
    YOLOV4 = 1,
    YOLOV5 = 2,
    YOLOV8 = 3,
    TENSORRT = 4,
    OPENVINO = 5,
    FACEDETECT = 6,
    FACERECOGNIZE = 7,
    ALPR = 8,
    OCR = 9,
    ANOMALIB = 10,
    POSE = 11,
    SAM = 12,
    ODHUBMODEL = 13,
    YOLOV10RTOD = 14, // TensorRT Object Detection for Yolov10
    YOLOV10OVOD = 15, // OpenVINO Object Detection for Yolov10
    CUSTOMDETECTOR = 16, // Custom Detector
    YOLOV12 = 17, // YoloV12 standard for yolov12
    CUSTOMPY = 18, // Custom Python script model
    MOTIONDETECTOR = 19, // Motion Detector,
    ONNXCL = 20,
    ONNXPOSE = 21,
    RTPOSE = 22,
    ONNXSEG = 23,
    RTSEG = 24,
    ONNXOBB = 25,
    RTOBB = 26,
    MOVIENET = 27,
    ONNXSAM3 = 28,
    RTSAM3 = 29,
    ONNXYOLO = 30,
    RTYOLO = 31
};
enum TrackerType {
    BYTETRACK = 0,
    UCMC = 1,
    OCSORT = 2
};
namespace ANSCENTER {
    struct Point {
        int x, y;
    };
    struct ROIConfig {
        bool Rectangle;
        bool Polygon;
        bool Line;
        int MinItems;
        int MaxItems;
        std::string Name;
        std::string ROIMatch;
    };

    struct Parameter {
        std::string Name;
        std::string DataType;
        int NoOfDecimals;
        int MaxValue;
        int MinValue;
        std::string StartValue;
        std::vector<std::string> ListItems;
        std::string DefaultValue;
        std::string Value;
    };

    struct ROIValue {
        std::string ROIMatch;
        std::vector<Point> ROIPoints;
        std::string Option;
        std::string Name;
        int OriginalImageSize;
    };
    struct Params {
        std::vector<ROIConfig> ROI_Config;
        std::vector<std::string> ROI_Options;
        std::vector<Parameter> Parameters;
        std::vector<ROIValue> ROI_Values;
    };

    /* Example
    {
  "ROI_Config":[
    {
      "Rectangle":true,
      "Polygon":true,
      "Line":false,
      "MinItems":0,
      "MaxItems":3,
      "Name":"Traffic Light",
      "ROI-Match":"All Corners"
    },
    {
      "Rectangle":true,
      "Polygon":false,
      "Line":false,
      "MinItems":1,
      "MaxItems":1,
      "Name":"Car Zone",
      "ROI-Match":"All Corners"
    },
    {
      "Rectangle":false,
      "Polygon":false,
      "Line":true,
      "MinItems":1,
      "MaxItems":2,
      "Name":"Cross Line",
      "ROI-Match":"All Corners"
    }
  ],
  "ROI_Options":[
    "Inside ROI",
    "Inside ROI",
    "Both Directions"
  ],
  "Parameters":[
    {
      "Name":"Para1",
      "DataType":"Boolean",
      "NoOfdecimals":0,
      "MaxValue":0,
      "MinValue":0,
      "StartValue":"",
      "ListItems":[],
      "DefaultValue":"",
      "Value":"true"
    },
    {
      "Name":"Para2",
      "DataType":"Integer",
      "NoOfdecimals":0,
      "MaxValue":5,
      "MinValue":1,
      "StartValue":"2",
      "ListItems":[],
      "DefaultValue":"",
      "Value":"3"
    },
    {
      "Name":"Para3",
      "DataType":"List-Single",
      "NoOfdecimals":0,
      "MaxValue":0,
      "MinValue":0,
      "StartValue":"",
      "ListItems":["A","B","C"],
      "DefaultValue":"",
      "Value":"A"
    },
    {
      "Name":"Para4",
      "DataType":"Range",
      "NoOfdecimals":0,
      "MaxValue":100,
      "MinValue":50,
      "StartValue":">,60",
      "ListItems":[">","<"],
      "DefaultValue":"",
      "Value":">,52.000000"
    }
  ],
  "ROI_Values":[
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[
        {"x":269,"y":134},
        {"x":777,"y":134},
        {"x":777,"y":457},
        {"x":269,"y":457}
      ],
      "Option":"Inside ROI",
      "Name":"Car Zone 1",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[{"x":280,"y":613},{"x":1108,"y":280}],
      "Option":"Above",
      "Name":"Cross Line 1",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[{"x":1511,"y":383},{"x":1283,"y":754}],
      "Option":"Left side",
      "Name":"Cross Line 2",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[
        {"x":229,"y":161},
        {"x":964,"y":161},
        {"x":964,"y":628},
        {"x":229,"y":628}
      ],
      "Option":"Left side",
      "Name":"Traffic Light 1",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[
        {"x":1115,"y":304},
        {"x":1730,"y":304},
        {"x":1730,"y":695},
        {"x":1115,"y":695}
      ],
      "Option":"Left side",
      "Name":"Traffic Light 2",
      "OriginalImageSize":1920
    },
    {
      "ROI-Match":"Centre Point",
      "ROIPoints":[
        {"x":678,"y":683},
        {"x":1217,"y":683},
        {"x":1217,"y":1026},
        {"x":678,"y":1026}
      ],
      "Option":"Left side",
      "Name":"Traffic Light 3",
      "OriginalImageSize":1920
    }
  ]
}
    */
	struct Object
	{
		int classId{ 0 };
		int trackId{ 0 };
		std::string className{};
		float confidence{ 0.0 };
		cv::Rect box{};
		std::vector<cv::Point2f> polygon;    // Polygon that contain x1,y1,x2,y2,x3,y3,x4,y4
		cv::Mat mask{};                      // Face image in box (cropped) or mask image for segmentation
		cv::cuda::GpuMat gpuMask{};          // GPU-resident face crop (set by NV12 affine warp, avoids re-upload)
		std::vector<float> kps{};            // Pose exsimate 17 keypoints or oriented bouding box (xcenter, ycenter, width, height, angle)
		std::string extraInfo;               // More information such as facial recognition
		std::string cameraId;                // Use to check if this object belongs to any camera
		//std::string attributes;            // Attributes such as keypoint string
	};
	class ANSLIB_API ANSLIB {
    public:
        ANSLIB();
        ~ANSLIB() noexcept;
        ANSLIB(const ANSLIB&) = delete;
        ANSLIB& operator=(const ANSLIB&) = delete;
        ANSLIB(ANSLIB&&) = delete;
        ANSLIB& operator=(ANSLIB&&) = delete;
		[[nodiscard]] static ANSLIB* Create();
		static void Destroy(ANSLIB* instance);
		[[nodiscard]] int 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);
		[[nodiscard]] int RunInference(cv::Mat cvImage, const char* cameraId, std::vector<ANSCENTER::Object>& detectionResult);
        [[nodiscard]] int RunInferenceWithOptions(cv::Mat cvImage, const char* cameraId, const char* activeROIMode, std::vector<ANSCENTER::Object>& detectionResult);
		[[nodiscard]] int OptimizeModel(const char* modelFilePath, const char* modelFileZipPassword, int modelType, int modelDetectionType,int fp16);
		[[nodiscard]] int Optimize(bool fp16); // Perform optimization on the loaded model on current model folder
		[[nodiscard]] int GetEngineType();
		[[nodiscard]] int 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);
		[[nodiscard]] int DetectMovement(cv::Mat image, const char* cameraId, std::vector<Object>& results);
		[[nodiscard]] cv::Rect GetActiveWindow(cv::Mat cvImage);
		[[nodiscard]] int SetPrompt(const char* text);
		[[nodiscard]] int SetTracker(int trackerType, int enableTracker);
		[[nodiscard]] int SetTrackerParameters(const char* trackerParams);

        // Logging — process-wide, thread-safe, no instance required.
        // Routes to SPDLogger inside ANSLicensingSystem.dll via dynamically-resolved
        // C exports. Silently no-ops if the DLL or symbols are unavailable.
        static void LogInfo (const char* src, const char* msg, const char* file = "", int line = 0);
        static void LogError(const char* src, const char* msg, const char* file = "", int line = 0);
        static void LogFatal(const char* src, const char* msg, const char* file = "", int line = 0);

        // DebugView gate. Wraps ANSCENTER_IsDebugViewEnabled() — non-zero when
        // logging should be emitted (env var ANSCENTER_DBGVIEW or sentinel file
        // C:\ProgramData\ANSCENTER\ansvisdebugview.txt). Cheap on the hot path.
        // Used by the ANSLIB_DBG macro below; can also be called directly to
        // gate expensive log-prep work.
        static int IsDebugViewEnabled();

        // DebugView emit. Hands a pre-formatted "[Tag] body\n" line to
        // ANSCENTER_DebugLog inside the licensing DLL, which strips noise path
        // prefixes (e.g. C:\ProgramData\ANSCENTER\, C:\ProgramData\<profile>\)
        // before writing to OutputDebugStringA + stderr. Used by ANSLIB_DBG.
        // If the licensing DLL is unavailable, falls back to raw emit (no
        // filtering) so messages aren't lost.
        static void DebugLog(const char* preformattedLine);
    private:
        HMODULE dllHandle = nullptr;
        bool loaded = false;
        void* ANSHandle = nullptr;
        const char* 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);
        int RunInference_CPP(void** Handle, cv::Mat** cvImage, const char* cameraId, std::vector<ANSCENTER::Object>& detectionResult);
        int RunInferenceComplete_CPP(void** Handle, cv::Mat** cvImage, const char* cameraId, const char* activeROIMode,std::vector<ANSCENTER::Object>& detectionResult);
        const char* OptimizeModelStr_CS(const char* modelFilePath, const char* modelFileZipPassword, int modelType, int modelDetectionType,int fp16);
        int ReleaseANSODHandle(void** Handle);
		int GetActiveRect(cv::Mat cvImage, cv::Rect& activeWindow);
		int GetODParameters(ANSCENTER::Params& param);
		int UpdateDetectionMinScore(float scoreThreshold);
        [[nodiscard]] bool IsLoaded() const;

        typedef const char* (*CreateANSODHandle_CSFuncT)(void**, const char*, const char*, const char*, float, float, float, int, int, int, int);
        typedef const char* (*OptimizeModelStr_CSFuncT)(const char*, const char*, int, int, int);
        typedef int (*RunInference_CPPFuncT)(void**, cv::Mat**, const char*, std::vector<ANSCENTER::Object>& detectionResult);
        typedef int (*RunInferenceComplete_CPPFuncT)(void**, cv::Mat**, const char*, const char*, std::vector<ANSCENTER::Object>& detectionResult);
        typedef int (*ReleaseANSODHandleFuncT)(void**);
		typedef int(*GetEngineTypeFuncT)();
		typedef int(*LoadModelFromFolderFuncT)(void**, const char*, const char*, const char*, float, float, float, int, int, int,int, const char*,std::string&);
		typedef int(*GetActiveRectFuncT)(void**, cv::Mat, cv::Rect&);
		typedef int(*DetectMovementFuncT)(void**, cv::Mat, const char*, std::vector<ANSCENTER::Object>&);
		typedef int(*OptimizeFuncT)(void**, bool);
        typedef int(*GetODParametersFuncT)(void**, ANSCENTER::Params&);
        typedef int(*UpdateDetectionMinScoreFuncT)(void**, float);  // Add 'Func' to match other typedefs
        typedef int(*SetPromptFuncT)(void**, const char*);
        typedef int(*SetTrackerFuncT)(void**, int, int);
        typedef int(*SetTrackerParametersFuncT)(void**, const char*);

        CreateANSODHandle_CSFuncT CreateANSODHandle_CSFunc = nullptr;
        OptimizeModelStr_CSFuncT OptimizeModelStr_CSFunc = nullptr;
        ReleaseANSODHandleFuncT ReleaseANSODHandleFunc = nullptr;
        RunInference_CPPFuncT RunInference_CPPFunc = nullptr;
        RunInferenceComplete_CPPFuncT RunInferenceComplete_CPPFunc = nullptr;
		GetEngineTypeFuncT GetEngineTypeFunc = nullptr;
		LoadModelFromFolderFuncT LoadModelFromFolderFunc = nullptr;
		GetActiveRectFuncT GetActiveRectFunc = nullptr;
		DetectMovementFuncT DetectMovementFunc = nullptr;
		OptimizeFuncT OptimizeFunc = nullptr;
        GetODParametersFuncT GetODParametersFunc = nullptr;
        UpdateDetectionMinScoreFuncT UpdateDetectionMinScoreFunc = nullptr;
        SetPromptFuncT SetPromptFunc = nullptr;
        SetTrackerFuncT SetTrackerFunc = nullptr;
        SetTrackerParametersFuncT SetTrackerParametersFunc = nullptr;
    };
}

struct CustomObject
{
    int classId{ 0 };
    int trackId{ 0 };
    std::string className{};
    float confidence{ 0.0 };
    cv::Rect box{};
    std::vector<cv::Point2f> polygon;    // Polygon that contain x1,y1,x2,y2,x3,y3,x4,y4
    cv::Mat mask{};                      // Face image in box (cropped) or mask image for segmentation
    cv::cuda::GpuMat gpuMask{};          // GPU-resident face crop (set by NV12 affine warp, avoids re-upload)
    std::vector<float> kps{};            // Pose exsimate 17 keypoints or oriented bouding box (xcenter, ycenter, width, height, angle)
    std::string extraInfo;               // More information such as facial recognition
    std::string cameraId;                // Use to check if this object belongs to any camera
    //std::string attributes;            // Attributes such as keypoint string
};
struct CustomPoint { int x, y; };
struct CustomROIConfig {
    bool Rectangle;
    bool Polygon;
    bool Line;
    int MinItems;
    int MaxItems;
    std::string Name;
    std::string ROIMatch;
};
struct CustomParameter {
    std::string Name;
    std::string DataType;
    int NoOfDecimals;
    int MaxValue;
    int MinValue;
    std::string StartValue;
    std::vector<std::string> ListItems;
    std::string DefaultValue;
    std::string Value;
};
struct CustomROIValue {
    std::string ROIMatch;
    std::vector<CustomPoint> ROIPoints;
    std::string Option;
    std::string Name;
    int OriginalImageSize;
};
struct CustomParams {
    std::vector<CustomROIConfig> ROI_Config;
    std::vector<std::string> ROI_Options;
    std::vector<CustomParameter> Parameters;
    std::vector<CustomROIValue> ROI_Values;
};
class CUSTOM_API IANSCustomClass
{
protected:
    std::string _modelDirectory; // The directory where the model is located
    float _detectionScoreThreshold{ 0.5 };
    CustomParams _params; // Parameters for the model
    bool _loadEngineOnCreate{ false }; // Load engine on create
public:
    virtual bool Initialize(const std::string& modelDirectory, float detectionScoreThreshold, std::string& labelMap) = 0;
    virtual bool OptimizeModel(bool fp16) = 0;
    virtual std::vector<CustomObject> RunInference(const cv::Mat& input) = 0;
    virtual std::vector<CustomObject> RunInference(const cv::Mat& input, const std::string& camera_id) = 0;
    virtual bool ConfigureParameters(CustomParams& param) = 0;
    virtual bool Destroy() = 0;
    bool SetParameters(const CustomParams& param) {
        try {
            this->_params.ROI_Config.clear();
            for (auto& cf : param.ROI_Config) {
                this->_params.ROI_Config.push_back(cf);
            }
            this->_params.ROI_Options.clear();
            for (auto& op : param.ROI_Options) {
                this->_params.ROI_Options.push_back(op);
            }
            this->_params.Parameters.clear();
            for (auto& par : param.Parameters) {
                this->_params.Parameters.push_back(par);
            }
            this->_params.ROI_Values.clear();
            for (auto& roi : param.ROI_Values) {
                this->_params.ROI_Values.push_back(roi);
            }
            return true;
        }
        catch (...) {
            return false;
        }
    };
    void SetLoadEngineOnCreate(bool loadEngineOnCreate) {
        this->_loadEngineOnCreate = loadEngineOnCreate;
    }
};

// ANSLIB_DBG: DebugView logging macro mirroring ANS_DBG. Both the gate and the
// emit path are dynamic-loaded inside ANSLIB.dll — consumers don't need to
// link or even know about ANSLicensingSystem.dll. Output goes to BOTH
// OutputDebugStringA (DebugView) and stderr, identical format to ANS_DBG:
// "[tag] formatted\n".
//
// Noise path prefixes (e.g. C:\ProgramData\ANSCENTER\, C:\ProgramData\<profile>\)
// are stripped centrally inside ANSCENTER_DebugLog before emit, so log lines
// like "loaded from C:\ProgramData\Jh7O7nUe7vS\Models\Foo" appear as
// "loaded from Models\Foo".
//
// Usage: ANSLIB_DBG("MyModule", "value=%d ptr=%p", val, ptr);
//
// Hot-path cost when gate is OFF: one indirect call. When gate is ON: snprintf
// to a 1024-byte stack buffer + cross-DLL call into ANSCENTER_DebugLog (which
// scrubs prefixes then emits). No heap, no per-call allocation in the macro.
#ifndef ANSCORE_DEBUGVIEW
#define ANSCORE_DEBUGVIEW 1   // 1 = runtime-gated (default), 0 = hard-off (stripped)
#endif
#if ANSCORE_DEBUGVIEW
#define ANSLIB_DBG(tag, fmt, ...) do { \
    if (::ANSCENTER::ANSLIB::IsDebugViewEnabled()) { \
        char _ansl_dbg_buf[1024]; \
        snprintf(_ansl_dbg_buf, sizeof(_ansl_dbg_buf), "[" tag "] " fmt "\n", ##__VA_ARGS__); \
        ::ANSCENTER::ANSLIB::DebugLog(_ansl_dbg_buf); \
    } \
} while(0)
#else
#define ANSLIB_DBG(tag, fmt, ...) ((void)0)
#endif

#endif
