#ifndef ANSOPENCV_H
#define ANSOPENCV_H
#define ANSOPENCV_API __declspec(dllexport)
#include <iostream>
#include "ANSLicense.h"
#include "LabVIEWHeader/extcode.h"
#include <vector>
#include <array>
#include <atomic>
#include <memory>
#include <opencv2/opencv.hpp>

// Forward declaration for NI Vision IMAQ Image (avoids nivision.h dependency for consumers)
#ifndef NI_IMAGE_TYPE_DEF
    #define NI_IMAGE_TYPE_DEF
    typedef struct Image_struct Image;
#endif

#define MUTEX_TIMEOUT_MS 45000
namespace fs = std::filesystem;
namespace ANSCENTER
{
	std::string CompressJpegToString(const cv::Mat& image, int quality);
	struct DetectionObject
	{
		int classId{ 0 };
		int trackId{ 0 };
		std::string className{};
		float confidence{ 0.0 };
		cv::Rect box{};
		cv::Mat mask{};             //Json string mask ="point1.x,point1.y,...."
		std::vector<float> kps{};   // Pose exsimate keypoint
		std::string extraInfo;      // More information such as facial recognition
	};
	
    class TurboJpegCompressor {
    public:
        TurboJpegCompressor();
		~TurboJpegCompressor() noexcept;
        TurboJpegCompressor(const TurboJpegCompressor&) = delete;
        TurboJpegCompressor& operator=(const TurboJpegCompressor&) = delete;
		[[nodiscard]] std::string compress(const cv::Mat& image, int quality);
    private:
		void* _handle = nullptr;
        unsigned char* _buffer = nullptr;
        unsigned long _bufferSize = 0;
    };

	// GPU-accelerated JPEG encoder using nvJPEG (NVIDIA only).
	// Falls back silently if init fails or on non-NVIDIA hardware.
	class NvJpegCompressor {
	public:
		NvJpegCompressor();
		~NvJpegCompressor() noexcept;
		NvJpegCompressor(const NvJpegCompressor&) = delete;
		NvJpegCompressor& operator=(const NvJpegCompressor&) = delete;
		[[nodiscard]] std::string compress(const cv::Mat& image, int quality);
		[[nodiscard]] bool isValid() const noexcept { return _valid; }
	private:
		void cleanup() noexcept;
		bool            _valid = false;
		void*           _nvHandle = nullptr;      // nvjpegHandle_t
		void*           _encState = nullptr;      // nvjpegEncoderState_t
		void*           _encParams = nullptr;     // nvjpegEncoderParams_t
		void*           _stream = nullptr;        // cudaStream_t
		unsigned char*  _gpuBuffer = nullptr;     // reusable device memory
		size_t          _gpuBufferSize = 0;
	};

	// Fixed-size pool of NvJpegCompressors (~40MB VRAM each).
	// Threads that can't acquire an encoder fall back to TurboJPEG.
	// Fixed pool of NvJpegCompressors sized by GPU VRAM.
	// Formula: poolSize = VRAM_GB / 2 (min 1, e.g. 2GB→1, 4GB→2, 8GB→4, 10GB→5).
	// Threads that can't acquire an encoder fall back to TurboJPEG.
	class NvJpegPool {
	public:
		static NvJpegPool& Instance();
		[[nodiscard]] std::string tryCompress(const cv::Mat& image, int quality);
		[[nodiscard]] bool isAvailable() const noexcept { return _available; }
		[[nodiscard]] int poolSize() const noexcept { return _poolSize; }
	private:
		NvJpegPool();
		~NvJpegPool() = default;
		NvJpegPool(const NvJpegPool&) = delete;
		NvJpegPool& operator=(const NvJpegPool&) = delete;
		static int detectPoolSize();
		bool                                            _available = false;
		int                                             _poolSize = 0;
		std::vector<std::unique_ptr<NvJpegCompressor>>  _encoders;
		std::unique_ptr<std::atomic<bool>[]>            _inUse;   // can't use vector — atomic is non-copyable
	};
    /// <summary>
	/// // ANSOPENCV class provides various image processing functionalities using OpenCV and ANS Center SDK.
    /// </summary>
    class ANSOPENCV_API ANSOPENCV
	{
	public:
		[[nodiscard]] bool Init(std::string licenseKey);
		void ImageResize(const cv::Mat& inputFrame, int width, int height, cv::Mat& outputFrame);
		void ImageResizeWithRatio(const cv::Mat& inputFrame, int width, cv::Mat& outputFrame);
		[[nodiscard]] cv::Mat  BlurObjects(const cv::Mat& image, const std::vector<cv::Rect>& objects);
		[[nodiscard]] cv::Mat  BlurBackground(const cv::Mat& image, const std::vector<cv::Rect>& objects);
		[[nodiscard]] cv::Mat  ToGray(const cv::Mat& image);
		[[nodiscard]] cv::Mat  ImageDenoise(const cv::Mat& image);
		[[nodiscard]] cv::Mat  ImageRepair(const cv::Mat& image);
		[[nodiscard]] cv::Mat  ImageCrop(const cv::Mat& image,const cv::Rect& ROI, int originalImageSize = 0);
		[[nodiscard]] cv::Mat  ImageResizeV2(const cv::Mat& image, int resizeWidth, int orginalImageSize = 0);
		[[nodiscard]] std::string QRDecoder(const cv::Mat& image);
		[[nodiscard]] std::string QRDecoderWithBBox(const cv::Mat& image, int maxImageSize, const std::vector<cv::Rect>& bBox);

		[[nodiscard]] std::string MatToBase64(const cv::Mat& image);
		[[nodiscard]] std::string MatToBinaryData(const cv::Mat& image);
		[[nodiscard]] std::vector<cv::Rect> GetBoundingBoxes(std::string strBBoxes);
		[[nodiscard]] std::string VectorDetectionToJsonString(const std::vector<DetectionObject>& dets);
		[[nodiscard]] std::string PatternMatches(cv::Mat& image, cv::Mat& templateImage, double threshold);
		[[nodiscard]] cv::Mat ImageDarkEnhancement(const cv::Mat& img, double brightnessScaleFactor=1.5);
		[[nodiscard]] cv::Mat ImageContrastEnhancement(const cv::Mat& img);
		[[nodiscard]] cv::Mat ImageWhiteBalance(const cv::Mat& img);
		
		[[nodiscard]] cv::Mat RotateImage(const cv::Mat& image, double angle);
		[[nodiscard]] cv::Mat FlipImage(const cv::Mat& image, int flipCode);//flipCode = 0: Vertical flip (around the x-axis). flipCode = 1: Horizontal flip (around the y-axis). flipCode = -1: Both axes, flipping the image horizontally and vertically.
		[[nodiscard]] cv::Mat ShiftImage(const cv::Mat& image, int shiftX, int shiftY);
		[[nodiscard]] cv::Mat AddGaussianNoise(const cv::Mat& image, double mean, double stddev);
		[[nodiscard]] cv::Mat AddSaltAndPepperNoise(const cv::Mat& image, double amount);
		[[nodiscard]] cv::Mat AddSpeckleNoise(const cv::Mat& image, double stddev);

		static void InitCameraNetwork();
		static void DeinitCameraNetwork();

		static cv::Mat resizeImageToFit(const cv::Mat& inputImage, int maxWidth, int maxHeight, int& newWidth, int& newHeight);
		static bool resizeImage(cv::Mat& inputImage, int resizeWidth, int orginalImageSize=0);
		static bool cropImage(cv::Mat& inputImage, const cv::Rect& resizeROI, int originalImageSize=0);
		static bool ImagesToMP4(const std::string& imageFolder, const std::string& outputVideoPath, int maxWidth, int fps);
		// Direct FFmpeg (libav*) encoder path. Prefers HEVC/H.265 (libx265),
		// falls back to H.264 (libx264), then MPEG-4 Part 2 (mpeg4).
		// Produces significantly smaller files than ImagesToMP4 for the same
		// visual quality. Same parameter meaning as ImagesToMP4.
		static bool ImagesToMP4FF(const std::string& imageFolder, const std::string& outputVideoPath, int maxWidth, int fps);
		// Hardware-accelerated FFmpeg path. Tries, in order:
		//   hevc_nvenc, hevc_qsv, hevc_amf   (NVIDIA/Intel/AMD HEVC)
		//   h264_nvenc, h264_qsv, h264_amf   (NVIDIA/Intel/AMD H.264)
		//   libx265, libx264, mpeg4          (software fallbacks)
		// First encoder that opens successfully is used. HEVC is preferred
		// everywhere because it compresses ~40% smaller than H.264 at the
		// same visual quality, and hardware HEVC is free on any modern GPU.
		static bool ImagesToMP4HW(const std::string& imageFolder, const std::string& outputVideoPath, int maxWidth, int fps);

	private:
		void CheckLicense();
		double CalculateIoU(const cv::Rect& box1, const cv::Rect& box2);
		void NonMaximumSuppression(std::vector<DetectionObject>& detectedObjects, double iouThreshold);
		std::string EncodeJpegString(const cv::Mat& img, int quality);
		SPDLogger& _logger = SPDLogger::GetInstance("ANSCV", false);
		std::string					_licenseKey;
		std::recursive_mutex		_mutex;

		//std::once_flag				licenseOnceFlag; // For one-time license check
		// Atomic so lock-free methods (ImageResize, ImageResizeWithRatio,
		// MatToBinaryData, EncodeJpegString) can read it without _mutex.
		std::atomic<bool>			_licenseValid{ false };
	public:
	};
}


extern "C" __declspec(dllexport) int CreateANSCVHandle(ANSCENTER::ANSOPENCV **Handle, const char* licenseKey);
extern "C" __declspec(dllexport) int ReleaseANSCVHandle(ANSCENTER::ANSOPENCV **Handle);
extern "C" __declspec(dllexport) int ANSCV_ImageResize(ANSCENTER::ANSOPENCV * *Handle,unsigned char* inputImage, unsigned int bufferLength, int width, int height, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageResizeWithRatio(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, int width,LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageToBase64(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageToGray(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageDenoise(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageRepair(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageAutoWhiteBalance(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageBrightEnhance(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, double brightnessScaleFactor, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageContrastEnhance(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ImageCrop(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int x, int y, int width, int height, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_GetImageSize(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle imageSize);
extern "C" __declspec(dllexport) int ANSCV_GetImageSizeFromImageFile(ANSCENTER::ANSOPENCV** Handle, const char* imageFilePath, LStrHandle imageSize);

extern "C" __declspec(dllexport) int ANSCV_BlurObjects(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, const char* strBboxes, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_BlurBackground(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, const char* strBboxes,  LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_QRDecoder(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle detectedQRText);
extern "C" __declspec(dllexport) int ANSCV_QRDecoderCV(ANSCENTER::ANSOPENCV** Handle, const cv::Mat &image, std::string& detectedQRText);

extern "C" __declspec(dllexport) int ANSCV_PatternMatchs(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, const char* templateFilePath, double theshold, LStrHandle detectedMatchedLocations);


extern "C" __declspec(dllexport) int ANSCV_RotateImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double angle, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_FlipImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int flipCode, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_ShiftImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int shiftX, int shiftY, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_AddGaussianNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double mean, double stddev, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_AddSaltAndPepperNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double amount, LStrHandle outputImage);
extern "C" __declspec(dllexport) int ANSCV_AddSpeckleNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double stddev, LStrHandle outputImage);

extern "C" __declspec(dllexport) void ANSCV_InitCameraResource();
extern "C" __declspec(dllexport) void ANSCV_FreeCameraResource();
extern "C" __declspec(dllexport) int  ANSCV_ResizeImage_Static(unsigned char* inputImage, unsigned int bufferLength, int width,int height, int& newWidth, int&newHeight, LStrHandle outputImage);


// Image reference management functions
extern "C" __declspec(dllexport) int  ANSCV_CloneImage_S(cv::Mat **imageIn, cv::Mat** imageOut);
extern "C" __declspec(dllexport) int  ANSCV_ReleaseImage_S(cv::Mat** imageIn);
extern "C" __declspec(dllexport) int  ANSCV_ReSizeImage_S(cv::Mat** imageIn, int width, int originalImageSize = 0);
extern "C" __declspec(dllexport) int  ANSCV_CropImage_S(cv::Mat** imageIn, int x, int y, int width, int height, int originalImageSize=0);
extern "C" __declspec(dllexport) int  ANSCV_GetImage_S(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, LStrHandle outputImage);
extern "C" __declspec(dllexport) int  ANSCV_GetImage_CPP(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, std::string& outputImage);
extern "C" __declspec(dllexport) int  ANSCV_GetImageAndRemoveImgRef_S(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, LStrHandle outputImage);
extern "C" __declspec(dllexport) int  ANSCV_GetImageInfo_S(cv::Mat** imageIn, int &width, int& height);
extern "C" __declspec(dllexport) int  ANSCV_CreateImageFromJpegString_S(unsigned char* inputImage, unsigned int bufferLength,cv::Mat** image);
extern "C" __declspec(dllexport) int  ANSCV_CreateImageFromFile_S(const char* imageFilePath, cv::Mat** image);
// Pre-process image functions
extern "C" __declspec(dllexport) int  ANSCV_ImageAutoWhiteBalance_S(cv::Mat** imageIn);
extern "C" __declspec(dllexport) int  ANSCV_ImageBrightEnhance_S(cv::Mat** imageIn, double brightnessScaleFactor);
extern "C" __declspec(dllexport) int  ANSCV_ImageContrastEnhance_S(cv::Mat** imageIn);
extern "C" __declspec(dllexport) int  ANSCV_ImageDenoise_S(cv::Mat** imageIn);
extern "C" __declspec(dllexport) int  ANSCV_ImageRepair_S(cv::Mat** imageIn);
extern "C" __declspec(dllexport) int  ANSCV_ImageToGray_S(cv::Mat** imageIn);
extern "C" __declspec(dllexport) int  ANSCV_ImageRotate_S(cv::Mat** imageIn, double angle);
extern "C" __declspec(dllexport) int  ANSCV_ImageFlip_S(cv::Mat** imageIn, int flipCode);
// Post-process image functions
extern "C" __declspec(dllexport) int  ANSCV_ImageBlurObjects_S(cv::Mat** imageIn, const char* strBboxes);
extern "C" __declspec(dllexport) int  ANSCV_ImageBlurBackground_S(cv::Mat** imageIn, const char* strBboxes);
extern "C" __declspec(dllexport) int  ANSCV_ImageQRDecoder_S(cv::Mat** imageIn, int maxImageWidth, const char* strBboxes, LStrHandle detectedQRText);
extern "C" __declspec(dllexport) int  ANSCV_ImagePatternMatchs_S(cv::Mat** imageIn, const char* templateFilePath, double theshold, LStrHandle detectedMatchedLocations);


extern "C" __declspec(dllexport) int  ANSCV_ImagesToMP4_S(const char* imageFolder, const char* outputVideoPath, int maxWidth, int fps);
// Direct-FFmpeg variant. Same signature as ANSCV_ImagesToMP4_S but uses libav*
// encoders directly with libx265/libx264 tuning for smaller output files.
extern "C" __declspec(dllexport) int  ANSCV_ImagesToMP4FF_S(const char* imageFolder, const char* outputVideoPath, int maxWidth, int fps);
// Hardware-accelerated variant. Tries NVIDIA (NVENC), Intel (QSV), and AMD
// (AMF) HEVC/H.264 encoders in order, then falls back to software encoders.
// Same signature as ANSCV_ImagesToMP4_S.
extern "C" __declspec(dllexport) int  ANSCV_ImagesToMP4HW_S(const char* imageFolder, const char* outputVideoPath, int maxWidth, int fps);
// Prints the license string of the FFmpeg libraries linked into ANSCV.dll
// (LGPL vs GPL). Useful for verifying whether the bundled FFmpeg build is
// commercially safe to distribute. Prints to stdout.
extern "C" __declspec(dllexport) void ANSCV_PrintFFmpegLicense_S();

// IMAQ -> cv::Mat conversion (NI Vision Image*, auto-detects indirection level)
extern "C" __declspec(dllexport) int  ANSCV_IMAQ2Image(void* imaqHandle, cv::Mat** imageOut);
// cv::Mat -> IMAQ: outputs lossless PNG to LStrHandle, use IMAQ ReadFromString in LabVIEW
extern "C" __declspec(dllexport) int  ANSCV_Image2IMAQ(cv::Mat** imageIn, LStrHandle outputImage);

// cv::Mat -> LabVIEW 2D U32 array for IMAQ ArrayToColorImage VI "Image Pixels (U32)"
typedef struct { int32 dimSizes[2]; uInt32 elt[1]; } LVArray2D_U32;
typedef LVArray2D_U32** LVArray2D_U32Hdl;
extern "C" __declspec(dllexport) int  ANSCV_ImageToArray(cv::Mat** imageIn, LVArray2D_U32Hdl arrayOut);
// cv::Mat -> IMAQ via LStrHandle (lossless PNG)
extern "C" __declspec(dllexport) int  ANSCV_Image2IMAQ(cv::Mat** imageIn, LStrHandle outputImage);
// cv::Mat -> 1D U8 array (raw BGR pixels, row-major) + width, height, channels
typedef struct { int32 dimSizes[1]; uInt8 elt[1]; } LVArray1D_U8;
typedef LVArray1D_U8** LVArray1D_U8Hdl;
extern "C" __declspec(dllexport) int  ANSCV_ImageToFlatArray(cv::Mat** imageIn, LVArray1D_U8Hdl arrayOut, int* width, int* height, int* channels);
// 1D U8 raw pixel array -> JPEG string
extern "C" __declspec(dllexport) int  ANSCV_FlatArrayToJpeg(LVArray1D_U8Hdl arrayIn, int width, int height, int channels, int quality, LStrHandle outputImage);
// cv::Mat -> BMP binary string (no compression, fastest possible, for .NET Image.FromStream)
extern "C" __declspec(dllexport) int  ANSCV_ImageToBmp(cv::Mat** imageIn, int maxWidth, int& newWidth, int& newHeight, LStrHandle outputImage);
// BMP string -> JPEG string (for storage/network)
extern "C" __declspec(dllexport) int  ANSCV_BmpToJpeg(LStrHandle bmpInput, int quality, LStrHandle jpegOutput);

#endif