#ifndef ANSLPR_H
#define ANSLPR_H
#define ANSLPR_API __declspec(dllexport)
#pragma once
#include <iostream>
#include <cassert>
#include <fstream>
#include <string>
#include <filesystem>
#include <opencv2/opencv.hpp>
#include "Utility.h"
#include "ANSLicense.h"
#include "LabVIEWHeader/extcode.h"
#include "ANSEngineCommon.h"
#define  MAX_ALPR_FRAME 60
namespace ANSCENTER
{
	enum Country {
		VIETNAM = 0,
		CHINA = 1,
		AUSTRALIA = 2,
		USA = 3,
		INDONESIA = 4
	};
    class ALPRChecker {
    private:
        int maxFrames;
        int minVotesToStabilize = 3;    // need at least 3 identical reads to lock
        float iouMatchThreshold = 0.3f; // IoU to match same physical plate across frames
		std::recursive_mutex		_mutex;

        // Per-plate spatial tracking: each tracked plate has a location + text history
        struct TrackedPlate {
            cv::Rect lastBox;
            std::deque<std::string> textHistory;  // rolling history of OCR reads
            std::string lockedText;               // majority-vote winner (empty = not locked yet)
            int lockCount = 0;                    // how many consecutive frames the lock held
            int framesSinceLastSeen = 0;          // how many checkPlate calls since this plate was last matched
        };
        int _pruneCounter = 0;                    // counts checkPlate calls for periodic pruning
        // cameraId -> list of tracked plates
        std::unordered_map<std::string, std::vector<TrackedPlate>> trackedPlates;

        [[nodiscard]] int levenshteinDistance(const std::string& s1, const std::string& s2);
        [[nodiscard]] float computeIoU(const cv::Rect& a, const cv::Rect& b);
        [[nodiscard]] std::string majorityVote(const std::deque<std::string>& history);
    public:
		void Init(int framesToStore = MAX_ALPR_FRAME);
		ALPRChecker(int framesToStore = MAX_ALPR_FRAME) : maxFrames(framesToStore) {}
		// Original API (backward compatible) — no spatial tracking
		[[nodiscard]] std::string checkPlate(const std::string& cameraId, const std::string& detectedPlate);
		// Enhanced API with bounding box for spatial plate tracking
		[[nodiscard]] std::string checkPlate(const std::string& cameraId, const std::string& detectedPlate, const cv::Rect& plateBox);
    };

	class ANSLPR_API ANSALPR {

	protected:
		SPDLogger& _logger = SPDLogger::GetInstance("ALPR", false);
		bool   _debugFlag{ false };
		bool   _licenseValid{ false };
		bool   _isInitialized{ false};
		double _detectorThreshold{ 0.5 };
		double _ocrThreshold{ 0.5 };
		double _colorThreshold{ 0.0 };// If we use the colour information, this threshold should be greater  than 0.0
		std::vector<std::string> _plateFormats;
		std::string _licenseKey{};
		std::string _modelFolder;
		struct ImageSection {
			cv::Rect region;
			int priority;
			ImageSection(const cv::Rect& r) : region(r), priority(0) {}
		};
		cv::Size previousImageSize = cv::Size(0, 0);
		std::vector<ImageSection> cachedSections;
		int                     _currentPriority{ 0 }; // None 
		cv::Rect                _detectedArea;// Area where license plate are detected
		Country					_country;
		std::recursive_mutex	_mutex;
	public:
		[[nodiscard]] virtual bool Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword, double detectorThreshold, double ocrThreshold, double colourTheshold=0);
		[[nodiscard]] virtual bool LoadEngine();
		[[nodiscard]] virtual bool Inference(const cv::Mat& input, std::string& lprResult) = 0;
		[[nodiscard]] virtual bool Inference(const cv::Mat& input, std::string& lprResult,const std::string &cameraId) = 0;
		[[nodiscard]] virtual bool Inference(const cv::Mat& input, const std::vector<cv::Rect> & Bbox, std::string& lprResult) = 0;
		[[nodiscard]] virtual bool Inference(const cv::Mat& input, const std::vector<cv::Rect> & Bbox, std::string& lprResult,const std::string & cameraId) = 0;
		[[nodiscard]] virtual std::vector<Object> RunInference(const cv::Mat& input, const std::string &cameraId) = 0;
		[[nodiscard]] std::string VectorDetectionToJsonString(const std::vector<Object>& dets);
		void SetPlateFormats(const std::vector<std::string>& formats);
		void SetPlateFormat(const std::string& format);
		[[nodiscard]] std::vector<std::string> GetPlateFormats() const;
		virtual ~ANSALPR();
		void   CheckLicense();

		/// Enable/disable deep pipeline benchmarking.
		/// When enabled, logs per-stage timing for the full ALPR pipeline
		/// (LP detection, OCR, color classification, validation, serialization).
		/// Also propagates the flag to sub-detectors (_lpDetector, _ocrDetector, etc.).
		virtual void ActivateDebugger(bool debugFlag) { _debugFlag = debugFlag; }
		[[nodiscard]] virtual bool Destroy() = 0;
		[[nodiscard]] static std::vector<cv::Rect> GetBoundingBoxes(const std::string& strBBoxes);
		[[nodiscard]] static std::string PolygonToString(const std::vector<cv::Point2f>& polygon);
		[[nodiscard]] static std::string KeypointsToString(const std::vector<float>& kps);
		[[nodiscard]] static std::vector<cv::Point2f> RectToNormalizedPolygon(const cv::Rect& rect, float imageWidth, float imageHeight);
	protected:
		// Utilities for scanning license plate
		  // Function to seperate screen size
		[[nodiscard]] double calculateDistanceToCenter(const cv::Point& center, const cv::Rect& rect);
		[[nodiscard]] std::vector<ImageSection> divideImage(const cv::Mat& image);
		[[nodiscard]] std::vector<ANSALPR::ImageSection> createSlideScreens(const cv::Mat& image);
		[[nodiscard]] int getHighestPriorityRegion();
		[[nodiscard]] int getLowestPriorityRegion();
		[[nodiscard]] cv::Rect getRegionByPriority(int priority);
		[[nodiscard]] std::vector<Object> AdjustLicensePlateBoundingBoxes(const std::vector<Object>& detectionsInROI,
															const cv::Rect& roi, const cv::Size& fullImageSize,
															float aspectRatio = 2.0f, // width at least 2x height
															int padding = 10 // base padding
															);

	};

}

extern "C" ANSLPR_API int		   CreateANSALPRHandle(ANSCENTER::ANSALPR **Handle, const char* licenseKey, const char* modelZipFilePath, const char* modelZipPassword, int engineType, double detectorThreshold, double ocrThreshold, double colourThreshold);
extern "C" ANSLPR_API int		   LoadANSALPREngineHandle(ANSCENTER::ANSALPR** Handle);

extern "C" ANSLPR_API std::string  ANSALPR_RunInference(ANSCENTER::ANSALPR **Handle, unsigned char* jpeg_string, unsigned int bufferLength);
extern "C" ANSLPR_API std::string  ANSALPR_RunInferenceWithCamID(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId);
extern "C" ANSLPR_API std::string  ANSALPR_RunInferenceBinary(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height);
extern "C" ANSLPR_API std::string  ANSALPR_RunInferenceBinaryInCroppedImages(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, const char* strBboxes);
extern "C" ANSLPR_API std::string  ANSALPR_RunInferenceInCroppedImages(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes);
extern "C" ANSLPR_API std::string  ANSALPR_RunInferenceInCroppedImagesWithCamID(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, const char* cameraId);

extern "C" ANSLPR_API int          ReleaseANSALPRHandle(ANSCENTER::ANSALPR **Handle);
//// For LabVIEW API
extern "C" ANSLPR_API int          ANSALPR_RunInference_LV(ANSCENTER::ANSALPR **Handle, unsigned char* jpeg_string, unsigned int bufferLength, LStrHandle detectionResult);
extern "C" ANSLPR_API int          ANSALPR_RunInferenceWithCamID_LV(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* cameraId, LStrHandle detectionResult);
extern "C" ANSLPR_API int		   ANSALPR_RunInferenceBinary_LV(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_bytes, unsigned int width, unsigned int height, LStrHandle detectionResult);
extern "C" ANSLPR_API int		   ANSALPR_RunInferenceInCroppedImages_LV(ANSCENTER::ANSALPR * *Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, LStrHandle detectionResult);
extern "C" ANSLPR_API int		   ANSALPR_RunInferenceInCroppedImagesWithCamID_LV(ANSCENTER::ANSALPR** Handle, unsigned char* jpeg_string, unsigned int bufferLength, const char* strBboxes, const char* cameraId, LStrHandle detectionResult);
extern "C" ANSLPR_API int		   ANSALPR_RunInferenceComplete_LV(ANSCENTER::ANSALPR** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize, LStrHandle detectionResult, LStrHandle imageStr);
extern "C" ANSLPR_API int		   ANSALPR_RunInferenceComplete_CPP(ANSCENTER::ANSALPR** Handle, cv::Mat** cvImage, const char* cameraId, int getJpegString, int jpegImageSize,std::string& detectionResult, std::string& imageStr);
extern "C" ANSLPR_API int		   ANSALPR_RunInferencesComplete_LV(ANSCENTER::ANSALPR** Handle, cv::Mat** cvImage, const char* cameraId, int maxImageSize,const char* strBboxes, LStrHandle detectionResult);

// Get/Set format	
extern "C" ANSLPR_API int		   ANSALPR_SetFormat(ANSCENTER::ANSALPR** Handle, const char* format);
extern "C" ANSLPR_API int		   ANSALPR_SetFormats(ANSCENTER::ANSALPR** Handle, const char* formats);// comma separated formats
extern "C" ANSLPR_API int		   ANSALPR_GetFormats(ANSCENTER::ANSALPR** Handle, LStrHandle formats);// comma separated formats

#endif