// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include "ANSMOT.h"
#include "ANSByteTrackNCNN.h"
#include "ANSByteTrack.h"
#include "ANSByteTrackEigen.h"
#include "ANSOCSortTrack.h"
#include "ANSUCMC.h"
#include <unordered_map>
#include <condition_variable>
#include <mutex>
#include <cstdint>

// DebugView: filter on "[ANSMOT]" — gated by ANSCORE_DEBUGVIEW in ANSLicense.h.

// Handle registry with refcount — prevents use-after-free when
// ReleaseANSMOTHandle is called while an operation is still running.
// destructionStarted: set by the first Unregister caller; blocks new Acquires
// and makes subsequent Unregister calls return false without deleting.
// Prevents double-free when Release is raced on the same handle.
struct MOTEntry { int refcount; bool destructionStarted; };
static std::unordered_map<ANSCENTER::ANSMOT*, MOTEntry>& MOTHandleRegistry() {
	static std::unordered_map<ANSCENTER::ANSMOT*, MOTEntry> s;
	return s;
}
static std::mutex& MOTHandleRegistryMutex() {
	static std::mutex m;
	return m;
}
static std::condition_variable& MOTHandleRegistryCV() {
	static std::condition_variable cv;
	return cv;
}

static void RegisterMOTHandle(ANSCENTER::ANSMOT* h) {
	std::lock_guard<std::mutex> lk(MOTHandleRegistryMutex());
	MOTHandleRegistry()[h] = { 1, false };
	ANS_DBG("ANSMOT","Register: handle=%p (uint=%llu) registrySize=%zu",
	       (void*)h, (unsigned long long)(uintptr_t)h, MOTHandleRegistry().size());
}

static ANSCENTER::ANSMOT* AcquireMOTHandle(ANSCENTER::ANSMOT* h) {
	std::lock_guard<std::mutex> lk(MOTHandleRegistryMutex());
	auto it = MOTHandleRegistry().find(h);
	if (it == MOTHandleRegistry().end()) {
		ANS_DBG("ANSMOT","Acquire FAIL: handle=%p (uint=%llu) NOT in registry. registrySize=%zu",
		       (void*)h, (unsigned long long)(uintptr_t)h, MOTHandleRegistry().size());
		size_t i = 0;
		for (auto& kv : MOTHandleRegistry()) {
			ANS_DBG("ANSMOT","  registry[%zu] = %p (uint=%llu) refcount=%d destructionStarted=%d",
			       i++, (void*)kv.first, (unsigned long long)(uintptr_t)kv.first,
			       kv.second.refcount, kv.second.destructionStarted ? 1 : 0);
		}
		return nullptr;
	}
	if (it->second.destructionStarted) {
		ANS_DBG("ANSMOT","Acquire FAIL: handle=%p is being destroyed (destructionStarted=true)", (void*)h);
		return nullptr;
	}
	it->second.refcount++;
	ANS_DBG("ANSMOT","Acquire OK:   handle=%p refcount=%d", (void*)h, it->second.refcount);
	return h;
}

static bool ReleaseMOTHandleRef(ANSCENTER::ANSMOT* h) {
	std::lock_guard<std::mutex> lk(MOTHandleRegistryMutex());
	auto it = MOTHandleRegistry().find(h);
	if (it == MOTHandleRegistry().end()) return false;
	it->second.refcount--;
	if (it->second.refcount <= 0) {
		MOTHandleRegistryCV().notify_all();
	}
	return false;  // Only Unregister deletes.
}

static bool UnregisterMOTHandle(ANSCENTER::ANSMOT* h) {
	std::unique_lock<std::mutex> lk(MOTHandleRegistryMutex());
	auto it = MOTHandleRegistry().find(h);
	if (it == MOTHandleRegistry().end()) {
		ANS_DBG("ANSMOT","Unregister: handle=%p NOT in registry (already gone)", (void*)h);
		return false;
	}
	if (it->second.destructionStarted) {
		ANS_DBG("ANSMOT","Unregister: handle=%p already being destroyed by another thread, returning false", (void*)h);
		return false;  // Another thread already owns the delete.
	}
	ANS_DBG("ANSMOT","Unregister: handle=%p starting (refcount before=%d)", (void*)h, it->second.refcount);
	it->second.destructionStarted = true;
	it->second.refcount--;
	bool ok = MOTHandleRegistryCV().wait_for(lk, std::chrono::seconds(30), [&]() {
		auto it2 = MOTHandleRegistry().find(h);
		return it2 == MOTHandleRegistry().end() || it2->second.refcount <= 0;
	});
	if (!ok) {
		ANS_DBG("ANSMOT","WARNING: Unregister timed out waiting for in-flight operations on handle=%p", (void*)h);
		OutputDebugStringA("WARNING: UnregisterMOTHandle timed out waiting for in-flight operations\n");
	}
	MOTHandleRegistry().erase(h);
	return true;
}

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

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
/*
   enum MOTEType {
		BYTETRACKNCNN = 0,
		BYTETRACK = 1,
		BYTETRACKEIGEN=2,
		OCSORT=3
	};
*/
extern "C" __declspec(dllexport) int      __cdecl CreateANSMOTHandle(ANSCENTER::ANSMOT **Handle, int motType) {
	ANS_DBG("ANSMOT","CreateANSMOTHandle: HandlePtr=%p, *Handle(in)=%p, motType=%d",
	       (void*)Handle, (void*)(Handle ? *Handle : nullptr), motType);
	if (Handle == nullptr) { ANS_DBG("ANSMOT","Create FAIL: Handle is null"); return 0; }
	try {
		// Pure constructor: ignore *Handle(in). LabVIEW's CLF Node marshalling
		// reuses the same temp buffer per call site, so *Handle(in) often holds
		// leftover bytes from the previous Create's output even when the actual
		// LabVIEW wire is a different, freshly-allocated instance. Inspecting
		// *Handle(in) and destroying what we "see" tears down legitimate
		// parallel instances. (Same reasoning as CreateANSAWSHandle.)
		// Trade-off: a true double-Create on the same wire leaks the prior
		// handle -- caller's bug; the alternative is far worse.
		*Handle = nullptr;

		switch (motType) {
		case 0: //BYTETRACKNCNN =0
			(*Handle) = new ANSCENTER::ANSByteTrack();// ANSByteTrackNCNN();
			break;
		case 1: //BYTETRACK = 1
			(*Handle) = new ANSCENTER::ANSByteTrack();
			break;
		case 2://BYTETRACKEIGEN = 2
			(*Handle) = new ANSCENTER::ANSByteTrack();// ANSByteTrackEigen();
			break;
		case 3://UCMC = 3
			(*Handle) = new ANSCENTER::ANSUCMCTrack();
			break;
		case 4://OCSORT = 4
			(*Handle) = new ANSCENTER::ANSOCSortTrack();
			break;
		default:
			(*Handle) = new ANSCENTER::ANSByteTrack();// ANSByteTrackNCNN();
			break;
		}
		if (*Handle == nullptr) {
			ANS_DBG("ANSMOT","Create FAIL: new returned null");
			return 0;
		}
		else {
			ANS_DBG("ANSMOT","Create: allocated handle=%p (uint=%llu), calling Register...",
			       (void*)*Handle, (unsigned long long)(uintptr_t)*Handle);
			RegisterMOTHandle(*Handle);
			ANS_DBG("ANSMOT","Create OK: handle=%p (uint=%llu)", (void*)*Handle, (unsigned long long)(uintptr_t)*Handle);
			return 1;
		}
	}
	catch (std::exception& e) {
		ANS_DBG("ANSMOT","Create EXCEPTION (std::exception): %s", e.what());
		return 0;
	}
	catch (...) {
		ANS_DBG("ANSMOT","Create EXCEPTION (unknown)");
		return 0;
	}
}
static int ReleaseANSMOTHandle_Impl(ANSCENTER::ANSMOT** Handle) {
	try {
		if (!Handle || !*Handle) {
			ANS_DBG("ANSMOT","Release: HandlePtr or *Handle is null, no-op");
			return 1;
		}
		ANSCENTER::ANSMOT* h = *Handle;
		ANS_DBG("ANSMOT","Release called: handle=%p (uint=%llu)", (void*)h, (unsigned long long)(uintptr_t)h);
		if (!UnregisterMOTHandle(h)) {
			ANS_DBG("ANSMOT","Release: Unregister returned false (already gone or being destroyed by another thread), handle=%p", (void*)h);
			*Handle = nullptr;
			return 1;
		}
		h->Destroy();
		delete h;
		*Handle = nullptr;
		ANS_DBG("ANSMOT","Release OK: handle=%p deleted, registry now has %zu entries",
		       (void*)h, MOTHandleRegistry().size());
		return 1;
	}
	catch (...) {
		ANS_DBG("ANSMOT","Release EXCEPTION (unknown)");
		if (Handle) *Handle = nullptr;
		return 0;
	}
}

extern "C" __declspec(dllexport) int      __cdecl ReleaseANSMOTHandle(ANSCENTER::ANSMOT **Handle) {
	ANS_DBG("ANSMOT","ReleaseANSMOTHandle: HandlePtr=%p, *Handle=%p",
	       (void*)Handle, (void*)(Handle ? *Handle : nullptr));
	__try {
		return ReleaseANSMOTHandle_Impl(Handle);
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {
		ANS_DBG("ANSMOT","ReleaseANSMOTHandle: SEH exception caught");
		if (Handle) *Handle = nullptr;
		return 0;
	}
}

extern "C" __declspec(dllexport) int     __cdecl UpdateANSMOTParameters(ANSCENTER::ANSMOT * *Handle, const char* trackerParameter) {
	ANS_DBG("ANSMOT","UpdateANSMOTParameters: HandlePtr=%p, *Handle=%p, trackerParameter=%s",
	       (void*)Handle, (void*)(Handle ? *Handle : nullptr),
	       trackerParameter ? trackerParameter : "(null)");
	if (Handle == nullptr || *Handle == nullptr) return -1;
	MOTHandleGuard guard(AcquireMOTHandle(*Handle));
	if (!guard) return -1;
	try {
		bool result = guard.get()->UpdateParameters(trackerParameter);
		if (result) return 1;
		else return 0;
	}
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}

extern "C" __declspec(dllexport) int __cdecl UpdateANSMOT(ANSCENTER::ANSMOT * *Handle, int modelId, const char* detectionData, std::string & result) {
	ANS_DBG("ANSMOT","UpdateANSMOT: HandlePtr=%p, *Handle=%p, modelId=%d",
	       (void*)Handle, (void*)(Handle ? *Handle : nullptr), modelId);
	if (Handle == nullptr || *Handle == nullptr) return -1;
	MOTHandleGuard guard(AcquireMOTHandle(*Handle));
	if (!guard) return -1;
	try {
		result = guard.get()->Update(modelId, detectionData);
		if (result.empty())return 0;
		else return 1;
	}
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" __declspec(dllexport) int            __cdecl UpdateANSMOT_LV(ANSCENTER::ANSMOT * *Handle, int modelId, const char* detectionData, LStrHandle trackerResult) {
	ANS_DBG("ANSMOT","UpdateANSMOT_LV: HandlePtr=%p, *Handle=%p, modelId=%d",
	       (void*)Handle, (void*)(Handle ? *Handle : nullptr), modelId);
	if (Handle == nullptr || *Handle == nullptr) return -1;
	MOTHandleGuard guard(AcquireMOTHandle(*Handle));
	if (!guard) return -1;
	try {
		std::string st = guard.get()->Update(modelId, detectionData);
		int size = static_cast<int>(st.length());
		MgErr error;
		error = DSSetHandleSize(trackerResult, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr)
		{
			(*trackerResult)->cnt = size;
			memcpy((*trackerResult)->str, st.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}

extern "C" __declspec(dllexport) int            __cdecl UpdateANSMOTTracker(ANSCENTER::ANSMOT** Handle, int modelId, const std::vector<ANSCENTER::TrackerObject>& detectionObjects, std::vector<ANSCENTER::TrackerObject>& trackedObjects)
{
	ANS_DBG("ANSMOT","UpdateANSMOTTracker: HandlePtr=%p, *Handle=%p, modelId=%d, detectionObjects.size=%zu",
	       (void*)Handle, (void*)(Handle ? *Handle : nullptr), modelId, detectionObjects.size());
	if (Handle == nullptr || *Handle == nullptr) return -1;
	MOTHandleGuard guard(AcquireMOTHandle(*Handle));
	if (!guard) return -1;
	try {
		trackedObjects = guard.get()->UpdateTracker(modelId, detectionObjects);
		if (trackedObjects.empty()) return 0;
		else return 1;
	}
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}

// ============================================================================
// V2 LabVIEW API — Accept handle as uint64_t by value.
// Eliminates Handle** pointer-to-pointer instability when LabVIEW calls
// concurrently from multiple tasks.
// LabVIEW CLFN: set Handle parameter to Numeric / Unsigned Pointer-sized Integer / Pass: Value
// ============================================================================

extern "C" __declspec(dllexport) int __cdecl UpdateANSMOT_LV_V2(uint64_t handleVal, int modelId, const char* detectionData, LStrHandle trackerResult) {
	ANSCENTER::ANSMOT* directHandle = reinterpret_cast<ANSCENTER::ANSMOT*>(handleVal);
	ANS_DBG("ANSMOT","UpdateANSMOT_LV_V2: handleVal=%llu handle=%p, modelId=%d",
	       (unsigned long long)handleVal, (void*)directHandle, modelId);
	if (directHandle == nullptr) return -1;
	MOTHandleGuard guard(AcquireMOTHandle(directHandle));
	if (!guard) return -1;
	try {
		std::string st = guard.get()->Update(modelId, detectionData);
		int size = static_cast<int>(st.length());
		MgErr error = DSSetHandleSize(trackerResult, sizeof(int32) + size * sizeof(uChar));
		if (error == noErr) {
			(*trackerResult)->cnt = size;
			memcpy((*trackerResult)->str, st.c_str(), size);
			return 1;
		}
		else return 0;
	}
	catch (...) { return 0; }
}