#ifndef ANSRTSP_H
#define ANSRTSP_H
#define ANSRTSP_API __declspec(dllexport)
#include <iostream>
#include "ANSLicense.h"
#include "LabVIEWHeader/extcode.h"
#include <vector>
#include "sys_inc.h"
#include "rtsp_cln.h"
#include "hqueue.h"
#include "http.h"
#include "http_parse.h"
#include "video_decoder.h"
#include "audio_decoder.h"
#include "rtsp_player.h"
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <atomic>
#include <condition_variable>

namespace ANSCENTER
{
	class ANSRTSP_API ANSRTSPClient
	{
	protected:
		std::unique_ptr<CRtspPlayer> _playerClient = std::make_unique<CRtspPlayer>();

		std::string  _username;
		std::string  _password;
		std::string  _url;
		bool		 _useFullURL;
		int _imageRotateDeg = 0;
		int _displayWidth = 0;   // 0 = no resize (return original resolution)
		int _displayHeight = 0;
		cv::Mat  _pLastFrame;
		std::string _lastJpegImage;
		int		 _imageWidth,_imageHeight;
		int64_t  _pts;
		bool	 _isPlaying;
		bool     _useNV12FastPath = false;  // false = original stable CPU path, true = NV12 GPU fast path
		std::recursive_mutex		_mutex;

			// --- Per-client inference guard ---
		// Tracks how many GPU frames from this client are currently in-flight
		// (grabbed by GetRTSPCVImage but not yet released after inference).
		// Destroy() waits for this to reach 0 before freeing NVDEC surfaces,
		// preventing the use-after-free crash when LabVIEW stops a camera
		// while AI inference is still reading CUDA device pointers.
		std::atomic<int>            _inFlightFrames{0};
		std::condition_variable_any _inFlightDone;
	public:
		void IncrementInFlight() { _inFlightFrames.fetch_add(1, std::memory_order_acq_rel); }
		void DecrementInFlight() {
			if (_inFlightFrames.fetch_sub(1, std::memory_order_acq_rel) <= 1) {
				_inFlightDone.notify_all();
			}
		}
		// Atomically check _isPlaying AND increment _inFlightFrames under the
		// same mutex.  Returns true if the caller may proceed to access CUDA
		// resources (GetCudaHWFrame + D2D copy).  Returns false if the player
		// is stopping/reconnecting — caller must NOT touch CUDA resources.
		//
		// This closes the race window where Reconnect() sets _isPlaying=false
		// and calls close() while GetRTSPCVImage is between GetCudaHWFrame()
		// and the D2D copy in gpu_frame_attach_cuda().
		bool TryIncrementInFlight() {
			std::lock_guard<std::recursive_mutex> lock(_mutex);
			if (!_isPlaying) return false;
			_inFlightFrames.fetch_add(1, std::memory_order_acq_rel);
			return true;
		}
		ANSRTSPClient();
		~ANSRTSPClient() noexcept;
		[[nodiscard]] bool Init(std::string licenseKey, std::string url);
		[[nodiscard]] bool Init(std::string licenseKey, std::string username, std::string password, std::string url);
		[[nodiscard]] bool Setup();
		[[nodiscard]] bool Reconnect();
		[[nodiscard]] bool Start();
		[[nodiscard]] bool IsPaused();
		[[nodiscard]] bool IsPlaying();
		[[nodiscard]] bool IsRecording();
		[[nodiscard]] bool Stop();
		[[nodiscard]] bool Pause();
		void Destroy();
		void SetBBox(cv::Rect bbox);
		void SetCrop(bool crop);
		void EnableAudio(bool status);
		void SetAudioVolume(int volume);
		[[nodiscard]] cv::Mat GetImage(int& width, int& height, int64_t& pts);
		[[nodiscard]] std::string MatToBinaryData(const cv::Mat& image);
		[[nodiscard]] std::string GetJpegImage(int& width, int& height, int64_t& pts);
		bool areImagesIdentical(const cv::Mat& img1, const cv::Mat& img2);
		void SetImageRotate(int mode) {
			std::lock_guard<std::recursive_mutex> lock(_mutex);
			_imageRotateDeg = mode;
			if (mode > 360) _imageRotateDeg = 360;
		};
		static void SetMaxHWDecoders(int maxDecoders);
		static int  AutoConfigureHWDecoders(int maxPerGpuOverride = 0);
		void SetHWDecoding(int hwMode, int preferredGpu = -1);
		bool IsHWDecodingActive();
		int  GetHWDecodingGpuIndex();
		void SetDisplayResolution(int width, int height);  // Set display output size; 0,0 = original (no resize)
		void SetImageQuality(int mode);  // 0=fast (AI), 1=quality (display)
		void SetTargetFPS(double intervalMs);  // Set min interval between processed frames in ms (0 = no limit, 100 = ~10 FPS, 200 = ~5 FPS)
		void SetNV12FastPath(bool enable);     // true = NV12 GPU fast path (zero-copy inference), false = original CPU path (stable)
		bool IsNV12FastPath() const { return _useNV12FastPath; }
		double GetLastFrameAgeMs();            // Milliseconds since last frame from decoder (detects truly stale cameras, unaffected by SetTargetFPS)
		AVFrame* GetNV12Frame();  // Returns cloned NV12 frame for GPU fast-path (caller must av_frame_free)
		AVFrame* GetCudaHWFrame(); // Returns CUDA HW frame (device ptrs) for zero-copy inference
		bool     IsCudaHWAccel();  // true when decoder uses CUDA (NV12 stays in GPU VRAM)
	public:
		void CheckLicense();
		SPDLogger&      _logger = SPDLogger::GetInstance("ANSRTSP", false);
		bool            _licenseValid{ false };
		std::string     _licenseKey;
		//std::once_flag licenseOnceFlag;

	};
}
extern "C" __declspec(dllexport) int CreateANSRTSPHandle(ANSCENTER::ANSRTSPClient * *Handle, const char* licenseKey, const char* username, const char* password, const char* url);
extern "C" __declspec(dllexport) int ReleaseANSRTSPHandle(ANSCENTER::ANSRTSPClient * *Handle);
extern "C" __declspec(dllexport) int GetRTSPImage(ANSCENTER::ANSRTSPClient **Handle, int &width, int &height, int64_t& timeStamp, LStrHandle jpegImage);
extern "C" __declspec(dllexport) int GetRTSPStrImage(ANSCENTER::ANSRTSPClient * *Handle, int& width, int& height, int64_t & timeStamp, std::string& jpegImage);
extern "C" __declspec(dllexport) int GetRTSPCVImage(ANSCENTER::ANSRTSPClient** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);

extern "C" __declspec(dllexport) int StartRTSP(ANSCENTER::ANSRTSPClient * *Handle);
extern "C" __declspec(dllexport) int ReconnectRTSP(ANSCENTER::ANSRTSPClient * *Handle);
extern "C" __declspec(dllexport) int StopRTSP(ANSCENTER::ANSRTSPClient * *Handle);
extern "C" __declspec(dllexport) int PauseRTSP(ANSCENTER::ANSRTSPClient** Handle);
extern "C" __declspec(dllexport) int IsRTSPPaused(ANSCENTER::ANSRTSPClient * *Handle);
extern "C" __declspec(dllexport) int IsRTSPRunning(ANSCENTER::ANSRTSPClient * *Handle);
extern "C" __declspec(dllexport) int IsRTSPRecording(ANSCENTER::ANSRTSPClient * *Handle);
extern "C" __declspec(dllexport) void SetRTSPAudioVolume(ANSCENTER::ANSRTSPClient * *Handle, int volume);
extern "C" __declspec(dllexport) void EnableRTSPAudioVolume(ANSCENTER::ANSRTSPClient * *Handle, int status);
extern "C" __declspec(dllexport) void SetRTSPImageRotation(ANSCENTER::ANSRTSPClient * *Handle, double rotationAngle);
extern "C" __declspec(dllexport) int SetBBoxRTSP(ANSCENTER::ANSRTSPClient** Handle, int x, int y, int width, int height);
extern "C" __declspec(dllexport) int SetCropFlagRTSP(ANSCENTER::ANSRTSPClient** Handle, int cropFlag);
extern "C" __declspec(dllexport) void SetMaxHWDecoders(int maxDecoders);
extern "C" __declspec(dllexport) int  AutoConfigureHWDecoders(int maxPerGpuOverride);
extern "C" __declspec(dllexport) void SetRTSPHWDecoding(ANSCENTER::ANSRTSPClient** Handle, int hwMode, int preferredGpu = -1);
extern "C" __declspec(dllexport) int  IsRTSPHWDecodingActive(ANSCENTER::ANSRTSPClient** Handle);
extern "C" __declspec(dllexport) int  GetRTSPHWDecodingGpuIndex(ANSCENTER::ANSRTSPClient** Handle);
extern "C" __declspec(dllexport) void SetRTSPImageQuality(ANSCENTER::ANSRTSPClient** Handle, int mode);
extern "C" __declspec(dllexport) void SetRTSPDisplayResolution(ANSCENTER::ANSRTSPClient** Handle, int width, int height);
extern "C" __declspec(dllexport) void SetRTSPTargetFPS(ANSCENTER::ANSRTSPClient** Handle, double intervalMs);
extern "C" __declspec(dllexport) void SetRTSPNV12FastPath(ANSCENTER::ANSRTSPClient** Handle, int enable);
extern "C" __declspec(dllexport) double GetRTSPLastFrameAgeMs(ANSCENTER::ANSRTSPClient** Handle);
#endif