#ifndef ANSLICENSE_H
#define ANSLICENSE_H

#ifdef ANSLICENSE_EXPORTS
#define ANSLICENSE_API __declspec(dllexport)
#else
#define ANSLICENSE_API __declspec(dllimport)
#endif


#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <exception>
#include <iostream>
#include "anslicensing.h"
#include "LabVIEWHeader/extcode.h"
#include <mutex>
#ifdef _UNICODE
#define _T(s) L##s
#define _tprintf wprintf
#define _tcscmp wcscmp
#define _tfopen fopen
#else
#define _T(s) (s)
#define _TL(s) L##s
#define _tprintf printf
#define _tcscmp strcmp
#define _tfopen fopen
#endif

enum LicenseKeyStatus {
	SUCCESS = 0,
	INVALID_LICENSE_KEY = 1,
	INVALID_HARDWARE_ID = 2,
	LICENSE_EXPIRED = 3,
	INVALID_ACTIVATION_KEY = 4,
	PAYMENT_REQUIRED = 5,
	INVALID_PRODUCT = 6,
	UNKNOWN = 7
};

typedef struct {
	int32	cnt;			/* number of bytes that follow */
	double	value[1];		/* value of double */
} LDbl, * LDblPtr, ** LDblHandle;

namespace ANSCENTER
{

	// Precision used for GPU inference

	enum OCRType {
		SINGLETEXT = 0,
		STRUCTURE = 1
	};

	enum OCRLanguage {
		ENGLISH = 0,
		CHINESE = 1,
		FRENCH  = 2,
		GERMANY = 3,
		JAPANESE= 4,
		KOREAN  = 5,
		CUSTOM	=6
	};
	// Precision used for GPU inference
	enum class Precision {
		// Full precision floating point value
		FP32,
		// Half prevision floating point value
		FP16,
		// Int8 quantization.
		// Has reduced dynamic range, may result in slight loss in accuracy.
		// If INT8 is selected, must provide path to calibration dataset directory.
		INT8,
	};

	// Options for the network
	struct Options {
		// Precision to use for GPU inference.
		Precision precision = Precision::FP16;
		// If INT8 precision is selected, must provide path to calibration dataset
		// directory.
		std::string calibrationDataDirectoryPath;
		// The batch size to be used when computing calibration data for INT8
		// inference. Should be set to as large a batch number as your GPU will
		// support.
		int32_t calibrationBatchSize = 128;
		// The batch size which should be optimized for.
		int32_t optBatchSize = 1;
		// Maximum allowable batch size
		int32_t maxBatchSize = 16;
		// GPU device index
		int deviceIndex = 0;
		// Directory where the engine file should be saved
		std::string engineFileDir = ".";

		// Maximum allowed input width
		int32_t maxInputWidth = -1; // Default to -1 --> expecting fixed input size
		// Minimum allowed input width
		int32_t minInputWidth = -1; // Default to -1 --> expecting fixed input size
		// Optimal input width
		int32_t optInputWidth = -1; // Default to -1 --> expecting fixed input size

		// Maximum allowed input height
		int32_t maxInputHeight = -1; // Default to -1 --> expecting fixed input size
		// Minimum allowed input height
		int32_t minInputHeight = -1; // Default to -1 --> expecting fixed input size
		// Optimal input height
		int32_t optInputHeight = -1; // Default to -1 --> expecting fixed input size

		// GPU clock lock frequency in MHz.
		//  -1  = auto (default) — query max supported clock and lock at ~80%
		//   0  = disabled — let the driver manage clocks dynamically
		//  >0  = lock at this specific MHz value (e.g. 2400)
		// Prevents GPU power throttling that causes TensorRT timing instability
		// on laptop GPUs.  Requires elevated privileges; fails gracefully if not.
		int32_t gpuClockLockMHz = -1;
	};

	enum PrecisionType {
		INT8 = 0,
		FP16 = 1,
		FP32 = 2,
	};
	enum EngineType {
		CPU = 0,
		NVIDIA_GPU = 1,
		OPENVINO_GPU = 2,
		AMD_GPU = 3,
		AUTO_DETECT = 99   // default: hardware auto-detection
	};
	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
	};
	enum ANSPRODUCTS {
		ANNHUB = 1000,
		DLHUB = 1001,
		ODHUB = 1002,
		ANSVIS = 1003,
		ANS_FACERECOGNIZE = 1004,
		ANS_OCR = 1005,
		ANS_ALPR =1006,
		ANS_CV = 1007,
		ANS_TS = 1008
	};
	enum ANSLICENSESTATUS {
		ACTIVE = 0,
		EXPIRED = 1,
		TRIAL=2,
		UNLICENSE = 3
	};
	struct ModelConfig {
		DetectionType	detectionType;
		ModelType		modelType;
		PrecisionType	precisionType;
		int inpWidth;
		int inpHeight;
		float modelConfThreshold;
		float modelMNSThreshold;
		float detectionScoreThreshold;
		int   numKPS;			// Number of pose keypoint
		float kpsThreshold;
		bool autoGPUDetection;
		float unknownPersonThreshold;
		int gpuOptBatchSize = 1; 		// The batch size which should be optimized for.
		int gpuMaxBatchSize = 1;		// Maximum allowable batch size
		int gpuDeviceIndex = 0;		// GPU device index

		// Maximum allowed input width
		int32_t maxInputWidth = -1; // Default to -1 --> expecting fixed input size
		// Minimum allowed input width
		int32_t minInputWidth = -1; // Default to -1 --> expecting fixed input size
		// Optimal input width
		int32_t optInputWidth = -1; // Default to -1 --> expecting fixed input size

		// Maximum allowed input height
		int32_t maxInputHeight = -1; // Default to -1 --> expecting fixed input size
		// Minimum allowed input height
		int32_t minInputHeight = -1; // Default to -1 --> expecting fixed input size
		// Optimal input height
		int32_t optInputHeight = -1; // Default to -1 --> expecting fixed input size
	};

	struct ANSLicenseInfo {
		std::string productName;
		std::string licenseKey;
		std::string activationKey;
		std::string hardwareId;
		ANSLICENSESTATUS licenseStatus;
		int remainingDays;
		int enableFeature;
	};

	

	class ANSLICENSE_API SPDLogger
	{// Singleton design pattern
	protected:
		SPDLogger(const std::string& loggerFileName, bool consoleLoggerEnable = false)
			: _loggerFileName(loggerFileName), _consoleLoggerEnable(consoleLoggerEnable) {
			Init();
		}
		void Init();
		void ConfigureLogger();
		std::string _loggerFileName{};
		bool _consoleLoggerEnable{true};
		// Deleting copy constructor and assignment operator to ensure singleton
		SPDLogger(const SPDLogger&) = delete;
		SPDLogger& operator=(const SPDLogger&) = delete;

	public:  
		bool Release();
		~SPDLogger();
		// Static method to get the singleton instance
		static SPDLogger& GetInstance(const std::string& loggerFileName, bool consoleLoggerEnable = false) {
			static SPDLogger instance(loggerFileName, consoleLoggerEnable);
			return instance;
		}
		void Disable();
		void Enable();
		void LogTrace(const std::string& source, const std::string& messsage, const std::string& fileSource, int lineSource);
		void LogDebug(const std::string& source, const std::string& messsage, const std::string& fileSource, int lineSource);
		void LogInfo(const std::string& source, const std::string& messsage, const std::string& fileSource, int lineSource);
		void LogWarn(const std::string& source, const std::string& messsage, const std::string& fileSource, int lineSource);
		void LogError(const std::string& source, const std::string& messsage, const std::string& fileSource, int lineSource);
		void LogFatal(const std::string& source, const std::string& messsage, const std::string& fileSource, int lineSource);
	};

	class ANSLICENSE_API ANSLSHelper {
	private:
		LicenseTemplate*			_licenseTemplate = nullptr;
		License* _license							 = nullptr;
		std::wstring				_licenseFilePath = _T("");
		std::wstring				_licenseServiceURL = _T("");
		std::wstring				_sdkLicenseKey = _T("");
		std::wstring				_privateKey = _T("");
		std::wstring				_publicKey = _T("");

		SPDLogger&					_logger = SPDLogger::GetInstance("License");
		std::string					LoadLicenseContent();
		void						Setup5x5LicenseTemplate();// This is simple case
		std::wstring				GetProductId(std::string licenseKey);

	public:
		ANSLSHelper();
		ANSLSHelper(std::string licenseServiceURL);
		ANSLSHelper(std::string privateKey, std::string licenseServiceURL);
		ANSLSHelper(std::string privateKey, std::string publicKey, std::string licenseServiceURL);
		~ANSLSHelper();

		void			SetupLicenseTemplate();

		[[nodiscard]] bool			MatchCurrentHardwareId(std::string hwid);
		[[nodiscard]] bool			DectectClockmanipulation(int year, int month, int  date);

		[[nodiscard]] std::string	GetLicenseData(std::string licenseKey);
		[[nodiscard]] int			ActivateLicense(std::string licenseKey, std::string registrationName, std::string& activationKey); // Always activate the license (need internet connection)
		[[nodiscard]] int			ActivateLicenseWithCustomHWID(std::string licenseKey, std::string registrationName, std::string hardwareId, std::string& activationKey); // Always activate the license (need internet connection)

		[[nodiscard]] bool			LoadLicenseTemplate(std::string templateFilePath);
		[[nodiscard]] std::string	SaveLicenseTemplate(std::string templateFilePath, bool savePrivateKey);

		// These are the common methods to be used
		void			SetLicenseServiceURL(std::string licenseServiceURL);
		[[nodiscard]] std::string	GetCurrentHardwareId();
		void			LoadLicense(std::string licenseKey);
		[[nodiscard]] std::string	SaveLicense();
		[[nodiscard]] std::string	GenerateKey(int productId, std::string registrationName, int productFeature, bool trialLicense);
		[[nodiscard]] std::string	GenerateKey(int productId, std::string registrationName, int productFeature, int expiredYear, int expiredMonth, int expiredDate);
		[[nodiscard]] bool			IsLicenseKeyValid(std::string licenseKey, std::string registrationName); // Only check the license key with registration name and expiration date only. This is used when developer to use internal APIs called
		[[nodiscard]] bool			CheckLicenseKey(std::string licenseKey, std::string registrationName, int productId, int& enabledFeatures);   // Check if the license key is valid
		[[nodiscard]] int			ValidateLicense(std::string licenseKey, std::string registrationName, std::string& activationKey);
		[[nodiscard]] int			ValidateLicenseWithCustomHWID(std::string licenseKey, std::string registrationName, std::string customHWID, std::string& activationKey);

		[[nodiscard]] int			GenerateOfflineActivationData(std::string licenseKey, std::string registrationName, std::string& offlineActivationData);
		[[nodiscard]] int			InstallOfflineLicense(std::string licenseKey, std::string registrationName, std::string activationKey);

		// Utilities
		[[nodiscard]] static int		ExtractModelZipFile(std::string modelZipFile, std::string password, std::string modelName, std::string& extractedOutputFolder);
		[[nodiscard]] static bool		ZipFolerWithPassword(std::string folderPath, std::string zipFilePath, std::string password);
		[[nodiscard]] static int		PrepareEdgeModel(std::string modelZipFile, std::string zipFilePassword, std::string edgePassword, std::string modelName, std::string edgeSerialNumber, std::string& configFilePathName, std::string& labelMapPathName, std::string& outputModelFolder);
		[[nodiscard]] static std::string	GetSystemHardwareInformation();
		[[nodiscard]] static bool		ReleaseLogger();

	};


	class ANSLICENSE_API ANSLicenseHelper {
	private:
		[[nodiscard]] static bool deleteRecursively(const std::string& pathStr, bool safeMode);
		[[nodiscard]] static bool isPathSafe(const std::string& pathStr);
	public:
		[[nodiscard]] static bool LicenseVerification(std::string licenseKey, int productId, std::string registrationName);
		[[nodiscard]] static EngineType CheckHardwareInformation();
		[[nodiscard]] static std::string ListLicenses();
		[[nodiscard]] static int ActivateTrialLicense(std::string productName);
		[[nodiscard]] static int DeactivateLicense(std::string productName);
		[[nodiscard]] static int ActivateLicense(std::string productName, std::string licenseKey);
		[[nodiscard]] static int ActivateLicenseWithCustomHWID(std::string productName, std::string licenseKey, std::string hwid, std::string& activationKey);
		[[nodiscard]] static std::string DecodeValidationData(const char* validationData);
		[[nodiscard]] static std::string GenerateActivationDataFile(std::string activationKey,std::string licenseKey, std::string hardwareId, std::string validationData);
		[[nodiscard]] static std::string DecodeBase64(std::string base64String);
		[[nodiscard]] static bool RemoveTrialLicenseKeys(std::string registrationName);
		[[nodiscard]] static bool DeleleteRecursively(const std::string& pathStr) {
			return deleteRecursively(pathStr, true);
		}

	};
}
extern "C" __declspec(dllexport) int CreateANSLSHandle(ANSCENTER::ANSLSHelper * *Handle, const char* privateKey, const char* publicKey, const char* licenseServiceURL);
extern "C" __declspec(dllexport) int Release(ANSCENTER::ANSLSHelper * *Handle);
extern "C" __declspec(dllexport) void SetLicenseServiceURL(ANSCENTER::ANSLSHelper * *Handle, const char* licenseServiceURL);
extern "C" __declspec(dllexport) int GetLicenseData(ANSCENTER::ANSLSHelper * *Handle, const char* licenseKey, LStrHandle licenseData);
extern "C" __declspec(dllexport) int LoadLicenseTemplate(ANSCENTER::ANSLSHelper * *Handle, const char* templateFilePath);
extern "C" __declspec(dllexport) int GetCurrentHardwareId(ANSCENTER::ANSLSHelper * *Handle, LStrHandle currentHwID);
extern "C" __declspec(dllexport) int GenerateKey(ANSCENTER::ANSLSHelper * *Handle, int productId, const char* registrationName, int productFeature, int trialLicense, LStrHandle generatedKey);
extern "C" __declspec(dllexport) int GenerateKeyWithExpiredDate(ANSCENTER::ANSLSHelper * *Handle, int productId, const char* registrationName, int productFeature, int expiredYear, int expiredMonth, int expiredDate, LStrHandle generatedKey);
extern "C" __declspec(dllexport) int CheckLicenseKey(ANSCENTER::ANSLSHelper * *Handle, const char* licenseKey, const char* registrationName, int productId);// , LStrHandle strEnableFeature);   // Check if the license key is valid
extern "C" __declspec(dllexport) int ValidateLicense(ANSCENTER::ANSLSHelper * *Handle, const char* licenseKey, const char* registrationName, const char* existingActivationKey, LStrHandle activationKey);
extern "C" __declspec(dllexport) int ActivateLicense(ANSCENTER::ANSLSHelper * *Handle, const char* licenseKey, const char* registrationName, LStrHandle activationKey); // Always activate the license (need internet connection)
extern "C" __declspec(dllexport) int ActivateLicenseWithCustomHWID(ANSCENTER::ANSLSHelper * *Handle, const char* licenseKey, const char* registrationName, const char* hardwareId, LStrHandle activationKey); // Always activate the license (need internet connection)
extern "C" __declspec(dllexport) int IsLicenseKeyValid(ANSCENTER::ANSLSHelper * *Handle, const char* licenseKey, const char* registrationName); // Only check the license key with registration name and expiration date only. This is used when developer to use internal APIs called
extern "C" __declspec(dllexport) int GenerateOfflineActivationData(ANSCENTER::ANSLSHelper * *Handle, const char* licenseKey, const char* registrationName, LStrHandle offlineActivationData);
extern "C" __declspec(dllexport) int InstallOfflineLicense(ANSCENTER::ANSLSHelper * *Handle, const char* licenseKey, const char* registrationName, const char* activationKey);


// Utilities
extern "C" __declspec(dllexport) int ExtractModelZipFile(const char* modelZipFile, const char* password, const char* modelName, LStrHandle extractedOutputFolder);
extern "C" __declspec(dllexport) int ZipFolderToZipFile(const char* modelFolderSource,const char* modelZipFileDes, const char* password);
extern "C" __declspec(dllexport) int PrepareEdgeModel(const char* modelZipFile, const char* zipFilePassword, const char* edgePassword, const char* modelName, const char* edgeSerialNumber, LStrHandle pathArray);
extern "C" __declspec(dllexport) int GetSystemHardwareInformation(LStrHandle systemHardwareInformation);
extern "C" __declspec(dllexport) int ReleaseLoggers();
extern "C" __declspec(dllexport) int ListANSLicenses(std::string &listLicense);
extern "C" __declspec(dllexport) int ListANSLicenses_LV(LStrHandle ansProductLicenses);
extern "C" __declspec(dllexport) int ActivateTrialLicense(const char* productName);
extern "C" __declspec(dllexport) int DeactivateLicense(const char* productName);
extern "C" __declspec(dllexport) int ActivateProductLicense(const char* productName, const char* licenseKey);
extern "C" __declspec(dllexport) int ActivateProductLicenseWithCustomHWID(const char* productName, const char* licenseKey, const char* hwid, LStrHandle lvActivationKey);
extern "C" __declspec(dllexport) int GetRegistrationName(const char* validationData, LStrHandle registrationName);
extern "C" __declspec(dllexport) int GenerateActivationKeyData(const char* activationKey, const char* licenseKey, const char* hardwareId, const char* validationData, LStrHandle activationData);
extern "C" __declspec(dllexport) int ANSLicenseVerification(const char* licenseKey, int productId, const char* registrationName);
extern "C" __declspec(dllexport) int ANSModelConversion(const char* zipModelFile, const char* zipModelPassword);
extern "C" __declspec(dllexport) int DeletePathRecursively(const char* pathTobeDeleted);

#endif 