#include "ANSONVIF.h"
#include <cstdint>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "bm\sys_inc.h"
#include "bm\linked_list.h"
#include "http\http.h"
#include "http\http_parse.h"
#include "onvif\onvif_probe.h"
#include "onvif\onvif_event.h"
#include "onvif\onvif_api.h"
#include <unordered_map>
#include <condition_variable>
#include <mutex>

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

static void RegisterOnvifHandle(ANSCENTER::ANSOnvifClient* h) {
	std::lock_guard<std::mutex> lk(OnvifHandleRegistryMutex());
	OnvifHandleRegistry()[h] = 1;
}

static ANSCENTER::ANSOnvifClient* AcquireOnvifHandle(ANSCENTER::ANSOnvifClient* h) {
	std::lock_guard<std::mutex> lk(OnvifHandleRegistryMutex());
	auto it = OnvifHandleRegistry().find(h);
	if (it == OnvifHandleRegistry().end()) return nullptr;
	it->second++;
	return h;
}

static bool ReleaseOnvifHandleRef(ANSCENTER::ANSOnvifClient* h) {
	std::lock_guard<std::mutex> lk(OnvifHandleRegistryMutex());
	auto it = OnvifHandleRegistry().find(h);
	if (it == OnvifHandleRegistry().end()) return false;
	it->second--;
	if (it->second <= 0) {
		OnvifHandleRegistry().erase(it);
		OnvifHandleRegistryCV().notify_all();
		return true;
	}
	return false;
}

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

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

static bool ansonvifLicenceValid = false;
// Global once_flag to protect license checking
static std::once_flag ansonvifLicenseOnceFlag;
namespace ANSCENTER {
    static void VerifyGlobalANSONVIFLicense(const std::string& licenseKey) {
        try {
            std::cout << "ANSOnvifClient::CheckLicense.", "Checking ONVIF license. Please wait....";
            ansonvifLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1007, "ANSCV");//
            if (!ansonvifLicenceValid)ansonvifLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1003, "ANSVIS");//Default productId=1005
            if (!ansonvifLicenceValid)  std::cerr << "ANSOnvifClient::CheckLicense.", "Invalid ONVIF license. Please activate correct license.";
            else std::cout << "ANSOnvifClient::CheckLicense.", "Valid ONVIF license.";
        }
        catch (std::exception& e) {
            std::cerr << "ANSOnvifClient::CheckLicense()" << e.what();
			ansonvifLicenceValid = false;
        }
    }
    //Private methods
    void ANSOnvifClient::GetDevInfo(ONVIF_DEVICE_EX* p_device) {
        char profileToken[ONVIF_TOKEN_LEN];
        ONVIF_PROFILE* p_profile = nullptr;
        GetSystemDateAndTime(&p_device->onvif_device);
        GetCapabilities(&p_device->onvif_device);
        GetServices(&p_device->onvif_device);
        GetDeviceInformation(&p_device->onvif_device);
        GetVideoSources(&p_device->onvif_device);
        GetImagingSettings(&p_device->onvif_device);
        GetVideoSourceConfigurations(&p_device->onvif_device);
        GetVideoEncoderConfigurations(&p_device->onvif_device);
        if (GetAudioSources(&p_device->onvif_device))
        {
            GetAudioSourceConfigurations(&p_device->onvif_device);
            GetAudioEncoderConfigurations(&p_device->onvif_device);
        }

        if (p_device->onvif_device.Capabilities.ptz.support)
        {
            GetNodes(&p_device->onvif_device);
            GetConfigurations(&p_device->onvif_device);
        }

        if (p_device->onvif_device.curProfile)
        {
            strcpy(profileToken, p_device->onvif_device.curProfile->token);
        }
        else
        {
            memset(profileToken, 0, sizeof(profileToken));
        }

        if (profileToken[0] != '\0')
        {
            p_profile = onvif_find_profile(p_device->onvif_device.profiles, profileToken);
        }

        if (nullptr ==p_profile)
        {
            p_profile = p_device->onvif_device.profiles;
        }

        p_device->onvif_device.curProfile = p_profile;

        if (p_device->onvif_device.curProfile == nullptr)
        {
            p_device->thread_handler = 0;
            return ;
        }

        if (p_profile)
        {
            int len;
            uint8* p_buff;

            if (GetSnapshot(&p_device->onvif_device, p_profile->token, &p_buff, &len))
            {
                if (p_device->snapshot)
                {
                    FreeBuff(p_device->snapshot);
                    p_device->snapshot = nullptr;
                    p_device->snapshot_len = 0;
                }

                p_device->snapshot = p_buff;
                p_device->snapshot_len = len;

            }
        }
        if (p_device->onvif_device.Capabilities.events.support == 1 &&
            p_device->onvif_device.events.subscribe == FALSE)
        {
            Subscribe(&p_device->onvif_device, pps_get_index(m_dev_ul, p_device));
        }
    }
    ONVIF_DEVICE_EX* ANSOnvifClient::FindDevice(ONVIF_DEVICE_EX* pdevice)
    {
        ONVIF_DEVICE_EX* p_dev = static_cast<ONVIF_DEVICE_EX*>(pps_lookup_start(m_dev_ul));
        while (p_dev)
        {
            if (strcmp(p_dev->onvif_device.binfo.XAddr.host, pdevice->onvif_device.binfo.XAddr.host) == 0 &&
                p_dev->onvif_device.binfo.XAddr.port == pdevice->onvif_device.binfo.XAddr.port)
            {
                break;
            }
            p_dev = static_cast<ONVIF_DEVICE_EX*>(pps_lookup_next(m_dev_ul, p_dev));
        }

        pps_lookup_end(m_dev_ul);

        return p_dev;
    }
    ONVIF_DEVICE_EX* ANSOnvifClient::FindDeviceByEndpointReference(char* p_EndpointReference)
    {
        ONVIF_DEVICE_EX* p_dev = static_cast<ONVIF_DEVICE_EX*>(pps_lookup_start(m_dev_ul));
        while (p_dev)
        {
            if (strcmp(p_dev->onvif_device.binfo.EndpointReference, p_EndpointReference) == 0)
            {
                break;
            }
            p_dev = static_cast<ONVIF_DEVICE_EX*>(pps_lookup_next(m_dev_ul, p_dev));
        }

        pps_lookup_end(m_dev_ul);

        return p_dev;
    }
    ONVIF_DEVICE_EX* ANSOnvifClient::AddDevice(ONVIF_DEVICE_EX* pdevice)
    {
        ONVIF_DEVICE_EX* p_dev = static_cast<ONVIF_DEVICE_EX*>(pps_fl_pop(m_dev_fl));
        if (p_dev)
        {
            memcpy(p_dev, pdevice, sizeof(ONVIF_DEVICE_EX));
            p_dev->p_user = 0;
            p_dev->onvif_device.events.init_term_time = 60;
            onvif_SetAuthInfo(&p_dev->onvif_device, "admin", "admin");
            onvif_SetAuthMethod(&p_dev->onvif_device, AuthMethod_UsernameToken);
            onvif_SetReqTimeout(&p_dev->onvif_device, 10000);
            pps_ctx_ul_add(m_dev_ul, p_dev);
        }

        return p_dev;
    }
    void ANSOnvifClient::UpdateDevice(ONVIF_DEVICE_EX* p_device)
    {
        if (nullptr ==p_device)
        {
            return;
        }

        if (p_device->thread_handler)
        {
            return;
        }   
        BOOL need_update = FALSE;

        if (p_device->need_update)
        {
            need_update = TRUE;
        }

        if (!p_device->onvif_device.authFailed)
        {
            if (nullptr ==p_device->onvif_device.curProfile)
            {
                need_update = TRUE;
            }
            else if (p_device->onvif_device.curProfile->stream_uri[0] == '\0')
            {
                need_update = TRUE;
            }
        }

        if (need_update)
        {
            p_device->need_update = 0;
            GetDevInfo(p_device);
        }
        
    }
    void ANSOnvifClient::FreeDevice(ONVIF_DEVICE_EX* p_dev)
    {
        if (nullptr ==p_dev)
        {
            return;
        }

        while (p_dev->thread_handler)
        {
            usleep(1000);
        }

        onvif_free_device(&p_dev->onvif_device);

        if (p_dev->snapshot)
        {
            FreeBuff(p_dev->snapshot);
            p_dev->snapshot = nullptr;
            p_dev->snapshot_len = 0;
        }
    }
    void ANSOnvifClient::ClearDevices()
    {
        if (m_dev_ul != nullptr) {
            ONVIF_DEVICE_EX* next_dev;
            ONVIF_DEVICE_EX* dev = static_cast<ONVIF_DEVICE_EX*>(pps_lookup_start(m_dev_ul));
            int max_iterations = 100; // Set a reasonable limit for the maximum number of iterations
            int iteration_count = 0;

            while (dev && iteration_count < max_iterations)
            {
                next_dev = static_cast<ONVIF_DEVICE_EX*>(pps_lookup_next(m_dev_ul, dev));
                try {
                    FreeDevice(dev);
                }
                catch (const std::exception& e) {
                    // Log the exception or handle it as needed
                    printf("Exception occurred while freeing device: %s\n", e.what());
                }
                catch (...) {
                    // Catch any other types of exceptions
                    printf("Unknown exception occurred while freeing device.\n");
                }
                dev = next_dev;
                iteration_count++;
            }

            if (iteration_count >= max_iterations) {
                printf("Warning: ClearDevices loop exceeded maximum iterations.\n");
            }

            pps_lookup_end(m_dev_ul);
        }
    }
    void ANSOnvifClient::ProbeCallback(DEVICE_BINFO* p_res, int msgtype, void* p_data)
    {
        ONVIF_DEVICE_EX device;
        memset(&device, 0, sizeof(ONVIF_DEVICE_EX));
        if (msgtype == PROBE_MSGTYPE_MATCH || msgtype == PROBE_MSGTYPE_HELLO)
        {
            memcpy(&device.onvif_device.binfo, p_res, sizeof(DEVICE_BINFO));
            device.state = 1;
            std::string deviceURL = p_res->XAddr.url;
            if (deviceURL.find("/onvif/device_service") != std::string::npos) {
                OnvifDevice foundOnvifDevice;
                foundOnvifDevice.deviceIP = p_res->XAddr.host;
                foundOnvifDevice.devicePort = p_res->XAddr.port;

                // Check if the device is already in the list
                auto it = std::find_if(_deviceList.begin(), _deviceList.end(), [&foundOnvifDevice](const OnvifDevice& d) {
                    return d.deviceIP == foundOnvifDevice.deviceIP && d.devicePort == foundOnvifDevice.devicePort;
                    });

                // If the device is not found, add it to the list
                if (it == _deviceList.end()) {
                    printf("Found device. Ip : %s, port : %d\n", p_res->XAddr.host, p_res->XAddr.port);
                    _deviceList.push_back(foundOnvifDevice);
                }
            }      
        }
        else if (msgtype == PROBE_MSGTYPE_BYE)
        {
           printf("Exit onvif probe");

        }
    }

    void ANSOnvifClient::ErrorHandler(ONVIF_DEVICE* p_device)
    {
        if (p_device->authFailed) // Authentication failed 
        {
            std::cerr<<"ANSOnvifClient::ErrorHandler", "Authentication failed";
        }
        switch (p_device->errCode)
        {
        case ONVIF_ERR_ConnFailure: // Connection failed
            std::cerr << "ANSOnvifClient::ErrorHandler", "Connect failed";
            break;

        case ONVIF_ERR_MallocFailure: // Failed to allocate memory
            std::cerr << "ANSOnvifClient::ErrorHandler", "Memory malloc failed";
            break;

        case ONVIF_ERR_NotSupportHttps: // The device requires an HTTPS connection, but the onvif client library does not support it (the HTTPS compilation macro is not enabled)
            std::cerr << "ANSOnvifClient::ErrorHandler", "Not support https";
            break;

        case ONVIF_ERR_RecvTimeout: // Message receiving timeout
            std::cerr << "ANSOnvifClient::ErrorHandler", "Message receive timeout";
            break;

        case ONVIF_ERR_InvalidContentType: // Device response message content is invalid
            std::cerr << "ANSOnvifClient::ErrorHandler", "Invalid content type";
            break;

        case ONVIF_ERR_NullContent: // Device response message has no content
            std::cerr << "ANSOnvifClient::ErrorHandler", "Null content";
            break;

        case ONVIF_ERR_ParseFailed: // Parsing the message failed
            std::cerr << "ANSOnvifClient::ErrorHandler", "Message parse failed";
            break;

        case ONVIF_ERR_HandleFailed: // Message handling failed
            std::cerr << "ANSOnvifClient::ErrorHandler", "Message handle failed";
            break;

        case ONVIF_ERR_HttpResponseError: // The device responded with an error message
            std::cerr << "ANSOnvifClient::ErrorHandler. Code="<< p_device->fault.Code;
            std::cerr << "ANSOnvifClient::ErrorHandler. Subcode="<< p_device->fault.Subcode;
            std::cerr << "ANSOnvifClient::ErrorHandler. Season="<< p_device->fault.Subcode;
            break;

        default:
            break;
        }
    }
    std::string ANSOnvifClient::DeviceListToJsonString(const std::vector<OnvifDevice>& deviceList) {
        boost::property_tree::ptree root;
        boost::property_tree::ptree detectedObjects;
        for (int i = 0; i < deviceList.size(); i++) {
            boost::property_tree::ptree detectedNode;
            detectedNode.put("ip", deviceList[i].deviceIP);
            detectedNode.put("port", deviceList[i].devicePort);
            detectedObjects.push_back(std::make_pair("", detectedNode));
        }
        root.add_child("results", detectedObjects);
        std::ostringstream stream;
        boost::property_tree::write_json(stream, root, false);
        std::string trackingResult = stream.str();
        return trackingResult;
    }
    std::string ANSOnvifClient::DeviceProfileToJsonString(const std::vector<OnvifDeviceProfile>& deviceProfileList) {
        boost::property_tree::ptree root;
        boost::property_tree::ptree detectedObjects;
        for (int i = 0; i < deviceProfileList.size(); i++) {
            boost::property_tree::ptree detectedNode;
            detectedNode.put("ip", deviceProfileList[i].deviceIP);
            detectedNode.put("name", deviceProfileList[i].deviceProfileName);
            detectedNode.put("uri", deviceProfileList[i].deviceProfileURI);
            detectedObjects.push_back(std::make_pair("", detectedNode));
        }
        root.add_child("results", detectedObjects);
        std::ostringstream stream;
        boost::property_tree::write_json(stream, root, false);
        std::string trackingResult = stream.str();
        return trackingResult;
    }
    void ANSOnvifClient::CheckLicense() {
        //try {
        //    std::cout << "ANSOnvifClient::CheckLicense.", "Checking ONVIF license. Please wait....";
        //    _licenseValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(_licenseKey, 1007, "ANSCV");//
        //    if(!_licenseValid)_licenseValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(_licenseKey, 1003, "ANSVIS");//Default productId=1005
        //    if(!_licenseValid)  std::cerr << "ANSOnvifClient::CheckLicense.", "Invalid ONVIF license. Please activate correct license.";
        //    else std::cout << "ANSOnvifClient::CheckLicense.", "Valid ONVIF license.";
        //}
        //catch (std::exception& e) {
        //    std::cerr << "ANSOnvifClient::CheckLicense()"<< e.what();
        //}
        try {
            // Check once globally
            std::call_once(ansonvifLicenseOnceFlag, [this]() {
                VerifyGlobalANSONVIFLicense(_licenseKey);
                });

            // Update this instance's local license flag
            _licenseValid = ansonvifLicenceValid;
        }
        catch (const std::exception& e) {
             std::cerr << "ANSOnvifClient::CheckLicense()"<< e.what();
        }
    }
    bool ANSOnvifClient::Init(const std::string& licenseKey) {
        CheckLicense();
        if (!_licenseValid) {
            std::cerr << "ANSOnvifClient::Init", "Invalid license";
            return false;
        }
        try {
			_deviceList.clear();
            m_dev_fl = pps_ctx_fl_init(200, sizeof(ONVIF_DEVICE_EX), TRUE);
            m_dev_ul = pps_ctx_ul_init(m_dev_fl, TRUE);
            network_init();
            sys_buf_init(200);
            http_msg_buf_init(200);
            
            onvif_event_init(nullptr, 30100, 10);
            set_probe_cb(ProbeCallback, 0);
            //StartDetectTask();
            return true;
        }
        catch (std::exception& e)
        {
            std::cerr << "ANSOnvifClient::Init. Error"<< e.what();
            return false;
        }
        catch (...) {
            std::cerr << "ANSOnvifClient::Init", "Unknown error";
            return false;
        }
    }
    void ANSOnvifClient::Destroy() {

        ClearDevices();
        onvif_event_deinit();
        http_msg_buf_deinit();
        sys_buf_deinit();
        network_deinit();
        if (m_dev_ul != nullptr) pps_ul_free(m_dev_ul);
        if (m_dev_fl != nullptr)pps_fl_free(m_dev_fl);
        _deviceList.clear();
        std::cout << "ANSOnvifClient::Destroy.", "Clean-up ONVIF resource";

    }
    ANSOnvifClient::~ANSOnvifClient() {
        Destroy();
    }
    std::vector<OnvifDevice>ANSOnvifClient::SearchForDevices(int sleepDuration) {
		if (sleepDuration <= 0)sleepDuration = 1;
        if (_licenseValid) {
            std::lock_guard<std::mutex> lock(_mutex); 
            start_probe(nullptr, 30);
            sleep(sleepDuration);//sleep for 15 seconds
            stop_probe();
            return _deviceList;
        }
        else {
            std::cerr << "ANSOnvifClient::SearchForDevices.", "Invalid ONVIF license. Please activate correct license.";
            return _deviceList;
        }   
    }
    std::vector<OnvifDeviceProfile> ANSOnvifClient::SearchForDeviceProfile(const std::string& ip, int port, const std::string& username, const std::string& password) {
        std::lock_guard<std::mutex> lock(_mutex);
        std::vector<OnvifDeviceProfile> _deviceProfileList;
        _deviceProfileList.clear();
        if (_licenseValid) {
            ONVIF_DEVICE g_device;
            memset(&g_device, 0, sizeof(g_device));
            g_device.binfo.XAddr.https = 0;
            g_device.binfo.XAddr.port = port;
            strcpy(g_device.binfo.XAddr.host, ip.c_str());
            strcpy(g_device.binfo.XAddr.url, "/onvif/device_service");
            onvif_SetAuthInfo(&g_device, username.c_str(), password.c_str());
            onvif_SetAuthMethod(&g_device, AuthMethod_UsernameToken);
            onvif_SetReqTimeout(&g_device, 5000);
            if (!GetSystemDateAndTime(&g_device))
            {
                ErrorHandler(&g_device);
                std::cerr << "ANSOnvifClient::SearchForDeviceProfile. GetSystemDateAndTime failed."<< g_device.binfo.XAddr.host;
            }

            if (!GetCapabilities(&g_device))
            {
                ErrorHandler(&g_device);
                std::cerr << "ANSOnvifClient::SearchForDeviceProfile. GetCapabilities failed."<< g_device.binfo.XAddr.host;
            }

            if (!GetServices(&g_device))
            {
                ErrorHandler(&g_device);
                std::cerr << "ANSOnvifClient::SearchForDeviceProfile. GetServices failed."<< g_device.binfo.XAddr.host;
            }

            if (!GetDeviceInformation(&g_device))
            {
                ErrorHandler(&g_device);
                std::cerr << "ANSOnvifClient::SearchForDeviceProfile. GetDeviceInformation failed." << g_device.binfo.XAddr.host;
            }

            if (!GetProfiles(&g_device))
            {
                ErrorHandler(&g_device);
                std::cerr << "ANSOnvifClient::SearchForDeviceProfile. GetDeviceInformation failed."<< g_device.binfo.XAddr.host;
            }

            if (!GetStreamUris(&g_device, onvif_TransportProtocol::TransportProtocol_UDP))
            {
                ErrorHandler(&g_device);
                std::cerr << "ANSOnvifClient::SearchForDeviceProfile. GetDeviceInformation failed."<< g_device.binfo.XAddr.host;
            }

            ONVIF_PROFILE* p_profile = g_device.profiles;
            bool foundProfile = false;
            while (p_profile)
            {
                OnvifDeviceProfile foundOnvifProfile;
                foundOnvifProfile.deviceIP = ip;
                foundOnvifProfile.deviceProfileName = p_profile->name;
                foundOnvifProfile.deviceProfileURI = p_profile->stream_uri;
                _deviceProfileList.push_back(foundOnvifProfile);
                foundProfile = true;
                p_profile = p_profile->next;
            }
            if (!foundProfile) {
                OnvifDeviceProfile foundOnvifProfile;
                foundOnvifProfile.deviceIP = g_device.binfo.XAddr.host;
                foundOnvifProfile.deviceProfileName = "Error";
                foundOnvifProfile.deviceProfileURI = g_device.fault.Reason;
                _deviceProfileList.push_back(foundOnvifProfile);
            }
            onvif_free_device(&g_device);
            return _deviceProfileList;
        }
        else {
            std::cerr << "ANSOnvifClient::SearchForDeviceProfile.", "Invalid ONVIF license. Please activate correct license.";
            return _deviceProfileList;
        }
        
    }
    std::vector<OnvifDeviceProfile> ANSOnvifClient::SearchForDeviceProfiles(const std::string& username, const std::string& password) {
        std::vector<OnvifDeviceProfile> _deviceProfileList;
        _deviceProfileList.clear();
        if (_licenseValid) {
            SearchForDevices(15);
            if (_deviceList.size() <= 0) return _deviceProfileList;
            for (int i = 0; i < _deviceList.size(); i++)
            {
                OnvifDevice currentOnvifDevice = _deviceList.at(i);
                std::vector<OnvifDeviceProfile> _currentDeviceProfileList = SearchForDeviceProfile(currentOnvifDevice.deviceIP, currentOnvifDevice.devicePort, username, password);
                if (_currentDeviceProfileList.size() > 0) {
                    for (int j = 0; j < _currentDeviceProfileList.size(); j++) {
                        _deviceProfileList.push_back(_currentDeviceProfileList.at(j));
                    }
                }
            }
            return _deviceProfileList;
        }
        else {
            std::cerr << "ANSOnvifClient::SearchForDeviceProfiles.", "Invalid ONVIF license. Please activate correct license.";
            return _deviceProfileList;
        }

    }
    std::vector<OnvifDeviceProfile> ANSOnvifClient::SearchForDeviceProfiles(const std::vector<std::string>& usernameList, const std::vector<std::string>& passwordList) {
        std::vector<OnvifDeviceProfile> _deviceProfileList;
        _deviceProfileList.clear();
        if (_licenseValid) {
            SearchForDevices(15);
            if (_deviceList.size() <= 0) return _deviceProfileList;
            if (usernameList.size() <= 0) return _deviceProfileList;
            if (usernameList.size() != passwordList.size())return _deviceProfileList;
            for (int i = 0; i < _deviceList.size(); i++)
            {
                OnvifDevice currentOnvifDevice = _deviceList.at(i);
                for (int j = 0; j < usernameList.size(); j++)
                {
                    std::vector<OnvifDeviceProfile> _currentDeviceProfileList = SearchForDeviceProfile(currentOnvifDevice.deviceIP, currentOnvifDevice.devicePort, usernameList.at(j).c_str(), passwordList.at(j).c_str());

                    if (_currentDeviceProfileList.size() > 0) {
                        for (int k = 0; k < _currentDeviceProfileList.size(); k++) {
                            _deviceProfileList.push_back(_currentDeviceProfileList.at(k));
                        }
                    }
                }
            }
            return _deviceProfileList;
        }
        else {
            std::cerr << "ANSOnvifClient::SearchForDeviceProfiles.", "Invalid ONVIF license. Please activate correct license.";
            return _deviceProfileList;
        }
        
    }
    ANSOnvifClient::ANSOnvifClient() {
		_licenseValid = false;
    }
	void ANSOnvifClient::ClearDeviceList() {
		_deviceList.clear();
	}
}

extern "C" __declspec(dllexport) int CreateANSOnvifHandle(ANSCENTER::ANSOnvifClient * *Handle, const char* licenseKey) {
    if (Handle == nullptr || licenseKey == nullptr) return 0;
    try {
        // Release existing handle if called twice (prevents leak from LabVIEW)
        if (*Handle) {
            if (UnregisterOnvifHandle(*Handle)) {
                delete *Handle;
            }
            *Handle = nullptr;
        }

        *Handle = new ANSCENTER::ANSOnvifClient();
        if (*Handle == nullptr) return 0;
        if ((*Handle)->Init(licenseKey)) {
            RegisterOnvifHandle(*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 ReleaseANSOnvifHandle_Impl(ANSCENTER::ANSOnvifClient** Handle) {
	try {
		if (!Handle || !*Handle) return 1;
		if (!UnregisterOnvifHandle(*Handle)) {
			*Handle = nullptr;
			return 1;
		}
		delete *Handle;
		*Handle = nullptr;
		return 1;
	}
	catch (...) {
		if (Handle) *Handle = nullptr;
		return 0;
	}
}

extern "C" __declspec(dllexport) int ReleaseANSOnvifHandle(ANSCENTER::ANSOnvifClient * *Handle) {
	__try {
		return ReleaseANSOnvifHandle_Impl(Handle);
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {
		if (Handle) *Handle = nullptr;
		return 0;
	}
}
extern "C" __declspec(dllexport) int SearchANSOnvifDevices(ANSCENTER::ANSOnvifClient * *Handle, int sleepDuration, std::vector<std::string> &deviceList) {
    if (Handle == nullptr || *Handle == nullptr) return 0;
    OnvifHandleGuard guard(AcquireOnvifHandle(*Handle));
    if (!guard) return 0;
    try {
        deviceList.clear();
        std::vector<ANSCENTER::OnvifDevice> onvifDeviceList = guard.get()->SearchForDevices(sleepDuration);
        if (onvifDeviceList.size() <= 0)return 0;
        for (int i = 0; i < onvifDeviceList.size(); i++) {
            ANSCENTER::OnvifDevice currentDevice = onvifDeviceList.at(i);
            std::string currentDeviceStr = currentDevice.deviceIP + ":" + std::to_string(currentDevice.devicePort);
            deviceList.push_back(currentDeviceStr);
        }
        return 1;
    }
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" __declspec(dllexport) int SearchANSOnvifProfiles(ANSCENTER::ANSOnvifClient * *Handle, const char* username, const char* password, std::vector<std::string> &profileList) {
    if (Handle == nullptr || *Handle == nullptr || username == nullptr || password == nullptr) return 0;
    OnvifHandleGuard guard(AcquireOnvifHandle(*Handle));
    if (!guard) return 0;
    try {
        profileList.clear();
        std::vector<ANSCENTER::OnvifDeviceProfile> onvifDeviceProfileList = guard.get()->SearchForDeviceProfiles(username, password);
        if (onvifDeviceProfileList.size() <= 0)return 0;
        for (int i = 0; i < onvifDeviceProfileList.size(); i++) {
            ANSCENTER::OnvifDeviceProfile currentDevice = onvifDeviceProfileList.at(i);
            std::string currentDeviceStr = currentDevice.deviceProfileURI;
            profileList.push_back(currentDeviceStr);
        }
        return 1;
    }
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}

}
extern "C" __declspec(dllexport) int SearchANSOnvifSpecificProfiles(ANSCENTER::ANSOnvifClient * *Handle, const char* ip, int port, const char* username, const char* password, std::vector<std::string> &profileList) {
    if (Handle == nullptr || *Handle == nullptr || ip == nullptr || username == nullptr || password == nullptr) return 0;
    OnvifHandleGuard guard(AcquireOnvifHandle(*Handle));
    if (!guard) return 0;
    try {
        profileList.clear();
        std::vector<ANSCENTER::OnvifDeviceProfile> onvifDeviceProfileList = guard.get()->SearchForDeviceProfile(ip, port, username, password);
        if (onvifDeviceProfileList.size() <= 0)return 0;
        for (int i = 0; i < onvifDeviceProfileList.size(); i++) {
            ANSCENTER::OnvifDeviceProfile currentDevice = onvifDeviceProfileList.at(i);
            std::string currentDeviceStr = currentDevice.deviceProfileURI;
            profileList.push_back(currentDeviceStr);
        }
        return 1;
    }
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" __declspec(dllexport) int SearchANSOnvifDevices_LV(ANSCENTER::ANSOnvifClient * *Handle, int sleepDuration,LStrHandle deviceList) {
    if (Handle == nullptr || *Handle == nullptr || deviceList == nullptr) return 0;
    OnvifHandleGuard guard(AcquireOnvifHandle(*Handle));
    if (!guard) return 0;
    try {
        std::vector<ANSCENTER::OnvifDevice> onvifDeviceList = guard.get()->SearchForDevices(sleepDuration);
        std::string st = guard.get()->DeviceListToJsonString(onvifDeviceList);
        int size = static_cast<int>(st.length());
        if (size > 0) {
            MgErr error;
            error = DSSetHandleSize(deviceList, sizeof(int32) + size * sizeof(uChar));
            if (error == noErr)
            {
                (*deviceList)->cnt = size;
                memcpy((*deviceList)->str, st.c_str(), size);
                return 1;
            }
            else return 0;
        }
        else return 0;
    }
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" __declspec(dllexport) int SearchANSOnvifProfiles_LV(ANSCENTER::ANSOnvifClient * *Handle, const char* username, const char* password, LStrHandle profileList) {
    if (Handle == nullptr || *Handle == nullptr || username == nullptr || password == nullptr || profileList == nullptr) return 0;
    OnvifHandleGuard guard(AcquireOnvifHandle(*Handle));
    if (!guard) return 0;
    try {
        std::vector<ANSCENTER::OnvifDeviceProfile> onvifDeviceProfileList = guard.get()->SearchForDeviceProfiles(username, password);
        std::string st = guard.get()->DeviceProfileToJsonString(onvifDeviceProfileList);
        int size = static_cast<int>(st.length());
        if (size > 0) {
            MgErr error;
            error = DSSetHandleSize(profileList, sizeof(int32) + size * sizeof(uChar));
            if (error == noErr)
            {
                (*profileList)->cnt = size;
                memcpy((*profileList)->str, st.c_str(), size);
                return 1;
            }
            else return 0;
        }
        else return 0;
    }
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}
extern "C" __declspec(dllexport) int SearchANSOnvifSpecificProfiles_LV(ANSCENTER::ANSOnvifClient * *Handle, const char* ip, int port, const char* username, const char* password, LStrHandle profileList) {
    if (Handle == nullptr || *Handle == nullptr || ip == nullptr || username == nullptr || password == nullptr || profileList == nullptr) return 0;
    OnvifHandleGuard guard(AcquireOnvifHandle(*Handle));
    if (!guard) return 0;
    try {
        std::vector<ANSCENTER::OnvifDeviceProfile> onvifDeviceProfileList = guard.get()->SearchForDeviceProfile(ip, port, username, password);
        std::string st = guard.get()->DeviceProfileToJsonString(onvifDeviceProfileList);
        int size = static_cast<int>(st.length());
        if (size > 0) {
            MgErr error;
            error = DSSetHandleSize(profileList, sizeof(int32) + size * sizeof(uChar));
            if (error == noErr)
            {
                (*profileList)->cnt = size;
                memcpy((*profileList)->str, st.c_str(), size);
                return 1;
            }
            else return 0;
        }
        else return 0;
    }
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}

// ============================================================================
// V2 entry points — accept uint64_t handleVal by value instead of Handle**
// to eliminate LabVIEW buffer-reuse bug when concurrent calls share the same
// Handle** buffer address.
// ============================================================================

extern "C" __declspec(dllexport) int SearchANSOnvifDevices_LV_V2(uint64_t handleVal, int sleepDuration, LStrHandle deviceList) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSOnvifClient*>(handleVal);
	if (!_v2h) return 0;
	if (deviceList == nullptr) return 0;
	OnvifHandleGuard guard(AcquireOnvifHandle(_v2h));
	if (!guard) return 0;
	try {
		std::vector<ANSCENTER::OnvifDevice> onvifDeviceList = guard.get()->SearchForDevices(sleepDuration);
		std::string st = guard.get()->DeviceListToJsonString(onvifDeviceList);
		int size = static_cast<int>(st.length());
		if (size > 0) {
			MgErr error;
			error = DSSetHandleSize(deviceList, sizeof(int32) + size * sizeof(uChar));
			if (error == noErr)
			{
				(*deviceList)->cnt = size;
				memcpy((*deviceList)->str, st.c_str(), size);
				return 1;
			}
			else return 0;
		}
		else return 0;
	}
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}

extern "C" __declspec(dllexport) int SearchANSOnvifProfiles_LV_V2(uint64_t handleVal, const char* username, const char* password, LStrHandle profileList) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSOnvifClient*>(handleVal);
	if (!_v2h) return 0;
	if (username == nullptr || password == nullptr || profileList == nullptr) return 0;
	OnvifHandleGuard guard(AcquireOnvifHandle(_v2h));
	if (!guard) return 0;
	try {
		std::vector<ANSCENTER::OnvifDeviceProfile> onvifDeviceProfileList = guard.get()->SearchForDeviceProfiles(username, password);
		std::string st = guard.get()->DeviceProfileToJsonString(onvifDeviceProfileList);
		int size = static_cast<int>(st.length());
		if (size > 0) {
			MgErr error;
			error = DSSetHandleSize(profileList, sizeof(int32) + size * sizeof(uChar));
			if (error == noErr)
			{
				(*profileList)->cnt = size;
				memcpy((*profileList)->str, st.c_str(), size);
				return 1;
			}
			else return 0;
		}
		else return 0;
	}
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}

extern "C" __declspec(dllexport) int SearchANSOnvifSpecificProfiles_LV_V2(uint64_t handleVal, const char* ip, int port, const char* username, const char* password, LStrHandle profileList) {
	auto* _v2h = reinterpret_cast<ANSCENTER::ANSOnvifClient*>(handleVal);
	if (!_v2h) return 0;
	if (ip == nullptr || username == nullptr || password == nullptr || profileList == nullptr) return 0;
	OnvifHandleGuard guard(AcquireOnvifHandle(_v2h));
	if (!guard) return 0;
	try {
		std::vector<ANSCENTER::OnvifDeviceProfile> onvifDeviceProfileList = guard.get()->SearchForDeviceProfile(ip, port, username, password);
		std::string st = guard.get()->DeviceProfileToJsonString(onvifDeviceProfileList);
		int size = static_cast<int>(st.length());
		if (size > 0) {
			MgErr error;
			error = DSSetHandleSize(profileList, sizeof(int32) + size * sizeof(uChar));
			if (error == noErr)
			{
				(*profileList)->cnt = size;
				memcpy((*profileList)->str, st.c_str(), size);
				return 1;
			}
			else return 0;
		}
		else return 0;
	}
	catch (std::exception& e) {
		return 0;
	}
	catch (...) {
		return 0;
	}
}