#include "ANSFilePlayer.h"
#include "ANSMatRegistry.h"
#include "ANSGpuFrameOps.h"
#include <memory>
#include <cstdint>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/frame.h>
#include "media_codec.h"
static bool ansfileplayerLicenceValid = false;
// Global once_flag to protect license checking
static std::once_flag ansfileplayerLicenseOnceFlag;
namespace ANSCENTER {
#define CHECK_CUDA(call) do { \
        cudaError_t err = call; \
        if (err != cudaSuccess) { \
            std::cout<<"CUDA error: " + std::string(cudaGetErrorString(err)); \
        } \
    } while (0)
#define CHECK_NVJPEG(call) do { \
        nvjpegStatus_t status = call; \
        if (status != NVJPEG_STATUS_SUCCESS) { \
            std::cout <<"nvJPEG error: " + std::to_string(status); \
        } \
    } while (0)
	ANSFILEPLAYER::ANSFILEPLAYER() {
		_url = "";
		_imageRotateDeg = 0;
		_imageWidth = 0;
		_imageHeight = 0;
		_pts = 0;
		_isPlaying = false;
		_lastJpegImage = "";
	}
	ANSFILEPLAYER::~ANSFILEPLAYER() noexcept {
		try {
			Destroy();
		}
		catch (...) {}
	}
	void ANSFILEPLAYER::Destroy() {
		decltype(_playerClient) clientToClose;
		{
			std::lock_guard<std::recursive_mutex> lock(_mutex);
			try {
				_url = "";
				_imageRotateDeg = 0;
				_isPlaying = false;
				_lastJpegImage = "";
				_pLastFrame.release();
				clientToClose = std::move(_playerClient);
			}
			catch (const std::exception& e) {
				_logger.LogError("ANSFILEPLAYER::Destroy. Exception:", e.what(), __FILE__, __LINE__);
			}
			catch (...) {
				_logger.LogError("ANSFILEPLAYER::Destroy.", "Unknown exception", __FILE__, __LINE__);
			}
		}
		// Destructor calls close() exactly once — do not call close() explicitly
		// beforehand. CFilePlayer::close() is not safe to call twice (it re-enters
		// decoder Stop/flush on an already-torn-down decoder).
		if (clientToClose) {
			clientToClose.reset();
		}
	}
	void ANSFILEPLAYER::CheckLicense() {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		try {
			_licenseValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(_licenseKey, 1007, "ANSCV");//Default productId=1005
		}
		catch (std::exception& e) {
			this->_logger.LogFatal("ANSFILEPLAYER::CheckLicense. Error:", e.what(), __FILE__, __LINE__);
		}
	}
	bool ANSFILEPLAYER::Init(std::string licenseKey, std::string url) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		_licenseKey = licenseKey;
		CheckLicense();
		if (!_licenseValid) {
			this->_logger.LogError("ANSFILEPLAYER::Init.", "Invalid license", __FILE__, __LINE__);
			return false;
		}
		//network_init();
		//sys_buf_init(200);
		//rtsp_parse_buf_init(200);
		//http_msg_buf_init(200);
		_url = url;
		return Setup();
	}
	void ANSFILEPLAYER::SetBBox(cv::Rect bbox) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		_playerClient->setBbox(bbox);
	}
	void ANSFILEPLAYER::SetCrop(bool crop) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		_playerClient->setCrop(crop);
	}
	bool ANSFILEPLAYER::Setup() {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		return _playerClient->open(_url);
	}
	bool ANSFILEPLAYER::Reconnect() {
		{
			std::lock_guard<std::recursive_mutex> lock(_mutex);
			_isPlaying = false;
		}
		// CFilePlayer::open() calls close() internally at the top — no need
		// to close explicitly here (doing so would double-close the decoder).
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		Setup();
		return Start();
	}
	bool ANSFILEPLAYER::Start() {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		_isPlaying = _playerClient->play();
		return _isPlaying;
	}
	bool ANSFILEPLAYER::Stop() {
		decltype(_playerClient.get()) player = nullptr;
		{
			std::lock_guard<std::recursive_mutex> lock(_mutex);
			player = _playerClient.get();
		}
		if (player && player->pause()) {
			std::lock_guard<std::recursive_mutex> lock(_mutex);
			_isPlaying = false;
			return true;
		}
		return false;
	}
	bool ANSFILEPLAYER::IsPaused() {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		return _playerClient->isPaused();
	}
	bool ANSFILEPLAYER::IsPlaying() {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		return _playerClient->isPlaying();
	}
	bool ANSFILEPLAYER::IsRecording() {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		return _playerClient->isRecording();
	}
	std::string ANSFILEPLAYER::GetJpegImage(int& width, int& height, int64_t& pts) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		// If the player is playing, process the frame
		if (_isPlaying) {
			// Get a new frame from the player client
			_lastJpegImage = _playerClient->getJpegImage(width, height, pts);
			// Update internal state variables
			_pts = pts;
			_imageWidth = width;
			_imageHeight = height;
			// Return the frame
			return _lastJpegImage;
		}
		// If the player is not playing, return the last known frame
		else {
			width = _imageWidth;
			height = _imageHeight;
			pts = _pts;
			return _lastJpegImage;
		}
	}
	cv::Mat ANSFILEPLAYER::GetImage(int& width, int& height, int64_t& pts) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		if (!_isPlaying) {
			width = _imageWidth;
			height = _imageHeight;
			pts = _pts;
			return _pLastFrame;
		}

		_pLastFrame.release();
		int imgW = 0, imgH = 0;
		_pLastFrame = _playerClient->getImage(imgW, imgH, pts);

		if (_pLastFrame.empty()) {
			width = _imageWidth;
			height = _imageHeight;
			pts = _pts;
			return _pLastFrame;
		}

		cv::Mat result = _pLastFrame;

		// Apply rotation if needed
		if (_imageRotateDeg >= 1 && _imageRotateDeg != 0) {
			cv::Point2f center(imgW / 2.0f, imgH / 2.0f);
			cv::Mat rotMat = cv::getRotationMatrix2D(center, _imageRotateDeg, 1.0);
			cv::Mat rotated;
			cv::warpAffine(result, rotated, rotMat, result.size(),
				cv::INTER_CUBIC, cv::BORDER_CONSTANT, cv::Scalar());
			result = rotated;
		}

		// Store full-res for inference (before display resize)
		_inferenceImage = result;

		// Resize for display if display resolution is set
		if (_displayWidth > 0 && _displayHeight > 0 &&
			(result.cols != _displayWidth || result.rows != _displayHeight)) {
			cv::Mat displayResult;
			cv::resize(result, displayResult, cv::Size(_displayWidth, _displayHeight),
				0, 0, cv::INTER_LINEAR);
			result = displayResult;
		}

		_pts = pts;
		width = result.cols;
		height = result.rows;
		_imageWidth = width;
		_imageHeight = height;
		return result;
	}
	void ANSFILEPLAYER::SetDisplayResolution(int width, int height) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		_displayWidth = width;
		_displayHeight = height;
	}
	cv::Mat ANSFILEPLAYER::GetInferenceImage() {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		return _inferenceImage;
	}
	void ANSFILEPLAYER::EnableAudio(bool status) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		_playerClient->enableAudio(status);
	}
	void ANSFILEPLAYER::SetAudioVolume(int volume) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		_playerClient->setVolume(volume);
	}
	std::string ANSFILEPLAYER::MatToBinaryData(const cv::Mat& image) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		if (!image.empty()) {
			if ((image.data != nullptr) && (image.u != nullptr)) {
				try {
					std::vector<uchar> imageData;
					bool success = cv::imencode(".jpg", image, imageData);
					if (!success) {
						this->_logger.LogError("ANSFILEPLAYER::MatToBinaryData. Error:", "Failed to encode the image.", __FILE__, __LINE__);
						return "";
					}
					std::string binaryData(imageData.begin(), imageData.end());
					return binaryData;
				}
				catch (const std::exception& e) {
					this->_logger.LogFatal("ANSFILEPLAYER::MatToBinaryData. Error:", e.what(), __FILE__, __LINE__);
					return "";
				}
				catch (...) {
					this->_logger.LogFatal("ANSFILEPLAYER::MatToBinaryData. Error:", "Caught unknown exception!", __FILE__, __LINE__);
					return "";
				}
			}
			else return "";
		}
		else return "";
	}



	
	static void VerifyGlobalANSFPLicense(const std::string& licenseKey) {
		try {
			ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1007, "ANSCV");//Default productId=1005
			if (!ansfileplayerLicenceValid) { // we also support ANSTS license
				ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1003, "ANSVIS");//Default productId=1003 (ANSVIS)
			}
			if (!ansfileplayerLicenceValid) { // we also support ANSTS license
				ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1008, "ANSTS");//Default productId=1008 (ANSTS)
			}
		}
		catch (std::exception& e) {
			ansfileplayerLicenceValid = false;
		}
	}

}

extern "C" __declspec(dllexport) int CreateANSFilePlayerHandle(ANSCENTER::ANSFILEPLAYER** Handle, const char* licenseKey, const char* url) {
	if (!Handle || !licenseKey || !url) return -1;
	try {
		auto ptr = std::make_unique<ANSCENTER::ANSFILEPLAYER>();
		bool result = ptr->Init(licenseKey, url);
		if (result) {
			*Handle = ptr.release();
			extern void anscv_unregister_handle(void*);
			extern void anscv_register_handle(void*, void(*)(void*));
			anscv_register_handle(*Handle, [](void* p) {
				auto* h = static_cast<ANSCENTER::ANSFILEPLAYER*>(p);
				try { h->Stop(); } catch (...) {}
				try { h->Destroy(); } catch (...) {}
				try { delete h; } catch (...) {}
			});
			return 1;
		}
		*Handle = nullptr;
		return 0;
	}
	catch (...) { return 0; }
}
extern "C" __declspec(dllexport) int ReleaseANSFilePlayerHandle(ANSCENTER::ANSFILEPLAYER** Handle) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		extern void anscv_unregister_handle(void*);
		anscv_unregister_handle(*Handle);
		// Destructor calls Destroy() — no need to call it explicitly (avoids double-destroy)
		std::unique_ptr<ANSCENTER::ANSFILEPLAYER> ptr(*Handle);
		*Handle = nullptr;
		return 0;
	}
	catch (...) {
		if (Handle) *Handle = nullptr;
		return 0;
	}
}
extern "C" __declspec(dllexport) int GetFilePlayerStrImage(ANSCENTER::ANSFILEPLAYER** Handle, int& width, int& height, int64_t& timeStamp, std::string& jpegImage) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		jpegImage = (*Handle)->GetJpegStringImage(width, height, timeStamp);
		if (!jpegImage.empty()) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error getting image data from FilePlayer client: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) int GetFilePlayerImage(ANSCENTER::ANSFILEPLAYER** Handle, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		std::string jpegString = (*Handle)->GetJpegStringImage(width, height, timeStamp);
		int size = jpegString.length();
		if (size > 0) {
			MgErr error;
			// Resize the jpegImage handle to hold the image data
			error = DSSetHandleSize(jpegImage, sizeof(int32) + size * sizeof(uChar));
			// Check if resizing the handle was successful
			if (error == noErr) {
				// Set the size of the image in the handle
				(*jpegImage)->cnt = size;
				// Use memcpy to copy the data from the std::string to the LStrHandle's str buffer
				memcpy((*jpegImage)->str, jpegString.c_str(), size);
				// Return success
				return 1;
			}
			else {
				// Return failure if there was an error in resizing the handle
				std::cerr << "Error resizing jpegImage handle: " << error << std::endl;
				return 0;
			}
		}
		else {
			// If the JPEG image string is empty, return failure
			std::cerr << "No image data retrieved from FilePlayer client." << std::endl;
			return 0;
		}
	}
	catch (const std::exception& e) {
		std::cerr << "Error getting image data from FilePlayer client: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) int GetFilePlayerCVImage(ANSCENTER::ANSFILEPLAYER** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image) {
	if (!Handle || !(*Handle) || !image) {
		std::cerr << "Error: Invalid input parameters in GetFilePlayerCVImage." << std::endl;
		return -1;
	}
	try {
		cv::Mat img = (*Handle)->GetImage(width, height, timeStamp);
		if (img.empty()) {
			return 0;
		}
		anscv_mat_replace(image, std::move(img));

		// Attach NV12/CUDA frames to registry for inference zero-copy (same as ANSRTSP/ANSVideoPlayer).
		// CFilePlayer inherits getNV12Frame()/getCudaHWFrame() from CVideoPlayer.
		int gpuIdx = (*Handle)->_playerClient->getHWDecodingGpuIndex();
		AVFrame* cudaHW = (*Handle)->_playerClient->getCudaHWFrame();
		if (cudaHW) {
			AVFrame* cpuNV12 = (*Handle)->_playerClient->getNV12Frame();
			gpu_frame_attach_cuda(*image, cudaHW, gpuIdx, timeStamp, cpuNV12);
		} else {
			AVFrame* nv12 = (*Handle)->_playerClient->getNV12Frame();
			if (nv12) {
				gpu_frame_attach(*image, nv12, gpuIdx, timeStamp);
			} else {
				// CPU-only fallback: attach full-res BGR clone for ANSRTYOLO
				cv::Mat infImg = (*Handle)->GetInferenceImage();
				if (!infImg.empty() && infImg.data != (*image)->data) {
					(*Handle)->_inferenceClonePrev = (*Handle)->_inferenceCloneCurr;
					(*Handle)->_inferenceCloneCurr = infImg.clone();

					GpuFrameData data{};
					data.avframe        = nullptr;
					data.yPlane         = (*Handle)->_inferenceCloneCurr.data;
					data.uvPlane        = nullptr;
					data.yLinesize      = static_cast<int>((*Handle)->_inferenceCloneCurr.step[0]);
					data.uvLinesize     = 0;
					data.width          = (*Handle)->_inferenceCloneCurr.cols;
					data.height         = (*Handle)->_inferenceCloneCurr.rows;
					data.pixelFormat    = 1000;  // ANSCV_PIX_FMT_BGR24
					data.gpuIndex       = -1;
					data.pts            = timeStamp;
					data.isCudaDevicePtr = false;
					data.cpuAvframe     = nullptr;
					data.cpuYPlane      = nullptr;
					data.cpuUvPlane     = nullptr;
					data.cpuYLinesize   = 0;
					data.cpuUvLinesize  = 0;

					ANSGpuFrameRegistry::instance().attach(*image, std::move(data));
					auto pending = ANSGpuFrameRegistry::instance().drain_pending();
					for (void* p : pending) {
						AVFrame* stale = static_cast<AVFrame*>(p);
						av_frame_free(&stale);
					}
				}
			}
		}

		return 1;
	}
	catch (const cv::Exception& e) {
		std::cerr << "OpenCV exception in GetFilePlayerCVImage: " << e.what() << std::endl;
		return -2;
	}
	catch (const std::exception& e) {
		std::cerr << "Exception in GetFilePlayerCVImage: " << e.what() << std::endl;
		return -2;
	}
	catch (...) {
		std::cerr << "Unknown exception in GetFilePlayerCVImage" << std::endl;
		return -2;
	}
}

extern "C" __declspec(dllexport) int StartFilePlayer(ANSCENTER::ANSFILEPLAYER** Handle) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		bool result = (*Handle)->Start();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error starting file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) int ReconnectFilePlayer(ANSCENTER::ANSFILEPLAYER** Handle) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		bool result = (*Handle)->Reconnect();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error reconnecting file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) int StopFilePlayer(ANSCENTER::ANSFILEPLAYER** Handle) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		bool result = (*Handle)->Stop();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error stopping file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) int IsFilePlayerPaused(ANSCENTER::ANSFILEPLAYER** Handle) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		bool result = (*Handle)->IsPaused();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error checking if file player is paused: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) int IsFilePlayerRunning(ANSCENTER::ANSFILEPLAYER** Handle) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		bool result = (*Handle)->IsPlaying();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error checking if file player is running: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) int IsFilePlayerRecording(ANSCENTER::ANSFILEPLAYER** Handle) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		bool result = (*Handle)->IsRecording();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error checking if file player is recording: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) void SetFilePlayerAudioVolume(ANSCENTER::ANSFILEPLAYER** Handle, int volume)
{
	if (Handle == nullptr || *Handle == nullptr) return;
	try {
		(*Handle)->SetAudioVolume(volume);
	}
	catch (...) { }
}
extern "C" __declspec(dllexport) void EnableFilePlayerAudioVolume(ANSCENTER::ANSFILEPLAYER** Handle, int status)
{
	if (Handle == nullptr || *Handle == nullptr) return;
	try {
		bool audioStatus = false;
		if (status == 1)audioStatus = true;
		(*Handle)->EnableAudio(audioStatus);
	}
	catch (const std::exception& e) {
		std::cerr << "Error enabling audio for file player: " << e.what() << std::endl;
	}
	catch (...) { }
}
extern "C" __declspec(dllexport) void SetFilePlayerImageRotation(ANSCENTER::ANSFILEPLAYER** Handle, double rotationAngle) {
	if (Handle == nullptr || *Handle == nullptr) return;
	try {
		(*Handle)->SetImageRotate(rotationAngle);
	}
	catch (...) { }
}
extern "C" __declspec(dllexport) int SetBBoxFilePlayer(ANSCENTER::ANSFILEPLAYER** Handle, int x, int y, int width, int height) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		cv::Rect bbox(x, y, width, height);
		(*Handle)->SetBBox(bbox);
		return 1;
	}
	catch (const std::exception& e) {
		std::cerr << "Error setting bounding box for file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}
extern "C" __declspec(dllexport) int SetCropFlagFilePlayer(ANSCENTER::ANSFILEPLAYER** Handle, int cropFlag) {
	if (Handle == nullptr || *Handle == nullptr) return -1;
	try {
		bool crop = false;
		if (cropFlag == 1)crop = true;
		(*Handle)->SetCrop(crop);
		return 1;
	}
	catch (const std::exception& e) {
		std::cerr << "Error setting crop flag for file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

// ============================================================================
// V2 functions: accept uint64_t handleVal by value (LabVIEW-safe)
// ============================================================================

extern "C" __declspec(dllexport) int GetFilePlayerImage_V2(uint64_t handleVal, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		std::string jpegString = h->GetJpegStringImage(width, height, timeStamp);
		int size = jpegString.length();
		if (size > 0) {
			MgErr error;
			error = DSSetHandleSize(jpegImage, sizeof(int32) + size * sizeof(uChar));
			if (error == noErr) {
				(*jpegImage)->cnt = size;
				memcpy((*jpegImage)->str, jpegString.c_str(), size);
				return 1;
			}
			else {
				std::cerr << "Error resizing jpegImage handle: " << error << std::endl;
				return 0;
			}
		}
		else {
			std::cerr << "No image data retrieved from FilePlayer client." << std::endl;
			return 0;
		}
	}
	catch (const std::exception& e) {
		std::cerr << "Error getting image data from FilePlayer client: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) int GetFilePlayerCVImage_V2(uint64_t handleVal, int& width, int& height, int64_t& timeStamp, cv::Mat** image) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h || !image) return -1;
	try {
		cv::Mat img = h->GetImage(width, height, timeStamp);
		if (img.empty()) {
			return 0;
		}
		anscv_mat_replace(image, std::move(img));

		// NV12/CUDA registry attach (same as V1)
		int gpuIdx = h->_playerClient->getHWDecodingGpuIndex();
		AVFrame* cudaHW = h->_playerClient->getCudaHWFrame();
		if (cudaHW) {
			AVFrame* cpuNV12 = h->_playerClient->getNV12Frame();
			gpu_frame_attach_cuda(*image, cudaHW, gpuIdx, timeStamp, cpuNV12);
		} else {
			AVFrame* nv12 = h->_playerClient->getNV12Frame();
			if (nv12) {
				gpu_frame_attach(*image, nv12, gpuIdx, timeStamp);
			} else {
				cv::Mat infImg = h->GetInferenceImage();
				if (!infImg.empty() && infImg.data != (*image)->data) {
					h->_inferenceClonePrev = h->_inferenceCloneCurr;
					h->_inferenceCloneCurr = infImg.clone();

					GpuFrameData data{};
					data.avframe        = nullptr;
					data.yPlane         = h->_inferenceCloneCurr.data;
					data.uvPlane        = nullptr;
					data.yLinesize      = static_cast<int>(h->_inferenceCloneCurr.step[0]);
					data.uvLinesize     = 0;
					data.width          = h->_inferenceCloneCurr.cols;
					data.height         = h->_inferenceCloneCurr.rows;
					data.pixelFormat    = 1000;
					data.gpuIndex       = -1;
					data.pts            = timeStamp;
					data.isCudaDevicePtr = false;
					data.cpuAvframe     = nullptr;
					data.cpuYPlane      = nullptr;
					data.cpuUvPlane     = nullptr;
					data.cpuYLinesize   = 0;
					data.cpuUvLinesize  = 0;

					ANSGpuFrameRegistry::instance().attach(*image, std::move(data));
					auto pending = ANSGpuFrameRegistry::instance().drain_pending();
					for (void* p : pending) {
						AVFrame* stale = static_cast<AVFrame*>(p);
						av_frame_free(&stale);
					}
				}
			}
		}

		return 1;
	}
	catch (const cv::Exception& e) {
		std::cerr << "OpenCV exception in GetFilePlayerCVImage_V2: " << e.what() << std::endl;
		return -2;
	}
	catch (const std::exception& e) {
		std::cerr << "Exception in GetFilePlayerCVImage_V2: " << e.what() << std::endl;
		return -2;
	}
	catch (...) {
		std::cerr << "Unknown exception in GetFilePlayerCVImage_V2" << std::endl;
		return -2;
	}
}

extern "C" __declspec(dllexport) int StartFilePlayer_V2(uint64_t handleVal) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		bool result = h->Start();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error starting file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) int ReconnectFilePlayer_V2(uint64_t handleVal) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		bool result = h->Reconnect();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error reconnecting file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) int StopFilePlayer_V2(uint64_t handleVal) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		bool result = h->Stop();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error stopping file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) int IsFilePlayerPaused_V2(uint64_t handleVal) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		bool result = h->IsPaused();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error checking if file player is paused: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) int IsFilePlayerRunning_V2(uint64_t handleVal) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		bool result = h->IsPlaying();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error checking if file player is running: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) int IsFilePlayerRecording_V2(uint64_t handleVal) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		bool result = h->IsRecording();
		if (result) return 1;
		else return 0;
	}
	catch (const std::exception& e) {
		std::cerr << "Error checking if file player is recording: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) void SetFilePlayerAudioVolume_V2(uint64_t handleVal, int volume) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return;
	try {
		h->SetAudioVolume(volume);
	}
	catch (...) { }
}

extern "C" __declspec(dllexport) void EnableFilePlayerAudioVolume_V2(uint64_t handleVal, int status) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return;
	try {
		bool audioStatus = false;
		if (status == 1) audioStatus = true;
		h->EnableAudio(audioStatus);
	}
	catch (const std::exception& e) {
		std::cerr << "Error enabling audio for file player: " << e.what() << std::endl;
	}
	catch (...) { }
}

extern "C" __declspec(dllexport) void SetFilePlayerImageRotation_V2(uint64_t handleVal, double rotationAngle) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return;
	try {
		h->SetImageRotate(rotationAngle);
	}
	catch (...) { }
}

extern "C" __declspec(dllexport) int SetBBoxFilePlayer_V2(uint64_t handleVal, int x, int y, int width, int height) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		cv::Rect bbox(x, y, width, height);
		h->SetBBox(bbox);
		return 1;
	}
	catch (const std::exception& e) {
		std::cerr << "Error setting bounding box for file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) int SetCropFlagFilePlayer_V2(uint64_t handleVal, int cropFlag) {
	auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
	if (!h) return -1;
	try {
		bool crop = false;
		if (cropFlag == 1) crop = true;
		h->SetCrop(crop);
		return 1;
	}
	catch (const std::exception& e) {
		std::cerr << "Error setting crop flag for file player: " << e.what() << std::endl;
		return -1;
	}
	catch (...) {
		return -1;
	}
}

extern "C" __declspec(dllexport) void SetFilePlayerDisplayResolution(ANSCENTER::ANSFILEPLAYER** Handle, int width, int height) {
	if (Handle == nullptr || *Handle == nullptr) return;
	try {
		(*Handle)->SetDisplayResolution(width, height);
	} catch (...) { }
}
