#ifndef ANSLPRRT_H
#define ANSLPRRT_H
#pragma once
#include "ANSLPR.h"
#include <onnxruntime_c_api.h>
#include <onnxruntime_cxx_api.h>
#include "yolov5_alpr_onnx_detector.h"
#include "ONNX_detector.h"
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock

namespace ANSCENTER
{
	class ANSLPR_API ANSALPR_RT :public ANSALPR {
	private:
		std::mutex mtxMutex;      // mutex for critical section
		std::string _globalModelFileName{};
		std::string _focusedOnLPRModelFileName{};
		std::string _platesTypesClassifierFileName{};
		std::string _platesTypesLabelsFileName{};

		size_t _globalViewId;
		size_t _focusedOnLPRId;
		size_t _platesTypesClassifierId;
		[[nodiscard]] bool   CloseReferences();

	public:
		ANSALPR_RT();
		~ANSALPR_RT();
		[[nodiscard]] bool Initialize(const std::string& licenseKey, const std::string& modelZipFilePath, const std::string& modelZipPassword) override;
		[[nodiscard]] bool Inference(const cv::Mat& input, std::string& lprResult);
		[[nodiscard]] bool Inference(const cv::Mat& input, const std::vector<cv::Rect>& Bbox, std::string& lprResult);
		[[nodiscard]] bool Destroy() override;

	private:
		[[nodiscard]] std::string VectorDetectionToJsonString(const std::vector<Object>& dets);
		void CheckStatus(OrtStatus* status);
		cv::Rect GetGlobalROI(std::list<cv::Rect> ROIs);
		std::list<Yolov5_alpr_onxx_detector*>::const_iterator	GetDetector(unsigned int id, const std::list<Yolov5_alpr_onxx_detector*>& detectors,
			const std::list<unsigned int>& detectors_ids);
		std::list<Plates_types_classifier*>::const_iterator		GetPlatesTypesClassifier(unsigned int id, const std::list<Plates_types_classifier*>& plates_types_classifiers,
			const std::list<unsigned int>& plates_types_classifiers_ids);
		unsigned int GetNewId(const std::list<unsigned int>& detectors_ids);
		bool CloseDetector(unsigned int id, std::list<Ort::Env*>& _envs, std::list<Ort::SessionOptions*>& _lsessionOptions, std::list<Yolov5_alpr_onxx_detector*>& _detectors,
			std::list<unsigned int>& _detectors_ids);
		bool CloseDetector(unsigned int id, std::list<Ort::Env*>& _envs, std::list<Ort::SessionOptions*>& _lsessionOptions, std::list<Plates_types_classifier*>& _detectors,
			std::list<unsigned int>& _detectors_ids);

		unsigned int InitYoloDetector(unsigned int len, const char* model_file);
		unsigned int InitPlatesClassifer(unsigned int len_models_filename, const char* model_file, unsigned int len_labels_filename, const char* labels_file);
		bool TwoStageALPR(const int width,//width of image
			const int height,//height of image i.e. the specified dimensions of the image
			const int pixOpt,// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
			void* pbData, unsigned int step,// source image bytes buffer
			unsigned int id_global_view,
			unsigned int id_focused_on_lp,
			unsigned int lpn_len, char* lpn, cv::Rect& bbox);
		bool TwoStageLPRPlatesTypeDetection(const int width,//width of image
			const int height,//height of image i.e. the specified dimensions of the image
			const int pixOpt,// pixel type : 1 (8 bpp greyscale image) 3 (RGB 24 bpp image) or 4 (RGBA 32 bpp image)
			void* pbData,
			unsigned int step,// source image bytes buffer
			unsigned int id_global_view,
			unsigned int id_focused_on_lp,
			unsigned int id_plates_types_classifier,
			unsigned int lpn_len,
			char* lpn,
			cv::Rect& bbox);
		bool CloseDetector(unsigned int id);
		bool ClosePlatesTypesClassifier(unsigned int id);

	private:
		// Version-negotiated OrtApi pointer: caps at the DLL's max supported API level so
		// that a newer SDK header (ORT_API_VERSION=22) paired with an older runtime DLL
		// (e.g. ORT 1.17.1, API ≤17) does not emit "[ORT ERROR] API version not available".
		// Mirrors the negotiation pattern used in EPLoader.cpp.
		const OrtApi* g_ort = []() -> const OrtApi* {
			const OrtApiBase* base = OrtGetApiBase();
			int dllMaxApi = ORT_API_VERSION;
			const char* verStr = base->GetVersionString();
			int major = 0, minor = 0;
			if (verStr && sscanf(verStr, "%d.%d", &major, &minor) == 2)
				dllMaxApi = minor;
			int targetApi = (ORT_API_VERSION < dllMaxApi) ? ORT_API_VERSION : dllMaxApi;
			return base->GetApi(targetApi);
		}();
		std::list<Ort::Env*> detectors_envs;
		std::list<Ort::SessionOptions*> l_detectors_sessionOptions;
		std::list<Yolov5_alpr_onxx_detector*> detectors;
		std::list<unsigned int> detectors_ids;
		std::list<Ort::Env*> plates_types_envs;
		std::list<Ort::SessionOptions*> l_plates_types_classifier_sessionOptions;
		std::list<Plates_types_classifier*> plates_types_classifiers;
		std::list<unsigned int> plates_types_classifier_ids;
	};
}
#endif