#include "ANSTrainingEngine.h"
#include "ANSODTraininingEngine.h"
#include "ANSYOLOTrainingEngine.h"
#include "ANSYOLOCLTrainingEngine.h"
#include "ANSYOLOSEGTrainingEngine.h"
#include "ANSYOLOOBBTrainingEngine.h"
#include "Utility.h"
#include <random>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <regex>
#include <string>
#include <vector>
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
#include <unordered_map>
#include <condition_variable>

// Handle registry with refcount — prevents use-after-free when
// ReleaseANSTREHandle is called while an operation is still running.
static std::unordered_map<ANSCENTER::ANSTRE*, int>& TREHandleRegistry() {
    static std::unordered_map<ANSCENTER::ANSTRE*, int> s;
    return s;
}
static std::mutex& TREHandleRegistryMutex() {
    static std::mutex m;
    return m;
}
static std::condition_variable& TREHandleRegistryCV() {
    static std::condition_variable cv;
    return cv;
}

static void RegisterTREHandle(ANSCENTER::ANSTRE* h) {
    std::lock_guard<std::mutex> lk(TREHandleRegistryMutex());
    TREHandleRegistry()[h] = 1;
}

static ANSCENTER::ANSTRE* AcquireTREHandle(ANSCENTER::ANSTRE* h) {
    std::lock_guard<std::mutex> lk(TREHandleRegistryMutex());
    auto it = TREHandleRegistry().find(h);
    if (it == TREHandleRegistry().end()) return nullptr;
    it->second++;
    return h;
}

static bool ReleaseTREHandleRef(ANSCENTER::ANSTRE* h) {
    std::lock_guard<std::mutex> lk(TREHandleRegistryMutex());
    auto it = TREHandleRegistry().find(h);
    if (it == TREHandleRegistry().end()) return false;
    it->second--;
    if (it->second <= 0) {
        TREHandleRegistry().erase(it);
        TREHandleRegistryCV().notify_all();
        return true;
    }
    return false;
}

static bool UnregisterTREHandle(ANSCENTER::ANSTRE* h) {
    std::unique_lock<std::mutex> lk(TREHandleRegistryMutex());
    auto it = TREHandleRegistry().find(h);
    if (it == TREHandleRegistry().end()) return false;
    it->second--;
    bool ok = TREHandleRegistryCV().wait_for(lk, std::chrono::seconds(30), [&]() {
        auto it2 = TREHandleRegistry().find(h);
        return it2 == TREHandleRegistry().end() || it2->second <= 0;
    });
    if (!ok) {
        OutputDebugStringA("WARNING: UnregisterTREHandle timed out waiting for in-flight operations\n");
    }
    TREHandleRegistry().erase(h);
    return true;
}

// RAII guard — ensures ReleaseTREHandleRef is always called
class TREHandleGuard {
    ANSCENTER::ANSTRE* engine;
public:
    explicit TREHandleGuard(ANSCENTER::ANSTRE* e) : engine(e) {}
    ~TREHandleGuard() { if (engine) ReleaseTREHandleRef(engine); }
    ANSCENTER::ANSTRE* get() const { return engine; }
    explicit operator bool() const { return engine != nullptr; }
    TREHandleGuard(const TREHandleGuard&) = delete;
    TREHandleGuard& operator=(const TREHandleGuard&) = delete;
};

template <typename T>
T getData(const boost::property_tree::ptree& pt, const std::string& key)
{
	T ret;
	if (boost::optional<T> data = pt.get_optional<T>(key))
	{
		ret = data.get();
	}
	return ret;
}
namespace ANSCENTER
{	
	bool ANSTRE::IsProcessRunning(const char* processName){
		bool isRunning = false;
		PROCESSENTRY32 processEntry;
		processEntry.dwSize = sizeof(PROCESSENTRY32);

		// Create a snapshot of all running processes
		HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

		if (snapshot == INVALID_HANDLE_VALUE) {
			// Handle error (e.g., log or return false)
			return false;
		}

		if (Process32First(snapshot, &processEntry)) {
			do {
#ifdef UNICODE
				// Convert process name to wide string using proper UTF-8 conversion
				int wideLen = MultiByteToWideChar(CP_UTF8, 0, processName, -1, nullptr, 0);
				std::wstring wProcessName(wideLen > 0 ? wideLen - 1 : 0, L'\0');
				if (wideLen > 0) MultiByteToWideChar(CP_UTF8, 0, processName, -1, &wProcessName[0], wideLen);
				if (_wcsicmp(processEntry.szExeFile, wProcessName.c_str()) == 0) {
#else
				// Compare the process name with the target process name (case-insensitive)
				if (_stricmp(processEntry.szExeFile, processName) == 0) {
#endif
					isRunning = true;
					break;
				}
				} while (Process32Next(snapshot, &processEntry));
		}

		CloseHandle(snapshot);
		return isRunning;
	}
	bool ANSTRE::ZipExperiment(std::string projectName, int experimentNumber) {
		bool zipModelExist = false;
		_projectName = Space2Underscore(projectName);
		std::string projectPath = FindFilePathInFileList(_projects, _projectName);
		if (FolderExist(projectPath)) {
			std::string experimentPath = projectPath + "\\experiments\\" + std::to_string(experimentNumber);
			std::string zipExperimentPath = experimentPath + ".zip";
			std::string preTrainWeightFile = experimentPath + "\\train_last.pt";
			if (FileExist(zipExperimentPath))fs::remove(zipExperimentPath);
			if (ZipFolderWithPassword(experimentPath.c_str(), zipExperimentPath.c_str(), _zipPassword.c_str())) {
				DeleteFolder(experimentPath);
			}
			return FileExist(zipExperimentPath);
		}
		else {
			this->_logger.LogError("ANYLTRE::ANSTRE::ZipExperimen", "Invalid project path.", __FILE__, __LINE__);
			return false;
		}
	}
	double ANSTRE::ParseObjectDetectionMAP(std::string stEvaluationResult) {
		std::regex valuePattern("all\\s+\\d+\\s+\\d+\\s+\\d\\.\\d+\\s+\\d\\.\\d+\\s+\\d\\.\\d+\\s+(\\d\\.\\d+)");
		std::smatch matches;
		if (std::regex_search(stEvaluationResult, matches, valuePattern) && matches.size() > 1) {
			return std::atof(matches[1].str().c_str());
		}
		else {
			return 0;
		}
	}		
	std::string ANSTRE::GetLatestValidFolderAndDeleteOthers(const std::string& projectName, const std::string& rootPath) {
		if (!fs::exists(rootPath) || !fs::is_directory(rootPath)) {
			std::cerr << "Error: Root path is invalid or not a directory." << std::endl;
			return "";
		}
		std::vector<std::string> validFolderPath;
		// Step 1: Scan all subdirectories matching projectName + number
		for (const auto& entry : fs::directory_iterator(rootPath)) {
			if (entry.is_directory()) {
				//check if the folder has weights/best.pt
				// convert entry.path() to string
				std::string modelPath = entry.path().string() + "\\weights\\best.pt";
				// check if this model path is exists
				if (FileExist(modelPath))
				{
					validFolderPath.emplace_back(entry.path().string());
				}
				else {
					//delete the folder
					std::error_code ec;
					fs::remove_all(entry.path(), ec);
				}
			}
		}
		// Step 2: the latest folder
		fs::path selectedFolder;
		if (!validFolderPath.empty()) {
			selectedFolder = validFolderPath.back();
		}
		else {
			return "";
		}
		// Step 3: Delete all other folders except for the selected one
		if (validFolderPath.size() > 1) {
			for (const auto& folder : validFolderPath) {
				if (folder != selectedFolder && fs::exists(folder)) {
					std::error_code ec;
					fs::remove_all(folder, ec);
				}
			}
		}
		// Step 5: Rename the selected folder to the project name
		std::string newFolderName = rootPath + "\\" + projectName;
		//if selectedFolder is already the same as newFolderName, return
		if (selectedFolder == newFolderName) {
			return selectedFolder.string();
		}
		else {// rename the selected folder to the project name
			std::error_code ec;
			fs::rename(selectedFolder, newFolderName, ec);
			// return new folder name
			return newFolderName;
		}
	}
	std::string	ANSTRE::ExecuteCommand(const char* cmd) {

		std::string result;
		// Check if the command string is empty
		if (cmd == nullptr || std::string(cmd).empty()) {
			return "";  // Or simply return an empty string if no message is needed
		}

		STARTUPINFOA si;
		PROCESS_INFORMATION pi;
		SECURITY_ATTRIBUTES sa;
		HANDLE hStdOutRead, hStdOutWrite;

		// Set up the security attributes struct
		sa.nLength = sizeof(SECURITY_ATTRIBUTES);
		sa.bInheritHandle = TRUE;
		sa.lpSecurityDescriptor = nullptr;

		// Create a pipe for the child process's STDOUT
		if (!CreatePipe(&hStdOutRead, &hStdOutWrite, &sa, 0)) {
			this->_logger.LogFatal("ANSTRE::ExecuteCommand()", "Failed to create pipe", __FILE__, __LINE__);
			return "";
		}

		// Ensure the read handle to the pipe for STDOUT is not inherited
		SetHandleInformation(hStdOutRead, HANDLE_FLAG_INHERIT, 0);

		// Set up STARTUPINFO structure
		ZeroMemory(&si, sizeof(si));
		si.cb = sizeof(si);
		si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
		si.wShowWindow = SW_HIDE;
		si.hStdOutput = hStdOutWrite;
		si.hStdError = hStdOutWrite;

		// Create the child process
		std::string command = "cmd.exe /C " + std::string(cmd);
		ZeroMemory(&pi, sizeof(pi));

		if (!CreateProcessA(nullptr, const_cast<char*>(command.c_str()), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &pi)) {
			this->_logger.LogFatal("ANSTRE::ExecuteCommand()", "Failed to start process", __FILE__, __LINE__);
			CloseHandle(hStdOutWrite);
			CloseHandle(hStdOutRead);
			return "";
		}

		// Close the write end of the pipe before reading from the read end
		CloseHandle(hStdOutWrite);
		// Read output from the child process
		DWORD dwRead;
		CHAR chBuf[4096];
		ULONGLONG startTime = GetTickCount64();
		ULONGLONG timeoutDuration = 180000; // 3 minutes
		while (ReadFile(hStdOutRead, chBuf, 4096, &dwRead, nullptr) && dwRead != 0) {
			result.append(chBuf, dwRead);
			if ((GetTickCount64() - startTime) > timeoutDuration) break;
		}

		// Close the read handle of the pipe
		CloseHandle(hStdOutRead);

		// Close process and thread handles
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);

		return result;
	}
	std::vector<std::string> ANSTRE::ParseANSClass(const std::string& ansClassFileName) {
		std::vector<std::string> classNames;
		// Create a property tree and parse the JSON file
		boost::property_tree::ptree pt;
		try {
			boost::property_tree::read_json(ansClassFileName, pt);

			// Iterate directly through the array
			for (const auto& arrayElement : pt) {
				classNames.push_back(arrayElement.second.get<std::string>("ClassName"));
			}
		}
		catch (const boost::property_tree::ptree_bad_path& e) {
			std::cerr << "Error: " << e.what() << std::endl;
		}
		catch (const boost::property_tree::json_parser_error& e) {
			std::cerr << "JSON Parsing Error: " << e.what() << std::endl;
		}
		return classNames;
	}
	std::string ANSTRE::ConvertVectorToClass(const std::vector<std::string>& vec) {
		std::ostringstream oss;
		oss << "[";
		for (size_t i = 0; i < vec.size(); ++i) 
		{
			oss << vec[i];
			if (i < vec.size() - 1) 
			{
				oss << ",";
			}
		}
		oss << "]";
		return oss.str();
	}
}

extern "C" ANSTRE_API int  CreateANSTREHandle(ANSCENTER::ANSTRE** Handle, const char* licenseKey, const char* projectDirectory, const char* engineDirectory, const char* modelTemplateDirectory, const char* modelZipPassword, int trainingEngineType, int latestEngine) {
	if (Handle == nullptr) return 0;
	if (licenseKey == nullptr || projectDirectory == nullptr || engineDirectory == nullptr || modelTemplateDirectory == nullptr || modelZipPassword == nullptr) return 0;
	// Release existing handle if called twice (prevents leak from LabVIEW)
	if (*Handle) {
		if (UnregisterTREHandle(*Handle)) {
			delete *Handle;
		}
		*Handle = nullptr;
	}
	try {
		switch (trainingEngineType)
		{
		case 0:
			(*Handle) = new ANSCENTER::ANSODTRE();
			break;
		case 1:
			(*Handle) = new ANSCENTER::ANYLTRE();
			break;
		case 2:
			(*Handle) = new ANSCENTER::ANYLCLTRE();
			break;
		case 3:
			(*Handle) = new ANSCENTER::ANYLSEGTRE();
			break;
		case 4:
			(*Handle) = new ANSCENTER::ANYLOBBTRE();
			break;
		default:
			(*Handle) = new ANSCENTER::ANSODTRE();
			break;
		}
		if (*Handle == nullptr) return 0;
		bool isLatestEngine = (latestEngine == 1) ? true : false;
		if ((*Handle)->Init(licenseKey, projectDirectory, engineDirectory, modelTemplateDirectory, modelZipPassword, isLatestEngine)) {
			RegisterTREHandle(*Handle);
			return 1;
		}
		delete *Handle; *Handle = nullptr;
		return 0;
	}
	catch (std::exception& e) {
		if (*Handle != nullptr) { delete *Handle; *Handle = nullptr; }
		return 0;
	}
	catch (...) {
		if (*Handle != nullptr) { delete *Handle; *Handle = nullptr; }
		return 0;
	}
}
static int ReleaseANSTREHandle_Impl(ANSCENTER::ANSTRE** Handle) {
	try {
		if (!Handle || !*Handle) return 1;
		if (!UnregisterTREHandle(*Handle)) {
			*Handle = nullptr;
			return 1;
		}
		delete *Handle;
		*Handle = nullptr;
		return 1;
	}
	catch (...) {
		if (Handle) *Handle = nullptr;
		return 0;
	}
}

extern "C" ANSTRE_API int  ReleaseANSTREHandle(ANSCENTER::ANSTRE** Handle) {
	__try {
		return ReleaseANSTREHandle_Impl(Handle);
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {
		if (Handle) *Handle = nullptr;
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjects(ANSCENTER::ANSTRE** Handle, LStrHandle strProjects) {
	if (Handle == nullptr || strProjects == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::vector<std::string> projects = guard.get()->GetProjects();
		std::string st = VectorToSeparatedString(projects);
		int size = (int)st.length();
		MgErr error;
		error = DSSetHandleSize(strProjects, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*strProjects)->cnt = size;
			memcpy((*strProjects)->str, st.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjectExperiments(ANSCENTER::ANSTRE** Handle, const char* projectName, LStrHandle strProjectExperiments) {
	if (Handle == nullptr || projectName == nullptr || strProjectExperiments == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::vector<std::string> experiments = guard.get()->GetProjectExperiments(projectName);
		std::string st = VectorToSeparatedString(experiments);
		int size = (int)st.length();
		MgErr error;
		error = DSSetHandleSize(strProjectExperiments, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*strProjectExperiments)->cnt = size;
			memcpy((*strProjectExperiments)->str, st.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_CreateProject(ANSCENTER::ANSTRE** Handle, const char* projectName) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->CreateProject(stProjectName))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_DeleteProject(ANSCENTER::ANSTRE** Handle, const char* projectName) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->DeleteProject(stProjectName))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_SetWorkingDirectory(ANSCENTER::ANSTRE** Handle, const char* workingDirectory) {
	if (Handle == nullptr || workingDirectory == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stWorkingDirectory = workingDirectory;
		if (guard.get()->SetWorkingDirectory(stWorkingDirectory))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_UploadTrainingData(ANSCENTER::ANSTRE** Handle, const char* projectName) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->UploadTrainingData(stProjectName))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_CreateTrainingEngine(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, int extractorType, long numberStep, int batchSize, double learningRate) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->CreateTrainingEngine(stProjectName, experimentNumber, extractorType, numberStep, batchSize, learningRate))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjectExperimentStatus(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, LStrHandle projectStatus) {
	if (Handle == nullptr || projectName == nullptr || projectStatus == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		std::string stProjectStatus = guard.get()->GetProjectExperimentStatus(stProjectName, experimentNumber);
		int size = (int)stProjectStatus.length();
		MgErr error;
		error = DSSetHandleSize(projectStatus, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*projectStatus)->cnt = size;
			memcpy((*projectStatus)->str, stProjectStatus.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GenerateTrainingCommand(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, int extractorType, LStrHandle trainingCommand) {
	if (Handle == nullptr || projectName == nullptr || trainingCommand == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		std::string stTrainingCommand = guard.get()->GenerateTrainingCommand(stProjectName, experimentNumber, extractorType);
		int size = (int)stTrainingCommand.length();
		MgErr error;
		error = DSSetHandleSize(trainingCommand, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*trainingCommand)->cnt = size;
			memcpy((*trainingCommand)->str, stTrainingCommand.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GenerateCustomTrainingCommand(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, int extractorType, const char* pretrainedModel, LStrHandle trainingCommand) {
	if (Handle == nullptr || projectName == nullptr || pretrainedModel == nullptr || trainingCommand == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		std::string stTrainingCommand = guard.get()->GenerateCustomTrainingCommand(stProjectName, experimentNumber, extractorType, pretrainedModel);
		int size = (int)stTrainingCommand.length();
		MgErr error;
		error = DSSetHandleSize(trainingCommand, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*trainingCommand)->cnt = size;
			memcpy((*trainingCommand)->str, stTrainingCommand.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_EvaluateModel(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, LStrHandle mAPResult) {
	if (Handle == nullptr || projectName == nullptr || mAPResult == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		std::string stmAPResult = guard.get()->EvaluateModel(stProjectName, experimentNumber);
		int size = (int)stmAPResult.length();
		MgErr error;
		error = DSSetHandleSize(mAPResult, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*mAPResult)->cnt = size;
			memcpy((*mAPResult)->str, stmAPResult.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_DownloadModel(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, const char* downloadDirDestination, int modelMode)
{
	if (Handle == nullptr || projectName == nullptr || downloadDirDestination == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		bool downloadResult = guard.get()->DownloadModel(stProjectName, experimentNumber, downloadDirDestination, modelMode);
		if (downloadResult)return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjectDirectory(ANSCENTER::ANSTRE** Handle, LStrHandle strProjectDir) {
	if (Handle == nullptr || strProjectDir == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectDir = guard.get()->GetProjectDirectory();
		int size = (int)stProjectDir.length();
		MgErr error;
		error = DSSetHandleSize(strProjectDir, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*strProjectDir)->cnt = size;
			memcpy((*strProjectDir)->str, stProjectDir.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_ZipExperiment(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->ZipExperiment(stProjectName, experimentNumber))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API double  ANSTRE_ParseMAP(ANSCENTER::ANSTRE** Handle, const char* evaluationResult) {
	if (Handle == nullptr || evaluationResult == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stEvaluationResult = evaluationResult;
		return guard.get()->ParseObjectDetectionMAP(stEvaluationResult);
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_ParseTrainingResults(ANSCENTER::ANSTRE** Handle, const char* trainingResult, LStrHandle strResult) {
	if (Handle == nullptr || trainingResult == nullptr || strResult == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stTrainingResult = trainingResult;
		std::string stResult = guard.get()->ParseTrainingResults(stTrainingResult);
		int size = (int)stResult.length();
		MgErr error;
		error = DSSetHandleSize(strResult, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*strResult)->cnt = size;
			memcpy((*strResult)->str, stResult.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}

//CPP interface
extern "C" ANSTRE_API int  ANSTRE_GetProjects_CPP(ANSCENTER::ANSTRE** Handle, std::string& strProjects) {
	if (Handle == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::vector<std::string> projects = guard.get()->GetProjects();
		strProjects = VectorToSeparatedString(projects);
		return 1;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjectExperiments_CPP(ANSCENTER::ANSTRE** Handle, const char* projectName, std::string& strProjectExperiments) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::vector<std::string> experiments = guard.get()->GetProjectExperiments(projectName);
		strProjectExperiments = VectorToSeparatedString(experiments);
		return 1;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjectExperimentStatus_CPP(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, std::string& projectStatus) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		projectStatus = guard.get()->GetProjectExperimentStatus(stProjectName, experimentNumber);
		return 1;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GenerateTrainingCommand_CPP(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, int extractorType, std::string& trainingCommand) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		trainingCommand = guard.get()->GenerateTrainingCommand(stProjectName, experimentNumber, extractorType);
		return 1;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GenerateCustomTrainingCommand_CPP(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, int extractorType, const char* pretrainedModel, std::string& trainingCommand) {
	if (Handle == nullptr || projectName == nullptr || pretrainedModel == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		trainingCommand = guard.get()->GenerateCustomTrainingCommand(stProjectName, experimentNumber, extractorType, pretrainedModel);
		return 1;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_EvaluateModel_CPP(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, std::string& mAPResult) {
	if (Handle == nullptr || projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		mAPResult = guard.get()->EvaluateModel(stProjectName, experimentNumber);
		return 1;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_EvaluateModelOnTestData_CPP(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, const char* dataFolder,std::string& evaluationCommand) {
	if (Handle == nullptr || projectName == nullptr || dataFolder == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		bool zipModelExist = false;
		evaluationCommand = guard.get()->GenerateTestCommand(stProjectName, experimentNumber, zipModelExist, dataFolder);
		if (evaluationCommand.empty()) {
			return 0;
		}
		else {
			return 1;
		}
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_EvaluateModelOnTestData(ANSCENTER::ANSTRE** Handle, const char* projectName, int experimentNumber, const char* dataFolder, LStrHandle evaluationCommand) {
	if (Handle == nullptr || projectName == nullptr || dataFolder == nullptr || evaluationCommand == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		bool zipModelExist = false;
		std::string evaluationCommandStr = guard.get()->GenerateTestCommand(stProjectName, experimentNumber, zipModelExist, dataFolder);
		int size = (int)evaluationCommandStr.length();
		MgErr error;
		error = DSSetHandleSize(evaluationCommand, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*evaluationCommand)->cnt = size;
			memcpy((*evaluationCommand)->str, evaluationCommandStr.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}


extern "C" ANSTRE_API int  ANSTRE_GetProjectDirectory_CPP(ANSCENTER::ANSTRE** Handle, std::string& strProjectDir) {
	if (Handle == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		strProjectDir = guard.get()->GetProjectDirectory();
		return 1;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_ParseTrainingResults_CPP(ANSCENTER::ANSTRE** Handle, const char* trainingResult, std::string& strResult) {
	if (Handle == nullptr || trainingResult == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		std::string stTrainingResult = trainingResult;
		strResult = guard.get()->ParseTrainingResults(stTrainingResult);
		return 1;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_CheckEngineStatus(ANSCENTER::ANSTRE** Handle) {
	if (Handle == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		bool bResult = guard.get()->CheckEngineStatus();
		if (bResult) return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_CheckEngine(ANSCENTER::ANSTRE** Handle) {
	if (Handle == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(*Handle));
	if (!guard) return 0;
	try {
		bool bResult = guard.get()->CheckEngine();
		if (bResult) return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}

// ============================================================================
// V2 API — accepts uint64_t handleVal by value instead of ANSTRE** Handle.
// Eliminates LabVIEW buffer-reuse bug when concurrent calls share the same
// Handle** buffer address.
// ============================================================================

extern "C" ANSTRE_API int  ANSTRE_GetProjects_V2(uint64_t handleVal, LStrHandle strProjects) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (strProjects == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::vector<std::string> projects = guard.get()->GetProjects();
		std::string st = VectorToSeparatedString(projects);
		int size = (int)st.length();
		MgErr error;
		error = DSSetHandleSize(strProjects, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*strProjects)->cnt = size;
			memcpy((*strProjects)->str, st.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjectExperiments_V2(uint64_t handleVal, const char* projectName, LStrHandle strProjectExperiments) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr || strProjectExperiments == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::vector<std::string> experiments = guard.get()->GetProjectExperiments(projectName);
		std::string st = VectorToSeparatedString(experiments);
		int size = (int)st.length();
		MgErr error;
		error = DSSetHandleSize(strProjectExperiments, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*strProjectExperiments)->cnt = size;
			memcpy((*strProjectExperiments)->str, st.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_CreateProject_V2(uint64_t handleVal, const char* projectName) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->CreateProject(stProjectName))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_DeleteProject_V2(uint64_t handleVal, const char* projectName) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->DeleteProject(stProjectName))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_SetWorkingDirectory_V2(uint64_t handleVal, const char* workingDirectory) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (workingDirectory == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stWorkingDirectory = workingDirectory;
		if (guard.get()->SetWorkingDirectory(stWorkingDirectory))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_UploadTrainingData_V2(uint64_t handleVal, const char* projectName) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->UploadTrainingData(stProjectName))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_CreateTrainingEngine_V2(uint64_t handleVal, const char* projectName, int experimentNumber, int extractorType, long numberStep, int batchSize, double learningRate) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->CreateTrainingEngine(stProjectName, experimentNumber, extractorType, numberStep, batchSize, learningRate))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjectExperimentStatus_V2(uint64_t handleVal, const char* projectName, int experimentNumber, LStrHandle projectStatus) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr || projectStatus == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		std::string stProjectStatus = guard.get()->GetProjectExperimentStatus(stProjectName, experimentNumber);
		int size = (int)stProjectStatus.length();
		MgErr error;
		error = DSSetHandleSize(projectStatus, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*projectStatus)->cnt = size;
			memcpy((*projectStatus)->str, stProjectStatus.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GenerateTrainingCommand_V2(uint64_t handleVal, const char* projectName, int experimentNumber, int extractorType, LStrHandle trainingCommand) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr || trainingCommand == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		std::string stTrainingCommand = guard.get()->GenerateTrainingCommand(stProjectName, experimentNumber, extractorType);
		int size = (int)stTrainingCommand.length();
		MgErr error;
		error = DSSetHandleSize(trainingCommand, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*trainingCommand)->cnt = size;
			memcpy((*trainingCommand)->str, stTrainingCommand.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GenerateCustomTrainingCommand_V2(uint64_t handleVal, const char* projectName, int experimentNumber, int extractorType, const char* pretrainedModel, LStrHandle trainingCommand) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr || pretrainedModel == nullptr || trainingCommand == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		std::string stTrainingCommand = guard.get()->GenerateCustomTrainingCommand(stProjectName, experimentNumber, extractorType, pretrainedModel);
		int size = (int)stTrainingCommand.length();
		MgErr error;
		error = DSSetHandleSize(trainingCommand, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*trainingCommand)->cnt = size;
			memcpy((*trainingCommand)->str, stTrainingCommand.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_EvaluateModel_V2(uint64_t handleVal, const char* projectName, int experimentNumber, LStrHandle mAPResult) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr || mAPResult == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		std::string stmAPResult = guard.get()->EvaluateModel(stProjectName, experimentNumber);
		int size = (int)stmAPResult.length();
		MgErr error;
		error = DSSetHandleSize(mAPResult, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*mAPResult)->cnt = size;
			memcpy((*mAPResult)->str, stmAPResult.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_EvaluateModelOnTestData_V2(uint64_t handleVal, const char* projectName, int experimentNumber, const char* dataFolder, LStrHandle evaluationCommand) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr || dataFolder == nullptr || evaluationCommand == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		bool zipModelExist = false;
		std::string evaluationCommandStr = guard.get()->GenerateTestCommand(stProjectName, experimentNumber, zipModelExist, dataFolder);
		int size = (int)evaluationCommandStr.length();
		MgErr error;
		error = DSSetHandleSize(evaluationCommand, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*evaluationCommand)->cnt = size;
			memcpy((*evaluationCommand)->str, evaluationCommandStr.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_DownloadModel_V2(uint64_t handleVal, const char* projectName, int experimentNumber, const char* downloadDirDestination, int modelMode) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr || downloadDirDestination == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		bool downloadResult = guard.get()->DownloadModel(stProjectName, experimentNumber, downloadDirDestination, modelMode);
		if (downloadResult)return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_GetProjectDirectory_V2(uint64_t handleVal, LStrHandle strProjectDir) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (strProjectDir == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectDir = guard.get()->GetProjectDirectory();
		int size = (int)stProjectDir.length();
		MgErr error;
		error = DSSetHandleSize(strProjectDir, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*strProjectDir)->cnt = size;
			memcpy((*strProjectDir)->str, stProjectDir.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_ZipExperiment_V2(uint64_t handleVal, const char* projectName, int experimentNumber) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (projectName == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stProjectName = projectName;
		if (guard.get()->ZipExperiment(stProjectName, experimentNumber))return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API double  ANSTRE_ParseMAP_V2(uint64_t handleVal, const char* evaluationResult) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0.0;
	if (evaluationResult == nullptr) return 0.0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0.0;
	try {
		std::string stEvaluationResult = evaluationResult;
		return guard.get()->ParseObjectDetectionMAP(stEvaluationResult);
	}
	catch (...) {
		return 0.0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_ParseTrainingResults_V2(uint64_t handleVal, const char* trainingResult, LStrHandle strResult) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	if (trainingResult == nullptr || strResult == nullptr) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		std::string stTrainingResult = trainingResult;
		std::string stResult = guard.get()->ParseTrainingResults(stTrainingResult);
		int size = (int)stResult.length();
		MgErr error;
		error = DSSetHandleSize(strResult, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*strResult)->cnt = size;
			memcpy((*strResult)->str, stResult.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_CheckEngineStatus_V2(uint64_t handleVal) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		bool bResult = guard.get()->CheckEngineStatus();
		if (bResult) return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" ANSTRE_API int  ANSTRE_CheckEngine_V2(uint64_t handleVal) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSTRE*>(handleVal);
	if (!_v2h) return 0;
	TREHandleGuard guard(AcquireTREHandle(_v2h));
	if (!guard) return 0;
	try {
		bool bResult = guard.get()->CheckEngine();
		if (bResult) return 1;
		else return 0;
	}
	catch (...) {
		return 0;
	}
}