#include "iobox_api.h"
#include "cJSON.h"
#include <iomanip>
#include <fstream>
#include "mbedtls/sha256.h"
#include "mbedtls/aes.h"
#include "mbedtls/base64.h"
#include <sstream>
#include <iostream>
#include <algorithm>
#include <string>
// Implementation:
#include <thread>
#include <chrono>
#include <condition_variable>

namespace ANSCENTER {
	iobox_api::iobox_api(const std::string& ip_mcast, int port_mcast)
	{
		macToIpMap.clear();
		ipToDeviceMap.clear();
		this->ip_mcast = ip_mcast;
		this->port_mcast = port_mcast;
		_username = "admin";
		_password = "1234";
		_port = 502;
		WSADATA wsaData;
		if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
			this->_logger.LogError("iobox_api::iobox_api. WSAStartup failed with error: ", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
			return;
		}
	}
	iobox_api::~iobox_api() noexcept
	{
		// Cancel all active toggle operations before destroying
		// This prevents detached threads from accessing a destroyed object
		{
			std::lock_guard<std::mutex> mapLock(toggleMapMutex);
			for (auto& pair : activeToggles) {
				auto toggleInfo = pair.second;
				if (toggleInfo) {
					std::lock_guard<std::mutex> infoLock(toggleInfo->mtx);
					toggleInfo->cancelled = true;
					toggleInfo->active = false;
					toggleInfo->cv.notify_one();
				}
			}
			activeToggles.clear();
		}

		// Close all open TCP sockets before clearing profiles
		{
			std::lock_guard<std::recursive_mutex> lock(this->_mutex);
			for (auto& profile : this->iobox_profiles) {
				if (profile.sock_tcp != INVALID_SOCKET) {
					closesocket(profile.sock_tcp);
					profile.sock_tcp = INVALID_SOCKET;
				}
			}
			this->iobox_profiles.clear();
		}

		macToIpMap.clear();
		ipToDeviceMap.clear();
		WSACleanup();
	}

	bool isStringExistInVectorString(const std::vector<std::string>& vec, const std::string& str) {
		try {
			for (const auto& item : vec) {
				if (item.find(str) != std::string::npos) {
					std::cout << "Found partial match: " << str << " in " << item << std::endl;
					return true;
				}
			}
			return false;
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return false;
		}
	}

	void printBufferAsHex(const char* buffer, size_t length) {
		try {
			for (size_t i = 0; i < length; ++i) {
				printf("%02X ", static_cast<unsigned char>(buffer[i]));
				if ((i + 1) % 16 == 0) {
					printf("\n");
				}
			}
			printf("\n");
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
		}
	}
	void printStringAsHex(const std::string& str) {
		try {
			for (size_t i = 0; i < str.size(); ++i) {
				printf("%02X ", static_cast<unsigned char>(str[i]));
				if ((i + 1) % 16 == 0) {
					printf("\n");
				}
			}
			printf("\n");
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
		}
	}

	std::string messageEncrypt(char* input, size_t input_len, const char* key) {
#if MESSAGE_CRYPT_ENABLE == 0
		return std::string(input, input_len);
#else 
		mbedtls_aes_context aes;
		char iv[17] = "1234567890123456";

		size_t padding_len = 16 - (input_len % 16);
		if (padding_len == 0) {
			padding_len = 16;
		}
		size_t padded_len = input_len + padding_len;
		std::vector<char> padded_input(padded_len);
		memcpy(padded_input.data(), input, input_len);
		for (size_t i = input_len; i < padded_len; ++i) {
			padded_input[i] = static_cast<char>(padding_len);
		}
		//init vector to hold data output
		std::vector<char> output(padded_len);
		std::string base64_output;

		mbedtls_aes_init(&aes);
		if (mbedtls_aes_setkey_enc(&aes, (const unsigned char*)key, 128) != 0) {
			std::cerr << "Failed to set key" << std::endl;
			mbedtls_aes_free(&aes);
			//return empty vector
			return base64_output;
		}

		if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, padded_len, (unsigned char*)iv, (const unsigned char*)padded_input.data(), (unsigned char*)output.data()) != 0) {
			std::cerr << "Failed to encrypt" << std::endl;
			mbedtls_aes_free(&aes);
			return base64_output;
		}
		mbedtls_aes_free(&aes);
		//encode to base64
		base64_output.resize(output.size() * 2);
		size_t base64_len = 0;
		if (mbedtls_base64_encode((unsigned char*)base64_output.data(), base64_output.size(), &base64_len, (const unsigned char*)output.data(), output.size()) != 0) {
			std::cerr << "Failed to encode to base64" << std::endl;
			return base64_output;
		}
		base64_output.resize(base64_len);
		// std::cout << "Base64 output: " << base64_output << std::endl;

		return base64_output;
#endif
	}
	std::string messageDecrypt(char* input, size_t input_len, const char* key) {
#if MESSAGE_CRYPT_ENABLE == 0
		return std::string(input, input_len);
#else
		mbedtls_aes_context aes;
		//base64 decode
		std::vector<char> base64_decoded(input_len);
		size_t base64_decoded_len = 0;
		if (mbedtls_base64_decode((unsigned char*)base64_decoded.data(), base64_decoded.size(), &base64_decoded_len, (const unsigned char*)input, input_len) != 0) {
			std::cerr << "Failed to decode base64" << std::endl;
			return std::string();
		}
		base64_decoded.resize(base64_decoded_len);
		// std::cout << "Base64 decoded: " << base64_decoded.size() << std::endl;


		char iv[17] = "1234567890123456";
		if (base64_decoded_len % 16 != 0) {
			std::cerr << "Invalid input length" << std::endl;
			return std::string();
		}
		std::vector<char> output(base64_decoded_len);
		mbedtls_aes_init(&aes);
		if (mbedtls_aes_setkey_dec(&aes, (const unsigned char*)key, 128) != 0) {
			std::cerr << "Failed to set key" << std::endl;
			mbedtls_aes_free(&aes);
			return std::string();
		}

		if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, base64_decoded_len, (unsigned char*)iv, (const unsigned char*)base64_decoded.data(), (unsigned char*)output.data()) != 0) {
			std::cerr << "Failed to decrypt" << std::endl;
			mbedtls_aes_free(&aes);
			return std::string();
		}
		mbedtls_aes_free(&aes);

		// Remove PKCS5 padding
		unsigned char padding_len = output.back();
		if (padding_len > 0 && padding_len <= 16) {
			// std::cout << "Padding length: " << (int)padding_len << std::endl;
			// printBufferAsHex(output.data(), output.size());
			output.resize(output.size() - padding_len);
		}
		else {
			std::cerr << "Invalid padding length" << std::endl;
			return std::string();;
		}
		//copy to string
		std::string output_str(output.data(), output.size());
		// std::cout << "Decrypted: " << output_str << std::endl;
		return output_str;
#endif
	}
	std::string stringToHexString(const std::string& str) {
		// std::stringstream ss;
		// for (size_t i = 0; i < str.length(); ++i) {
		//     ss << std::hex << std::setw(2) << std::setfill('0') << (int)str[i];
		// }
		// return ss.str();
		const char* hex_chars = "0123456789ABCDEF";
		std::string hex_str;
		hex_str.reserve(str.length() * 2);
		for (unsigned char c : str) {
			hex_str.push_back(hex_chars[c >> 4]);
			hex_str.push_back(hex_chars[c & 0x0F]);
		}
		return hex_str;
	}
	bool isValidIp(const std::string& ip) {
		struct sockaddr_in sa;
		return inet_pton(AF_INET, ip.c_str(), &(sa.sin_addr)) != 0;
	}

	std::string hostNameToIp(std::string hostName) {
		struct hostent* host;
		struct in_addr addr;
		if ((host = gethostbyname(hostName.c_str())) == NULL) {
			std::cerr << "Failed to get host by name: " << hostName << std::endl;
			return "";
		}
		addr.s_addr = *(u_long*)host->h_addr_list[0];
		return inet_ntoa(addr);
	}
	std::string checkIpFromRemote(const std::string& remote) {
		std::string ip;
		if (!isValidIp(remote)) {
			std::cout << "Host name address: " << remote << std::endl;
			ip = hostNameToIp(remote);
			if (ip == "") {
				std::cerr << "Failed to get ip from host name: " << remote << std::endl;
				return "";
			}
			return ip;
		}
		else return remote;
	}


	iobox_info_t parseIoboxResponseObj(cJSON* responseObj) {
		try {
			iobox_info_t info;
			cJSON* model = cJSON_GetObjectItem(responseObj, "Model");
			if (model != nullptr) {
				info.model = model->valuestring;
			}
			cJSON* serial = cJSON_GetObjectItem(responseObj, "Serial");
			if (serial != nullptr) {
				info.serial_number = serial->valuestring;
			}
			cJSON* hwVer = cJSON_GetObjectItem(responseObj, "HwVer");
			if (hwVer != nullptr) {
				info.hardware_version = hwVer->valuestring;
			}
			cJSON* fwVer = cJSON_GetObjectItem(responseObj, "FwVer");
			if (fwVer != nullptr) {
				info.firmware_version = fwVer->valuestring;
			}
			cJSON* macAddr = cJSON_GetObjectItem(responseObj, "macAddr");
			if (macAddr != nullptr) {
				info.mac_address = macAddr->valuestring;
			}
			cJSON* localIp = cJSON_GetObjectItem(responseObj, "localIp");
			if (localIp != nullptr) {
				info.ip_address = localIp->valuestring;
			}
			return info;
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return iobox_info_t();
		}
	}
	iobox_info_t parseIoboxResponse(const std::string& response) {
		try {
			//exp: {"response": { "Model":"ANS IOBOX","Serial":"IO123","HwVer":"11", "FwVer":"22","macAddr":"a2c3d4e5f6","localIp":"192.168.110.121"}}
			iobox_info_t info;
			cJSON* root = cJSON_Parse(response.c_str());
			if (root == nullptr) {
				std::cerr << "Failed to parse JSON" << std::endl;
				return info;
			}
			cJSON* responseObj = cJSON_GetObjectItem(root, "response");
			if (responseObj == nullptr) {
				std::cerr << "Failed to get response object" << std::endl;
				cJSON_Delete(root);
				return info;
			}
			info = parseIoboxResponseObj(responseObj);
			cJSON_Delete(root);

			std::cout << "Parsed IOBox Info:" << std::endl;
			std::cout << "  Model: " << info.model << std::endl;
			std::cout << "  Serial Number: " << info.serial_number << std::endl;
			std::cout << "  Hardware Version: " << info.hardware_version << std::endl;
			std::cout << "  Firmware Version: " << info.firmware_version << std::endl;
			std::cout << "  MAC Address: " << info.mac_address << std::endl;
			std::cout << "  IP Address: " << info.ip_address << std::endl;
			std::cout << "  Public IP Address: " << info.ip_public_address << std::endl;
			return info;
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return iobox_info_t();
		}
	}
	void show_info_iobox(iobox_info_t iobox_info) {
		std::cout << "  IP Address: " << iobox_info.ip_address << std::endl;
	}
	void iobox_api::show_profile_iobox(const std::string& remote) {
		try {
			std::string ip = checkIpFromRemote(remote);
			if (ip == "") {
				this->_logger.LogError("iobox_api::show_profile_iobox. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
				return;
			}
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::show_profile_iobox. IP address not found: ", ip, __FILE__, __LINE__);
				return;
			}
			show_profile_iobox(*profile);
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::show_profile_iobox. ", e.what(), __FILE__, __LINE__);
		}
	}
	void iobox_api::show_profile_iobox(const iobox_profile_t& profile) {
		try {
			// std::cout << "Profile ioboxs: " << this->iobox_profiles.size() << std::endl;
		   // for (const iobox_profile_t& profile : this->iobox_profiles) {
		   // std::cout << "* Index: " << &profile - &this->iobox_profiles[0] << std::endl;
			show_info_iobox(profile.iobox_info);
			std::cout << "  Is connected: " << profile.is_connected << std::endl;
			std::cout << "  Is anthenticated: " << profile.is_anthenticated << std::endl;
			std::cout << "  TCP Socket: " << profile.sock_tcp << std::endl;
			std::cout << "  Counting get: " << profile.counting_get << std::endl;
			std::cout << "  Model: " << profile.iobox_info.model << std::endl;
			std::cout << "  Serial Number: " << profile.iobox_info.serial_number << std::endl;
			std::cout << "  Hardware Version: " << profile.iobox_info.hardware_version << std::endl;
			std::cout << "  Firmware Version: " << profile.iobox_info.firmware_version << std::endl;
			std::cout << "  MAC Address: " << profile.iobox_info.mac_address << std::endl;
			std::cout << "  IP Address: " << profile.iobox_info.ip_address << std::endl;
			std::cout << "  Public IP Address: " << profile.iobox_info.ip_public_address << std::endl;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::show_profile_iobox. ", e.what(), __FILE__, __LINE__);
		}
	}
	void iobox_api::show_profile_ioboxs() {
		try {
			if (this->iobox_profiles.size() == 0) {
				this->_logger.LogError("iobox_api::show_profile_iobox. ", "No iobox profiles", __FILE__, __LINE__);
				return;
			}
			for (const auto& profile : this->iobox_profiles) {
				std::cout << "* Index: " << &profile - &this->iobox_profiles[0] << std::endl;
				show_profile_iobox(profile);
			}
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::show_profile_ioboxs. ", e.what(), __FILE__, __LINE__);
		}
	}
	void iobox_api::save_info_iobox(const iobox_info_t& iobox_info) {
		try {
			iobox_profile_t profile;
			profile.counting_get = 0;
			profile.is_connected = false;
			profile.is_anthenticated = false;
			profile.sock_tcp = INVALID_SOCKET;
			profile.iobox_info = iobox_info;
			this->iobox_profiles.push_back(profile);
			std::cout << "Save iobox, num device current: " << this->iobox_profiles.size() << std::endl;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::save_info_iobox. ", e.what(), __FILE__, __LINE__);
		}
	}
	void iobox_api::remove_last_info_iobox() {
		try {
			if (this->iobox_profiles.size() == 0) {
				this->_logger.LogError("iobox_api::remove_last_info_iobox. ", "No iobox profiles", __FILE__, __LINE__);
				return;
			}
			this->iobox_profiles.pop_back();
			std::cout << "Remove last iobox, num device current: " << this->iobox_profiles.size() << std::endl;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::remove_last_info_iobox. ", e.what(), __FILE__, __LINE__);
		}
	}
	bool iobox_api::sendTcpMessage(const std::string& ip, const char* buffer, size_t length, bool needCheckAuthen) {
		try {
			for (auto& profile : this->iobox_profiles) {
				if ((profile.iobox_info.ip_address == ip || profile.iobox_info.ip_public_address == ip) && profile.is_connected && profile.sock_tcp != INVALID_SOCKET) {
					if (needCheckAuthen && !profile.is_anthenticated) {
						this->_logger.LogError("iobox_api::sendTcpMessage. ", "Please anthenticate before send message", __FILE__, __LINE__);
						return false;
					}
					std::string encrypted = messageEncrypt((char*)buffer, length, this->aes_key);
					if (encrypted.size() == 0) {
						this->_logger.LogError("iobox_api::sendTcpMessage. ", "Failed to encrypt message", __FILE__, __LINE__);
						return false;
					}
					char buffer_read[1024];
					setSocketTimeout(profile.sock_tcp, 10);
					int result_read;
					do {
						result_read = recv(profile.sock_tcp, buffer_read, sizeof(buffer_read), 0);
					} while (result_read > 0);

					if (result_read == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
						// std::cerr << "recv failed with error: " << WSAGetLastError() << std::endl;
					}
					else if (result_read == 0) {
						this->_logger.LogError("iobox_api::sendTcpMessage. Connection closed by peer: ", ip, __FILE__, __LINE__);
						return false;
					}
					else {
						std::cout << "Cleared TCP receiver buffer for IP: " << ip << std::endl;
					}
					int result = send(profile.sock_tcp, encrypted.data(), (int)encrypted.size(), 0);
					if (result == SOCKET_ERROR) {
						int wsaErr = WSAGetLastError();
						if (wsaErr == WSAECONNRESET || wsaErr == WSAENOTCONN) {
							this->_logger.LogError("iobox_api::sendTcpMessage. Connection reset or not connected: ", ip, __FILE__, __LINE__);
						}
						else {
							this->_logger.LogError("iobox_api::sendTcpMessage. Send failed with error: ", std::to_string(wsaErr), __FILE__, __LINE__);
						}
						return false;
					}
					std::cout << "Sent " << length << " bytes message to " << ip << std::endl;
					std::cout << "Message: " << buffer << std::endl;
					std::cout << "Sent encrypt " << encrypted.size() << " bytes message to " << ip << std::endl;
					std::cout << "Message encrypt: " << encrypted.data() << std::endl;
					// printBufferAsHex(buffer, length);
					return true;
				}
			}
			this->_logger.LogError("iobox_api::sendTcpMessage. IP address not found or not connected: ", ip, __FILE__, __LINE__);
			return false;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::sendTcpMessage. ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}

	bool iobox_api::receiveTcpMessage(const std::string& ip, char* buffer, size_t& length, int timeout) {
		try {
			if (length == 0) {
				this->_logger.LogError("iobox_api::receiveTcpMessage. ", "Buffer length is zero", __FILE__, __LINE__);
				return false;
			}
			size_t bufferCapacity = length; // Save original buffer capacity
			for (auto& profile : this->iobox_profiles) {
				if ((profile.iobox_info.ip_address == ip || profile.iobox_info.ip_public_address == ip) && profile.is_connected && profile.sock_tcp != INVALID_SOCKET) {
					setSocketTimeout(profile.sock_tcp, timeout);
					// Reserve 1 byte for null terminator to prevent buffer overflow
					int recvLen = recv(profile.sock_tcp, buffer, (int)(bufferCapacity - 1), 0);
					if (recvLen > 0) {
						length = recvLen;
						buffer[length] = '\0';
						std::cout << "Received message: " << length << " bytes" << std::endl;
						std::cout << "Message: " << buffer << std::endl;
						// printBufferAsHex(buffer, recvLen);
						std::string decrypted = messageDecrypt(buffer, length, this->aes_key);
						if (decrypted.empty()) {
							this->_logger.LogError("iobox_api::receiveTcpMessage. ", "Failed to decrypt message", __FILE__, __LINE__);
							return false;
						}
						// Ensure decrypted data fits in buffer before copying
						if (decrypted.size() >= bufferCapacity) {
							this->_logger.LogError("iobox_api::receiveTcpMessage. ", "Decrypted message too large for buffer", __FILE__, __LINE__);
							return false;
						}
						//copy decrypted data to buffer
						memcpy(buffer, decrypted.data(), decrypted.size());
						length = decrypted.size();
						buffer[length] = '\0';
						std::cout << "Decrypt length: " << length << " bytes" << std::endl;
						std::cout << "Decrypt message: " << decrypted << std::endl;
						return true;
					}
					else if (recvLen == 0) {
						this->_logger.LogError("iobox_api::receiveTcpMessage. Connection closed by peer: ", ip, __FILE__, __LINE__);
						return false;
					}
					else if (recvLen == SOCKET_ERROR && WSAGetLastError() == WSAETIMEDOUT) {
						this->_logger.LogError("iobox_api::receiveTcpMessage. Recv timeout: ", ip, __FILE__, __LINE__);
						return false;
					}
					else {
						this->_logger.LogError("iobox_api::receiveTcpMessage. Recv  failed with error: ", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
						return false;
					}
				}
			}
			this->_logger.LogError("iobox_api::receiveTcpMessage. IP address not found or not connected: ", ip, __FILE__, __LINE__);
			return false;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::receiveTcpMessage. ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	void iobox_api::sendUnicastMessage(SOCKET sock, const char* ip, const std::string& message) {
		struct sockaddr_in unicastAddr;
		memset(&unicastAddr, 0, sizeof(unicastAddr));
		unicastAddr.sin_family = AF_INET;
		unicastAddr.sin_addr.s_addr = inet_addr(ip);
		unicastAddr.sin_port = htons(this->port_mcast);

		if (sendto(sock, message.c_str(), message.length(), 0, (struct sockaddr*)&unicastAddr, sizeof(unicastAddr)) == SOCKET_ERROR) {
			this->_logger.LogError("iobox_api::sendUnicastMessage. Sendto failed with error: ", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
		}
		else {
			std::cout << "Sent message: " << message << std::endl;
		}
	}
	void iobox_api::sendMulticastMessage(const std::string& message) {
		try {
			SOCKET sock = INVALID_SOCKET;
			struct sockaddr_in multicastAddr;

			sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			if (sock == INVALID_SOCKET) {
				this->_logger.LogError("iobox_api::sendMulticastMessage. Socket failed with error: ", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
				// WSACleanup();
				return;
			}

			memset(&multicastAddr, 0, sizeof(multicastAddr));
			multicastAddr.sin_family = AF_INET;
			multicastAddr.sin_addr.s_addr = inet_addr(this->ip_mcast.c_str());
			multicastAddr.sin_port = htons(this->port_mcast);

			if (sendto(sock, message.c_str(), message.length(), 0, (struct sockaddr*)&multicastAddr, sizeof(multicastAddr)) == SOCKET_ERROR) {
				this->_logger.LogError("iobox_api::sendMulticastMessage. Sendto failed with error: ", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
			}
			else {
				std::cout << "Sent message: " << message << std::endl;
			}

			closesocket(sock);
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::sendMulticastMessage. ", e.what(), __FILE__, __LINE__);
		}
	}
	void iobox_api::setSocketTimeout(SOCKET sock, int timeout) {
		try
		{
			// On Windows, SO_RCVTIMEO expects a DWORD value in milliseconds, not struct timeval.
			// Callers already pass millisecond values (e.g., 3000 = 3s, 10000 = 10s).
			DWORD timeoutMs = static_cast<DWORD>(timeout);
			if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutMs, sizeof(timeoutMs)) < 0) {
				this->_logger.LogError("iobox_api::setSocketTimeout. Setsockopt failed with error: ", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
			}
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::setSocketTimeout. ", e.what(), __FILE__, __LINE__);
		}
	}
	std::string toUpperCase(const std::string& str) {
		std::string result = str;
		std::transform(result.begin(), result.end(), result.begin(),
			[](unsigned char c) { return std::toupper(c); });
		return result;
	}
	std::string createAliasName(const std::string& serial, const std::string& hwVer, const std::string& fwVer, uint16_t index) {
		return "SN" + toUpperCase(serial) + "H" + hwVer + "F" + fwVer + "I" + std::to_string(index);
	}
	std::string createMulticastAlias(const std::string& aliasName, const std::string& model, const std::string& serial, const std::string& ip)
	{
		//exp: Alias-Model-SN-IP
		return aliasName + "-" + model + "-" + toUpperCase(serial) + "-" + ip;
	}

	uint16_t iobox_api::getIndexIoboxFromIp(const std::string& ip) {
		try {
			for (const auto& profile : this->iobox_profiles) {
				if (profile.iobox_info.ip_address == ip || profile.iobox_info.ip_public_address == ip) {
					return &profile - &this->iobox_profiles[0] + 1;
				}
			}
			return -1;
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return -1;
		}
	}
	void logNetworkAdapters(network_adapter_t adapter) {
		std::cout << "Adapter Index: " << adapter.adapter_index << std::endl;
		std::cout << "Adapter Name: " << adapter.adapter_name << std::endl;
		std::cout << "Adapter Description: " << adapter.adapter_description << std::endl;
		std::cout << "Adapter MAC: " << adapter.adapter_mac << std::endl;
		std::cout << "Adapter IP: " << adapter.adapter_ip << std::endl;
		std::cout << "Adapter Subnet: " << adapter.adapter_subnet << std::endl;
		std::cout << "Adapter Gateway: " << adapter.adapter_gateway << std::endl;
	}
	std::vector<network_adapter_t> iobox_api::getNetworkAdapters() {
		std::vector<network_adapter_t> adapters;
		PIP_ADAPTER_INFO pAdapterInfo;
		PIP_ADAPTER_INFO pAdapter = NULL;
		ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
		DWORD dwRetVal = 0;

		pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
		if (pAdapterInfo == NULL) {
			this->_logger.LogError("iobox_api::getNetworkAdapters. ", "Error allocating memory needed to call GetAdaptersinfo", __FILE__, __LINE__);
			return adapters;
		}
		if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
			free(pAdapterInfo);
			pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
			if (pAdapterInfo == NULL) {
				this->_logger.LogError("iobox_api::getNetworkAdapters. ", "Error allocating memory needed to call GetAdaptersinfo", __FILE__, __LINE__);
				return adapters;
			}
		}
		if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
			pAdapter = pAdapterInfo;
			while (pAdapter) {
				if (strcmp(pAdapter->IpAddressList.IpAddress.String, "0.0.0.0") == 0) {
					pAdapter = pAdapter->Next;
					continue;
				}
				network_adapter_t adapter;
				adapter.adapter_index = std::to_string(pAdapter->Index);
				adapter.adapter_name = pAdapter->AdapterName;
				adapter.adapter_description = pAdapter->Description;
				std::string mac((char*)pAdapter->Address, pAdapter->AddressLength);
				adapter.adapter_mac = stringToHexString(mac);
				adapter.adapter_ip = pAdapter->IpAddressList.IpAddress.String;
				adapter.adapter_subnet = pAdapter->IpAddressList.IpMask.String;
				adapter.adapter_gateway = pAdapter->GatewayList.IpAddress.String;
				adapters.push_back(adapter);
				pAdapter = pAdapter->Next;
			}
		}
		else {
			this->_logger.LogError("iobox_api::getNetworkAdapters. GetAdaptersInfo failed with error", std::to_string(dwRetVal), __FILE__, __LINE__);
		}
		if (pAdapterInfo) {
			free(pAdapterInfo);
		}
		for (const auto& adapter : adapters) {
			logNetworkAdapters(adapter);
		}
		return adapters;
	}

	std::vector<std::string> iobox_api::CreateDeviceChannel(const std::string& multicastInfo) {
		// We manually create the device channels based on the multicastInfo
		std::vector<std::string> devices;
		std::string DO1Channel = multicastInfo + "-DO1";
		std::string DO2Channel = multicastInfo + "-DO2";
		std::string DO3Channel = multicastInfo + "-DO3";
		std::string DO4Channel = multicastInfo + "-DO4";
		devices.push_back(DO1Channel);
		devices.push_back(DO2Channel);
		devices.push_back(DO3Channel);
		devices.push_back(DO4Channel);

		std::string DI1Channel = multicastInfo + "-DI1";
		std::string DI2Channel = multicastInfo + "-DI2";
		std::string DI3Channel = multicastInfo + "-DI3";
		std::string DI4Channel = multicastInfo + "-DI4";
		devices.push_back(DI1Channel);
		devices.push_back(DI2Channel);
		devices.push_back(DI3Channel);
		devices.push_back(DI4Channel);

		std::string AI1Channel = multicastInfo + "-AI1";
		std::string AI2Channel = multicastInfo + "-AI2";
		devices.push_back(AI1Channel);
		devices.push_back(AI2Channel);
		return devices;
	}

	std::vector<std::string> iobox_api::scanNetworkDevicesMulticast(int timeout) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		try {
			std::vector<std::string> devices;
			for (const auto& item : this->iobox_profiles) {
				if (item.is_connected || item.sock_tcp != INVALID_SOCKET) {
					std::cout << "Please close connection to " << item.iobox_info.ip_address << "(public: " << item.iobox_info.ip_public_address << ") " << "befor new scan" << std::endl;
					// return devices;
					if (!item.iobox_info.ip_public_address.empty())
					{
						disconnectToIobox(item.iobox_info.ip_public_address);
					}
					else
					{
						disconnectToIobox(item.iobox_info.ip_address);
					}
				}
			}
			//remove all elements from iobox_profiles before and we start with new scan
			this->iobox_profiles.clear();
			// WSADATA wsaData;
			SOCKET sock = INVALID_SOCKET;
			struct sockaddr_in multicastAddr;
			struct ip_mreq multicastRequest;
			char recvBuf[1024];
			struct sockaddr_in recvAddr;
			int recvAddrLen = sizeof(recvAddr);
			std::string message = "{\"request\":\"Discovery\"}";

			// if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
			//     std::cerr << "WSAStartup failed with error: " << WSAGetLastError() << std::endl;
			//     return devices;
			// }

			sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
			if (sock == INVALID_SOCKET) {
				this->_logger.LogError("iobox_api::scanNetworkDevicesMulticast. Socket failed with error:", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
				// WSACleanup();
				return devices;
			}

			memset(&multicastAddr, 0, sizeof(multicastAddr));
			multicastAddr.sin_family = AF_INET;
			multicastAddr.sin_addr.s_addr = htonl(INADDR_ANY);
			multicastAddr.sin_port = htons(this->port_mcast);
			if (bind(sock, (struct sockaddr*)&multicastAddr, sizeof(multicastAddr)) == SOCKET_ERROR) {
				this->_logger.LogError("iobox_api::scanNetworkDevicesMulticast. Bind failed with error:", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
				closesocket(sock);
				// WSACleanup();
				return devices;
			}
			multicastRequest.imr_multiaddr.s_addr = inet_addr(this->ip_mcast.c_str());
			multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);

#ifndef TEST_FIX_IP
			// if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest)) < 0) {
			//     std::cerr << "setsockopt failed with error: " << WSAGetLastError() << std::endl;
			//     closesocket(sock);
			//     // WSACleanup();
			//     return devices;
			// }
#endif
			setSocketTimeout(sock, 3000);

			auto start = std::chrono::steady_clock::now();
			sendMulticastMessage(message);
			while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) {
				int recvLen = recvfrom(sock, recvBuf, sizeof(recvBuf) - 1, 0, (struct sockaddr*)&recvAddr, &recvAddrLen);
				if (recvLen > 0) {
					recvBuf[recvLen] = '\0';
					std::string message_rsp(recvBuf);
					std::string senderIp = inet_ntoa(recvAddr.sin_addr);
					std::cout << "Received message: \"" << message_rsp << "\" from " << senderIp << std::endl;
					iobox_info_t info = parseIoboxResponse(message_rsp);

					if (info.ip_address == senderIp && info.ip_address != "" && !isStringExistInVectorString(devices, senderIp)) {
						std::string aliasName = createAliasName(info.serial_number, info.hardware_version, info.firmware_version, devices.size() + 1);
						std::string multicastAlias = createMulticastAlias(aliasName, info.model, info.serial_number, info.ip_address);
						std::cout << "multicast alias: " << multicastAlias << std::endl;
						devices.push_back(multicastAlias);
						save_info_iobox(info);
						// add to macToIpMap
						std::string mac = info.serial_number;
						std::string ip = info.ip_address;
						if (macToIpMap.find(mac) == macToIpMap.end()) {
							macToIpMap[mac] = ip;
							std::cout << "Added: " << mac << " -> " << ip << std::endl;
						}
						else {
							std::cout << "MAC already exists. Skipping insert.\n";
						}

						std::vector<std::string> deviceChannels = CreateDeviceChannel(multicastAlias);
						// add to ipToDeviceMap
						if (ipToDeviceMap.find(ip) == ipToDeviceMap.end()) {
							ipToDeviceMap[ip] = deviceChannels;
						}
						else {
							std::cout << "ip already exists. Skipping insert.\n";
						}

					}
				}
				else {
					// std::cerr << "recvfrom failed with error: " << WSAGetLastError() << std::endl;
					// sendMulticastMessage(message);
				}
			}
#ifndef TEST_FIX_IP
			// setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&multicastRequest, sizeof(multicastRequest));
#endif
			closesocket(sock);
			// WSACleanup();

			// Check if there are any devices foun. Otherwise, use unicast scan
			if (devices.empty())
			{
				std::cout << "No devices found using multicast scan. Trying unicast scan..." << std::endl;
				return scanNetworkDevicesManually(timeout);
			}
			return devices;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::scanNetworkDevicesMulticast. ", e.what(), __FILE__, __LINE__);
			return std::vector<std::string>();
		}
	}
	std::vector<std::string> iobox_api::scanNetworkDevicesManuallyOnNetworkAdapter(network_adapter_t adapter, int timeout)
	{
		std::vector<std::string> devices;

		for (const auto& item : this->iobox_profiles) {
			if (item.is_connected || item.sock_tcp != INVALID_SOCKET) {
				std::cout << "Please close connection to " << item.iobox_info.ip_address << " (public: " << item.iobox_info.ip_public_address << ") " << "befor new scan" << std::endl;
				// return devices;
				if (!item.iobox_info.ip_public_address.empty())
				{
					disconnectToIobox(item.iobox_info.ip_public_address);
				}
				else
				{
					disconnectToIobox(item.iobox_info.ip_address);
				}
			}
		}
		//remove all elements from iobox_profiles before and we start with new scan
		this->iobox_profiles.clear();
		// WSADATA wsaData;
		SOCKET sock = INVALID_SOCKET;
		struct sockaddr_in unicastAddress;
		struct ip_mreq multicastRequest;
		char recvBuf[1024];
		struct sockaddr_in recvAddr;
		int recvAddrLen = sizeof(recvAddr);
		std::string message = "{\"request\":\"Discovery\"}";

		// if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
		//     std::cerr << "WSAStartup failed with error: " << WSAGetLastError() << std::endl;
		//     return devices;
		// }

		sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (sock == INVALID_SOCKET) {
			std::cerr << "socket failed with error: " << WSAGetLastError() << std::endl;
			// WSACleanup();
			return devices;
		}

		memset(&unicastAddress, 0, sizeof(unicastAddress));
		unicastAddress.sin_family = AF_INET;
		unicastAddress.sin_addr.s_addr = htonl(INADDR_ANY);
		unicastAddress.sin_port = htons(this->port_mcast);
		if (bind(sock, (struct sockaddr*)&unicastAddress, sizeof(unicastAddress)) == SOCKET_ERROR) {
			this->_logger.LogError("iobox_api::scanNetworkDevicesManuallyOnNetworkAdapter. Bind failed with error:", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
			closesocket(sock);
			// WSACleanup();
			return devices;
		}
		setSocketTimeout(sock, 10);
		auto start = std::chrono::steady_clock::now();

		// Calculate the network address and broadcast address
		struct in_addr ip_addr, subnet_mask, network_addr, broadcast_addr;
		inet_pton(AF_INET, adapter.adapter_ip.c_str(), &ip_addr);
		inet_pton(AF_INET, adapter.adapter_subnet.c_str(), &subnet_mask);

		network_addr.s_addr = ip_addr.s_addr & subnet_mask.s_addr;
		broadcast_addr.s_addr = network_addr.s_addr | ~subnet_mask.s_addr;
		std::cout << "Network address: " << inet_ntoa(network_addr) << std::endl;
		std::cout << "Broadcast address: " << inet_ntoa(broadcast_addr) << std::endl;
		// Iterate over all possible host addresses in the subnet
		SOCKET sockSend = INVALID_SOCKET;

		sockSend = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (sockSend == INVALID_SOCKET) {
			this->_logger.LogError("iobox_api::scanNetworkDevicesManuallyOnNetworkAdapter. Socket failed with error:", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
			closesocket(sock);
			// WSACleanup();
			return devices;
		}

		for (uint32_t host = ntohl(network_addr.s_addr) + 1; host < ntohl(broadcast_addr.s_addr); ++host) {
			struct in_addr host_addr;
			host_addr.s_addr = htonl(host);
			std::string host_ip = inet_ntoa(host_addr);
			if (host_ip == adapter.adapter_ip) {
				continue;
			}
			// Send unicast message to each host
			sendUnicastMessage(sockSend, host_ip.c_str(), message);
			if (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) {
				int recvLen = recvfrom(sock, recvBuf, sizeof(recvBuf) - 1, 0, (struct sockaddr*)&recvAddr, &recvAddrLen);
				if (recvLen > 0) {
					recvBuf[recvLen] = '\0';
					std::string message_rsp(recvBuf);
					std::string senderIp = inet_ntoa(recvAddr.sin_addr);
					std::cout << "Received message: \"" << message_rsp << "\" from " << senderIp << std::endl;

					iobox_info_t info = parseIoboxResponse(message_rsp);

					if (info.ip_address == senderIp && info.ip_address != "" && !isStringExistInVectorString(devices, senderIp)) {
						std::string aliasName = createAliasName(info.serial_number, info.hardware_version, info.firmware_version, devices.size() + 1);
						std::string multicastAlias = createMulticastAlias(aliasName, info.model, info.serial_number, info.ip_address);
						std::cout << "unicast alias: " << multicastAlias << std::endl;
						devices.push_back(multicastAlias);
						save_info_iobox(info);

						// add to macToIpMap
						std::string mac = info.serial_number;
						std::string ip = info.ip_address;
						if (macToIpMap.find(mac) == macToIpMap.end()) {
							macToIpMap[mac] = ip;
							std::cout << "Added: " << mac << " -> " << ip << std::endl;
						}
						else {
							std::cout << "MAC already exists. Skipping insert.\n";
						}

						std::vector<std::string> deviceChannels = CreateDeviceChannel(multicastAlias);
						// add to ipToDeviceMap
						if (ipToDeviceMap.find(ip) == ipToDeviceMap.end()) {
							ipToDeviceMap[ip] = deviceChannels;
						}
						else {
							std::cout << "ip already exists. Skipping insert.\n";
						}
					}
					continue;
				}
				else {
					std::cout << "timeout for IP: " << host_ip << ", scan next" << std::endl;
					continue;
					// std::cerr << "recvfrom failed with error: " << WSAGetLastError() << std::endl;
					// sendMulticastMessage(message);
				}
			}
			else {
				break;
			}
		}
		closesocket(sockSend);

		closesocket(sock);
		// WSACleanup();
		return devices;
	}
	std::vector<std::string> iobox_api::scanNetworkDevicesManually(int timeout)
	{
		std::vector<network_adapter_t> adapters;
		adapters = getNetworkAdapters();
		if (adapters.size() == 0) {
			std::cerr << "Failed to get network adapters" << std::endl;
			this->_logger.LogError("iobox_api::scanNetworkDevicesManually.", "Failed to get network adapters", __FILE__, __LINE__);
			return std::vector<std::string>();
		}
		std::vector<std::string> devices;
		for (const auto& adapter : adapters) {
			std::cout << "\nAdapter: " << adapter.adapter_name << std::endl;
			std::vector<std::string> devices_adapter = scanNetworkDevicesManuallyOnNetworkAdapter(adapter, timeout);
			devices.insert(devices.end(), devices_adapter.begin(), devices_adapter.end());
		}
		for (const auto& device : devices) {
			std::cout << "Device: " << device << std::endl;
		}
		return devices;
	}
	bool parseResponeCommon(const std::string& response, const std::string& action, std::string& getResult, std::string& getReason, iobox_info_t& iobox_info) {
		try {
			cJSON* root = cJSON_Parse(response.c_str());
			if (root == nullptr) {
				std::cerr << "Failed to parse JSON" << std::endl;
				return false;
			}
			cJSON* responseObj = cJSON_GetObjectItem(root, "response");
			if (responseObj == nullptr) {
				std::cerr << "Failed to get response object" << std::endl;
				cJSON_Delete(root);
				return false;
			}
			cJSON* actionResponse = cJSON_GetObjectItem(responseObj, "action");
			if (actionResponse == nullptr || strcmp(actionResponse->valuestring, action.c_str()) != 0) {
				std::cerr << "Action mismatch or not found" << std::endl;
				cJSON_Delete(root);
				return false;
			}
			cJSON* resultResponse = cJSON_GetObjectItem(responseObj, "result");
			if (resultResponse == nullptr) {
				std::cerr << "Failed to get result" << std::endl;
				cJSON_Delete(root);
				return false;
			}
			if (resultResponse->type == cJSON_Array) {
				char* result = cJSON_PrintUnformatted(resultResponse);
				std::cout << "Result: " << result << std::endl;
				std::string resultStr(result);
				cJSON_free(result);
				getResult = resultStr;
			}
			else if (resultResponse->type == cJSON_String) {
				getResult = resultResponse->valuestring;
			}
			if (cJSON_HasObjectItem(responseObj, "info"))
			{
				cJSON* infoResponse = cJSON_GetObjectItem(responseObj, "info");
				if (infoResponse == nullptr) {
					std::cerr << "Failed to get info" << std::endl;
					cJSON_Delete(root);
					return false;
				}
				iobox_info = parseIoboxResponseObj(infoResponse);
			}
			cJSON* reasonResponse = cJSON_GetObjectItem(responseObj, "reason");
			if (reasonResponse == nullptr) {
				// std::cerr << "Failed to get reason" << std::endl;
				cJSON_Delete(root);
				return true;
			}
			getReason = reasonResponse->valuestring;

			cJSON_Delete(root);
			return true;
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return false;
		}
	}

	std::string iobox_api::connectToIobox(const std::string& remote, int port, const std::string& username, const std::string& password) {
		// check ip is already in iobox_profiles
		bool is_exist = false;
		iobox_profile_t* item = nullptr;
		std::string aliasConnect = "";
		bool is_onlyAuthen = false;
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::connectToIobox. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return aliasConnect;
		}
		std::cout << "IP address: " << ip << std::endl;
		try {
			if (!username.empty())_username = username;
			if (!password.empty())_password = password;
			if (port > 0)_port = port;
			for (auto& profile : this->iobox_profiles) {
				if (profile.iobox_info.ip_address == ip || profile.iobox_info.ip_public_address == ip) {
					is_exist = true;
					item = &profile;
					if (item->is_connected && item->is_anthenticated) {
						std::cout << "Ip is already connected and anthenticated: " << ip << std::endl;
						uint16_t idx = getIndexIoboxFromIp(ip);
						std::string aliasName = createAliasName(item->iobox_info.serial_number, item->iobox_info.hardware_version, item->iobox_info.firmware_version, idx);
						aliasConnect = createMulticastAlias(aliasName, item->iobox_info.model, item->iobox_info.serial_number, item->iobox_info.ip_public_address == "" ? item->iobox_info.ip_address : item->iobox_info.ip_public_address);
						return aliasConnect;
					}
					else if (item->is_connected && !item->is_anthenticated) {
						std::cout << "Ip is already connected but not anthenticated: " << ip << std::endl;
						// std::cout << "Please disconnect before reconnect" << std::endl;
						// return aliasConnect;
						is_onlyAuthen = true;
					}
					break;
				}
			}
			if (is_exist == false) {
				this->_logger.LogWarn("iobox_api::connectToIobox. Ip is not exist in list scan, now retry connect to this ip and append it to profile: ", ip, __FILE__, __LINE__);
				// return false;
			}

			// WSADATA wsaData;
			SOCKET sock = INVALID_SOCKET;
			struct sockaddr_in serverAddr;
			char recvBuf[1024];
			int recvLen = 0;
			struct timeval tv;
			tv.tv_sec = 3000;
			tv.tv_usec = 0;
			if (is_onlyAuthen == false)
			{
				sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
				if (sock == INVALID_SOCKET) {
					this->_logger.LogError("iobox_api::connectToIobox. Socket failed with error", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
					// WSACleanup();
					return aliasConnect;
				}

				serverAddr.sin_family = AF_INET;
				serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());
				serverAddr.sin_port = htons(_port);

				if (connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
					this->_logger.LogError("iobox_api::connectToIobox. Connect failed with error", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
					closesocket(sock);
					// WSACleanup();
					return aliasConnect;
				}
			}
			else {
				sock = item->sock_tcp;
			}

			setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));

			std::string action = "auth";
			//{"request":{"action":"auth","username":"<name>","password":"<pass>"}}
			std::string authMessage = "{\"request\":{\"action\":\"" + action + "\",\"username\":\"" + _username + "\",\"password\":\"" + _password + "\"}}";

			int sendLen = send(sock, authMessage.c_str(), authMessage.length(), 0);
			if (sendLen == SOCKET_ERROR) {
				this->_logger.LogError("iobox_api::connectToIobox. Send failed with error", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
				closesocket(sock);
				// WSACleanup();
				return aliasConnect;
			}
			//{"response":{"action":"auth","result":"<success/fail>","reason":"<if any error>"}}
			iobox_info_t info;

			recvLen = recv(sock, recvBuf, sizeof(recvBuf) - 1, 0);
			if (recvLen > 0) {
				recvBuf[recvLen] = '\0';
				std::cout << "Received message: " << recvBuf << std::endl;

				std::string response(recvBuf);
				std::string result;
				std::string reason;
				if (parseResponeCommon(response, action, result, reason, info)) {
					std::cout << "Parse response success" << std::endl;
					if (result == "success") {
						std::cout << "Authentication success" << std::endl;
					}
					else {
						this->_logger.LogError("iobox_api::connectToIobox. Authentication failed: ", reason, __FILE__, __LINE__);
						closesocket(sock);
						// WSACleanup();
						return aliasConnect;
					}
				}
				else {
					this->_logger.LogError("iobox_api::connectToIobox. Parse failed: ", response, __FILE__, __LINE__);
					closesocket(sock);
					// WSACleanup();
					return aliasConnect;
				}
			}
			else {
				this->_logger.LogError("iobox_api::connectToIobox. recv failed with error", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
				closesocket(sock);
				// WSACleanup();
				return aliasConnect;
			}

			// closesocket(sock);
			// WSACleanup();
			if (item == nullptr) {
				if (info.ip_address == "") {
					this->_logger.LogError("iobox_api::connectToIobox. ", "Failed to get info from response", __FILE__, __LINE__);
					closesocket(sock);
					return aliasConnect;
				}
				if (info.ip_address != ip) {
					info.ip_public_address = ip;
				}
				save_info_iobox(info);
				for (auto& profile : this->iobox_profiles) {
					if (profile.iobox_info.ip_address == ip || profile.iobox_info.ip_public_address == ip) {
						item = &profile;
						break;
					}
				}
			}
			if (item == nullptr) return aliasConnect;
			uint16_t idx = getIndexIoboxFromIp(ip);
			std::string aliasName = createAliasName(item->iobox_info.serial_number, item->iobox_info.hardware_version, item->iobox_info.firmware_version, idx);
			aliasConnect = createMulticastAlias(aliasName, item->iobox_info.model, item->iobox_info.serial_number, item->iobox_info.ip_public_address == "" ? item->iobox_info.ip_address : item->iobox_info.ip_public_address);
			std::cout << "alias: " << aliasConnect << std::endl;
			item->sock_tcp = sock;
			item->is_connected = true;
			item->is_anthenticated = true;
			return aliasConnect;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::connectToIobox. ", e.what(), __FILE__, __LINE__);
			return aliasConnect;
		}
	}

	bool iobox_api::connectToIoboxWithoutAuthen(const std::string& remote, int port) {
		// check ip is already in iobox_profiles
		bool is_exist = false;
		iobox_profile_t* item = nullptr;
		if (port > 0)_port = port;
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::connectToIobox. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return false;
		}
		try {
			for (auto& profile : this->iobox_profiles) {
				if (profile.iobox_info.ip_address == ip || profile.iobox_info.ip_public_address == ip) {
					is_exist = true;
					item = &profile;
					if (item->is_connected) {
						std::cout << "Ip is already connected: " << ip << std::endl;
						return true;
					}
					break;
				}
			}
			if (is_exist == false) {
				this->_logger.LogWarn("iobox_api::connectToIobox. Ip is not exist in list scan: ", ip, __FILE__, __LINE__);
				return false;
			}

			// WSADATA wsaData;
			SOCKET sock = INVALID_SOCKET;
			struct sockaddr_in serverAddr;
			char recvBuf[1024];
			int recvLen = 0;
			struct timeval tv;
			tv.tv_sec = 3000;
			tv.tv_usec = 0;
			// Initialize Winsock
			// int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
			// if (result != 0) {
			//     std::cerr << "WSAStartup failed: " << result << std::endl;
			//     return false;
			// }
			sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
			if (sock == INVALID_SOCKET) {
				this->_logger.LogError("iobox_api::connectToIobox. Socket failed with error: ", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
				// WSACleanup();
				return false;
			}

			serverAddr.sin_family = AF_INET;
			serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());
			serverAddr.sin_port = htons(_port);

			// // Bật keep-alive
			// int optval = 1;
			// int optlen = sizeof(optval);
			// if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, optlen) == SOCKET_ERROR) {
			//     std::cerr << "setsockopt for keep-alive failed: " << WSAGetLastError() << std::endl;
			//     closesocket(sock);
			//     WSACleanup();
			//     return false;
			// }

			// // Optional: Adjust Keep-Alive parameters (requires platform-specific APIs).
			// tcp_keepalive kaSettings = { 1, 5000, 1000 }; // Enable, Idle time 5s, Interval 1s
			// DWORD bytesReturned;
			// WSAIoctl(sock, SIO_KEEPALIVE_VALS, &kaSettings, sizeof(kaSettings), NULL, 0, &bytesReturned, NULL, NULL);

			if (connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
				this->_logger.LogError("iobox_api::connectToIobox. Connect failed with error: ", std::to_string(WSAGetLastError()), __FILE__, __LINE__);
				closesocket(sock);
				// WSACleanup();
				return false;
			}

			// closesocket(sock);
			// WSACleanup();
			item->sock_tcp = sock;
			item->is_connected = true;
			item->is_anthenticated = false;
			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::connectToIobox. ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}

	bool iobox_api::disconnectToIoboxWithResetOutputs(const std::string& remote) {
		return disconnectToIobox(remote);
		//std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		//std::string ip = checkIpFromRemote(remote);
		//if (ip == "") {
		//	this->_logger.LogError("iobox_api::disconnectToIobox. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
		//	return false;
		//}
		////// Reset all outputs to 0 or off before disconnect
		////setValue(remote, "DO1", "0");
		////setValue(remote, "DO2", "0");
		////setValue(remote, "DO3", "0");
		////setValue(remote, "DO4", "0");
		//try {
		//	// check ip is already in iobox_profiles
		//	bool is_exist = false;
		//	iobox_profile_t* item = nullptr;
		//	for (auto& profile : this->iobox_profiles) {
		//		if (profile.iobox_info.ip_address == ip || profile.iobox_info.ip_public_address == ip) {
		//			item = &profile;
		//			is_exist = true;
		//			if (item->is_connected) {
		//				std::cout << "Ip is already connected: " << ip << std::endl;
		//			}
		//			break;
		//		}
		//	}
		//	if (is_exist == false) {
		//		this->_logger.LogError("iobox_api::disconnectToIobox. Ip is not exist in list scan: ", ip, __FILE__, __LINE__);
		//		return false;
		//	}
		//	item->counting_get = 0;
		//	item->is_connected = false;
		//	item->is_anthenticated = false;
		//	closesocket(item->sock_tcp);
		//	item->sock_tcp = INVALID_SOCKET;
		//	// WSACleanup();
		//	std::cout << "Disconnected" << std::endl;
		//	return true;
		//}
		//catch (const std::exception& e) {
		//	this->_logger.LogFatal("iobox_api::disconnectToIobox. ", e.what(), __FILE__, __LINE__);
		//	return false;
		//}
	}

	bool iobox_api::disconnectToIobox(const std::string& remote) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::disconnectToIobox. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return false;
		}
		try {
			// check ip is already in iobox_profiles
			bool is_exist = false;
			iobox_profile_t* item = nullptr;
			for (auto& profile : this->iobox_profiles) {
				if (profile.iobox_info.ip_address == ip || profile.iobox_info.ip_public_address == ip) {
					item = &profile;
					is_exist = true;
					if (item->is_connected) {
						std::cout << "Ip is already connected: " << ip << std::endl;
					}
					break;
				}
			}
			if (is_exist == false) {
				this->_logger.LogWarn("iobox_api::disconnectToIobox. Ip is not exist in list scan: ", ip, __FILE__, __LINE__);
				return false;
			}
			item->counting_get = 0;
			item->is_connected = false;
			item->is_anthenticated = false;
			closesocket(item->sock_tcp);
			item->sock_tcp = INVALID_SOCKET;
			// WSACleanup();
			std::cout << "Disconnected" << std::endl;
			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::disconnectToIobox. ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}

	bool iobox_api::setAuthenticationIobox(const std::string& remote, const std::string& username, const std::string& password) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::setAuthenticationIobox. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return false;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				std::cerr << "IP address not found: " << ip << std::endl;
				return false;
			}
			if (!username.empty())_username = username;
			if (!password.empty())_password = password;

			std::string action = "change_auth";
			//{"request": {"action":"change_auth","username":"<name>","password":"<pass>"}}
			std::string authMessage = "{\"request\":{\"action\":\"" + action + "\",\"username\":\"" + _username + "\",\"password\":\"" + _password + "\"}}";
			if (!sendTcpMessage(ip, authMessage.c_str(), authMessage.length(), true)) {
				return false;
			}

			//{"response":{"action":"change_auth","result":"<success/fail>","reason":"<if any error>"}}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "Change auth success" << std::endl;
				}
				else {
					this->_logger.LogError("iobox_api::setAuthenticationIobox. Change auth failed: ", reason, __FILE__, __LINE__);

					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::setAuthenticationIobox. Parse response failed: ", response, __FILE__, __LINE__);
				return false;
			}

			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::setAuthenticationIobox. ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	std::string iobox_api::generateToken(const std::string& remote)
	{
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		try {
			std::string ip = checkIpFromRemote(remote);
			if (ip == "") {
				this->_logger.LogError("iobox_api::generateToken. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
				return "";
			}
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::generateToken. IP address not found: ", ip, __FILE__, __LINE__);
				return "";
			}
			//may be change this later
			return profile->iobox_info.serial_number + profile->iobox_info.model;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::generateToken. ", e.what(), __FILE__, __LINE__);
			return "";
		}

	}
	bool iobox_api::resetAuthenticationIobox(const std::string& remote, const std::string& token)
	{
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::resetAuthenticationIobox. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return false;
		}
		try {
			bool needRemove = false;
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::resetAuthenticationIobox. IP address not found: ", ip, __FILE__, __LINE__);
				// return false;
				// need temporary save iobox_info
				iobox_info_t info;
				info.ip_address = ip;
				save_info_iobox(info);
				profile = findProfileByIp(ip);
				needRemove = true;
			}
			if (connectToIoboxWithoutAuthen(ip, DEVICE_TCP_PORT) == false) {
				if (needRemove) remove_last_info_iobox();
				return false;
			}
			//{"request": {"action":"factory_reset","token":"<token>"}}
			std::string action = "factory_reset";
			std::string resetMessage = "{\"request\":{\"action\":\"" + action + "\",\"token\":\"" + token + "\"}}";
			if (!sendTcpMessage(ip, resetMessage.c_str(), resetMessage.length(), false)) {
				if (profile->is_anthenticated == false)
				{
					disconnectToIobox(ip);
					if (needRemove) remove_last_info_iobox();
				}
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				if (profile->is_anthenticated == false)
				{
					disconnectToIobox(ip);
					if (needRemove) remove_last_info_iobox();
				}
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "Factory reset success" << std::endl;
					if (profile->is_anthenticated == false)
					{
						disconnectToIobox(ip);
						if (needRemove) remove_last_info_iobox();
					}
					return true;
				}
				else {
					this->_logger.LogError("iobox_api::resetAuthenticationIobox. Factory reset failed: ", reason, __FILE__, __LINE__);
					if (profile->is_anthenticated == false)
					{
						disconnectToIobox(ip);
						if (needRemove) remove_last_info_iobox();
					}
					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::resetAuthenticationIobox. Parse response failed: ", response, __FILE__, __LINE__);
				if (profile->is_anthenticated == false)
				{
					disconnectToIobox(ip);
					if (needRemove) remove_last_info_iobox();
				}
				return false;
			}
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::resetAuthenticationIobox. ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}

	bool iobox_api::resetIobox(const std::string& remote, const std::string& token)
	{
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::resetIobox. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return false;
		}
		try {
			bool needRemove = false;
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::resetIobox. IP address not found: ", ip, __FILE__, __LINE__);
				// return false;
				// need temporary save iobox_info
				iobox_info_t info;
				info.ip_address = ip;
				save_info_iobox(info);
				profile = findProfileByIp(ip);
				needRemove = true;
			}
			if (connectToIoboxWithoutAuthen(ip, DEVICE_TCP_PORT) == false) {
				if (needRemove) remove_last_info_iobox();
				return false;
			}
			//{"request": {"action":"restart","token":"<token>"}}
			std::string action = "restart";
			std::string resetMessage = "{\"request\":{\"action\":\"" + action + "\",\"token\":\"" + token + "\"}}";
			if (!sendTcpMessage(ip, resetMessage.c_str(), resetMessage.length(), false)) {
				if (profile->is_anthenticated == false)
				{
					disconnectToIobox(ip);
					if (needRemove) remove_last_info_iobox();
				}
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				if (profile->is_anthenticated == false)
				{
					disconnectToIobox(ip);
					if (needRemove) remove_last_info_iobox();
				}
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "Restart success" << std::endl;
				}
				else {
					this->_logger.LogError("iobox_api::resetIobox. Restart failed: ", reason, __FILE__, __LINE__);

					if (profile->is_anthenticated == false)
					{
						disconnectToIobox(ip);
						if (needRemove) remove_last_info_iobox();
					}
					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::resetIobox. Parse response failed: ", response, __FILE__, __LINE__);
				if (profile->is_anthenticated == false)
				{
					disconnectToIobox(ip);
					if (needRemove) remove_last_info_iobox();
				}
				return false;
			}
			disconnectToIobox(ip);
			if (needRemove) remove_last_info_iobox();

			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::resetIobox. ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	iobox_profile_t* iobox_api::findProfileByIp(const std::string& ip) {
		std::lock_guard<std::recursive_mutex> lock(_mutex);
		try {
			iobox_profile_t* profile = nullptr;
			for (auto& item : this->iobox_profiles) {
				if (item.iobox_info.ip_address == ip || item.iobox_info.ip_public_address == ip) {
					profile = &item;
				}
			}
			return profile;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::findProfileByIp. ", e.what(), __FILE__, __LINE__);
			return nullptr;
		}
	}

	bool iobox_api::performSetValue(const std::string& ip, const std::string& channelName, const std::string& value) {
		const std::string action = "setValue";
		const std::string setValueMessage = "{\"request\":{\"action\":\"" + action + "\",\"channel\":\"" + channelName + "\",\"value\":\"" + value + "\"}}";

		if (!sendTcpMessage(ip, setValueMessage.c_str(), setValueMessage.length(), true)) {
			this->_logger.LogError("iobox_api::performSetValue.", " Failed to send TCP message", __FILE__, __LINE__);
			return false;
		}

		constexpr size_t BUFFER_SIZE = 512;  // Increased buffer size
		char revBuf[BUFFER_SIZE];
		size_t responseLength = BUFFER_SIZE - 1;  // Leave space for null terminator

		if (!receiveTcpMessage(ip, revBuf, responseLength)) {
			this->_logger.LogError("iobox_api::performSetValue. ", "Failed to receive TCP message", __FILE__, __LINE__);
			return false;
		}

		// Ensure null termination
		revBuf[responseLength] = '\0';
		std::string response(revBuf);

		std::string result, reason;
		iobox_info_t info;

		if (!parseResponeCommon(response, action, result, reason, info)) {
			this->_logger.LogError("iobox_api::performSetValue. Failed to parse response:", response, __FILE__, __LINE__);
			return false;
		}

		if (result != "success") {
			//this->_logger.LogError("iobox_api::performSetValue. Set operation failed. Reason:", reason, __FILE__, __LINE__);
			return false;
		}

		return true;
	}

	bool iobox_api::performGetValue(const std::string& ip, const std::string& channelName, std::string& outValue) {
		const std::string action = "getValue";
		const std::string getValueMessage = "{\"request\":{\"action\":\"" + action + "\",\"channel\":\"" + channelName + "\"}}";

		if (!sendTcpMessage(ip, getValueMessage.c_str(), getValueMessage.length(), true)) {
			this->_logger.LogError("iobox_api::performGetValue.", " Failed to send TCP message", __FILE__, __LINE__);
			return false;
		}

		constexpr size_t BUFFER_SIZE = 512;  // Increased buffer size
		char revBuf[BUFFER_SIZE];
		size_t responseLength = BUFFER_SIZE - 1;  // Leave space for null terminator

		if (!receiveTcpMessage(ip, revBuf, responseLength)) {
			this->_logger.LogError("iobox_api::performGetValue.", "Failed to receive TCP message", __FILE__, __LINE__);
			return false;
		}

		// Ensure null termination
		revBuf[responseLength] = '\0';
		std::string response(revBuf);

		std::string result, reason;
		iobox_info_t info;

		if (!parseResponeCommon(response, action, result, reason, info)) {
			this->_logger.LogError("iobox_api::performGetValue. Failed to parse response:", response, __FILE__, __LINE__);
			return false;
		}
		outValue = result;
		if (result == "fail") {
			//this->_logger.LogError("iobox_api::performGetValue. Get operation failed. Result:", result, __FILE__, __LINE__);
			return false;
		}
		return true;
	}

	bool iobox_api::getValueDataStringIoboxFromChannelName(const std::string& remote, const std::string& channelName, std::string& outValue) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		// Clear output parameter
		outValue.clear();
		// Validate input parameters
		if (remote.empty() || channelName.empty()) {
			this->_logger.LogError("iobox_api::getValueDataStringIoboxFromChannelName.", "Invalid parameters - remote or channelName is empty", __FILE__, __LINE__);
			return false;
		}
		std::string ip = checkIpFromRemote(remote);
		if (ip.empty()) {
			this->_logger.LogError("iobox_api::getValueDataStringIoboxFromChannelName. Failed to get ip from remote:", remote, __FILE__, __LINE__);
			return false;
		}
		// Check if we has connected to iobox
		if (!checkTcpConnectStatus(remote)) {
			disconnectToIobox(remote); // Ensure any previous connection is closed
			connectToIobox(remote, 0, "", "");
		}
		if (!checkTcpConnectStatus(remote))return false;
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::getValueDataStringIoboxFromChannelName. IP address not found:", ip, __FILE__, __LINE__);
				return false;
			}
			if (!performGetValue(ip, channelName, outValue)) {
				return false;
			}
			this->_logger.LogDebug("iobox_api::getValueDataStringIoboxFromChannelName. Successfully retrieved value for channel:", channelName, __FILE__, __LINE__);
			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::getValueDataStringIoboxFromChannelName. Exception:", e.what(), __FILE__, __LINE__);
			return false;
		}
	}

	bool iobox_api::setValueDataStringIoboxFromChannelName(const std::string& remote, const std::string& channelName, const std::string& value) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);

		// Validate input parameters
		if (remote.empty() || channelName.empty()) {
			this->_logger.LogError("iobox_api::setValueDataStringIoboxFromChannelName.", "Invalid parameters - remote or channelName is empty", __FILE__, __LINE__);
			return false;
		}

		std::string ip = checkIpFromRemote(remote);
		if (ip.empty()) {
			this->_logger.LogError("iobox_api::setValueDataStringIoboxFromChannelName. Failed to get ip from remote:", remote, __FILE__, __LINE__);
			return false;
		}
		// Check if we has connected to iobox
		if (!checkTcpConnectStatus(remote)) {
			disconnectToIobox(remote); // Ensure any previous connection is closed
			connectToIobox(remote, 0, "", "");
		}
		if (!checkTcpConnectStatus(remote))return false;
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::setValueDataStringIoboxFromChannelName. IP address not found:", ip, __FILE__, __LINE__);
				return false;
			}

			// Step 1: Set the value
			if (!performSetValue(ip, channelName, value)) {
				return false;
			}

			// Step 2: Read back and verify the value
			std::string readValue;
			if (!performGetValue(ip, channelName, readValue)) {
				return false;
			}

			// Step 3: Compare values
			if (readValue != value) {
				this->_logger.LogError("iobox_api::setValueDataStringIoboxFromChannelName. Value verification failed. Expected:", value, __FILE__, __LINE__);
				this->_logger.LogError("iobox_api::setValueDataStringIoboxFromChannelName. Value verification failed. Got:", readValue, __FILE__, __LINE__);

				return false;
			}

			this->_logger.LogDebug("iobox_api::setValueDataStringIoboxFromChannelName. Value set and verified successfully for channel:", channelName, __FILE__, __LINE__);
			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::setValueDataStringIoboxFromChannelName. Exception:", e.what(), __FILE__, __LINE__);
			return false;
		}
	}


	std::vector<std::string> iobox_api::getDeviceChannelNames(const std::string& remote)
	{
		bool needRemove = false;
		std::vector<std::string> channelNames;
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::getDeviceChannelNames. Failed to get ip from remote:", remote, __FILE__, __LINE__);
			return channelNames;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::getDeviceChannelNames. IP address not found:", ip, __FILE__, __LINE__);
				iobox_info_t info;
				info.ip_address = ip;
				save_info_iobox(info);
				profile = findProfileByIp(ip);
				needRemove = true;
			}
			if (connectToIoboxWithoutAuthen(ip, DEVICE_TCP_PORT) == false) {
				if (needRemove) remove_last_info_iobox();
				return channelNames;
			}
			//{"request": {"action":"channelList"}}
			std::string action = "channelList";
			std::string channelListMessage = "{\"request\":{\"action\":\"" + action + "\"}}";
			if (!sendTcpMessage(ip, channelListMessage.c_str(), channelListMessage.length(), false)) {
				disconnectToIobox(ip);
				if (needRemove) remove_last_info_iobox();
				return channelNames;
			}
			char revBuf[1024];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				disconnectToIobox(ip);
				if (needRemove) remove_last_info_iobox();
				return channelNames;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;

				cJSON* root = cJSON_Parse(result.c_str());
				if (root == nullptr) {
					this->_logger.LogError("iobox_api::getDeviceChannelNames. Failed to parse JSON:", result, __FILE__, __LINE__);
					disconnectToIobox(ip);
					if (needRemove) remove_last_info_iobox();
					return channelNames;
				}
				if (root->type != cJSON_Array) {
					this->_logger.LogError("iobox_api::getDeviceChannelNames. Failed to parse JSON:", "Failed to get array", __FILE__, __LINE__);
					cJSON_Delete(root);
					disconnectToIobox(ip);
					if (needRemove) remove_last_info_iobox();
					return channelNames;
				}
				cJSON* channelList = root;
				if (info.ip_address != ip)
				{
					info.ip_public_address = ip;
				}
				profile->iobox_info = info;
				int arraySize = cJSON_GetArraySize(channelList);
				for (int i = 0; i < arraySize; ++i) {
					cJSON* channel = cJSON_GetArrayItem(channelList, i);
					if (channel != nullptr && channel->type == cJSON_String) {
						uint16_t idx = getIndexIoboxFromIp(ip);
						std::string aliasName = createAliasName(info.serial_number, info.hardware_version, info.firmware_version, idx);
						std::string multicastAlias = createMulticastAlias(aliasName, info.model, info.serial_number, info.ip_public_address == "" ? info.ip_address : info.ip_public_address);
						std::string channelName = multicastAlias + "-" + channel->valuestring;
						channelNames.push_back(channelName);
					}
				}
				cJSON_Delete(root);
				// disconnectToIobox(ip);
				//not need remove
				return channelNames;
			}
			else {
				this->_logger.LogError("iobox_api::getDeviceChannelNames. Parse response failed:", response, __FILE__, __LINE__);
				//disconnectToIobox(ip);
				if (needRemove) remove_last_info_iobox();
				return channelNames;
			}
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::getDeviceChannelNames.", e.what(), __FILE__, __LINE__);
			return channelNames;
		}
	}



	bool iobox_api::openRs232Port(const std::string& remote, int baudrate, int dataBits, int stopBits, int parityBits) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			std::cerr << "Failed to get ip from remote: " << remote << std::endl;
			return false;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				std::cerr << "IP address not found: " << ip << std::endl;
				return false;
			}
			//{"request":{"action":"openRs232","dataBits":<num>,"stopBit":<num>,"parityBit":"<even/old/none>","baud": <baud>}}

			std::string action = "openRs232";
			std::string parityStr = "none";
			if (parityBits == 1) {
				parityStr = "odd";
			}
			else if (parityBits == 2) {
				parityStr = "even";
			}
			std::string openRs232Message = "{\"request\":{\"action\":\"" + action + "\",\"dataBits\":" + std::to_string(dataBits) + ",\"stopBit\":" + std::to_string(stopBits) + ",\"parityBit\":\"" + parityStr + "\",\"baud\":" + std::to_string(baudrate) + "}}";
			if (!sendTcpMessage(ip, openRs232Message.c_str(), openRs232Message.length(), true)) {
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "Open RS232 port success" << std::endl;
				}
				else {
					std::cerr << "Open RS232 port failed" << std::endl;
					std::cerr << "Reason: " << reason << std::endl;
					return false;
				}
			}
			else {
				std::cerr << "Parse response failed" << std::endl;
				return false;
			}
			return true;
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return false;
		}
	}
	bool iobox_api::closeRs232Port(const std::string& remote) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			std::cerr << "Failed to get ip from remote: " << remote << std::endl;
			return false;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				std::cerr << "IP address not found: " << ip << std::endl;
				return false;
			}
			//{"request":{"action":"closeRs232"}}

			std::string action = "closeRs232";
			std::string openRs232Message = "{\"request\":{\"action\":\"" + action + "\"}}";
			if (!sendTcpMessage(ip, openRs232Message.c_str(), openRs232Message.length(), true)) {
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "Close RS232 port success" << std::endl;
				}
				else {
					std::cerr << "Close RS232 port failed" << std::endl;
					std::cerr << "Reason: " << reason << std::endl;
					return false;
				}
			}
			else {
				std::cerr << "Parse response failed" << std::endl;
				return false;
			}
			return true;
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return false;
		}
	}
	bool iobox_api::writeRs232Port(const std::string& remote, const std::string& data, int timeout_ms) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			std::cerr << "Failed to get ip from remote: " << remote << std::endl;
			return false;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				std::cerr << "IP address not found: " << ip << std::endl;
				return false;
			}
			//{"request":{"action":"writeRs232","dataType":"<string/hexstring>","data":"<string or hex string>"}}
			std::string action = "writeRs232";
			std::string hexStringData = stringToHexString(data);
			size_t maxChunkSize = 64 * 2; // 64 bytes in hex
			size_t dataSize = hexStringData.size();
			size_t offset = 0;
			auto start = std::chrono::steady_clock::now();

			while (offset < dataSize) {
				if (std::chrono::steady_clock::now() - start > std::chrono::milliseconds(timeout_ms)) {
					std::cerr << "Write RS232 port timeout" << std::endl;
					return false;
				}
				size_t chunkSize = min(maxChunkSize, dataSize - offset);
				std::string chunk = hexStringData.substr(offset, chunkSize);
				std::string writeRs232Message = "{\"request\":{\"action\":\"" + action + "\",\"dataType\":\"hexstring\",\"data\":\"" + chunk + "\"}}";

				if (!sendTcpMessage(ip, writeRs232Message.c_str(), writeRs232Message.length(), true)) {
					return false;
				}
				char revBuf[256];
				size_t responseLength = sizeof(revBuf);
				if (!receiveTcpMessage(ip, revBuf, responseLength)) {
					return false;
				}
				revBuf[responseLength] = '\0';
				std::string response(revBuf);
				std::string result;
				std::string reason;
				iobox_info_t info;
				if (parseResponeCommon(response, action, result, reason, info)) {
					std::cout << "Parse response success" << std::endl;
					if (result == "success") {
						std::cout << "Write RS232 port success" << std::endl;
					}
					else {
						std::cerr << "Write RS232 port failed" << std::endl;
						std::cerr << "Reason: " << reason << std::endl;
						return false;
					}
				}
				else {
					std::cerr << "Parse response failed" << std::endl;
					return false;
				}
				offset += chunkSize;
			}

			return true;
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return false;
		}
	}
	std::string iobox_api::readRs232Port(const std::string& remote, const std::string& terminatorString, int lenExpect, int timeout_ms) {
		if (terminatorString == "" && lenExpect == 0) {
			std::cerr << "terminatorString and lenExpect cannot be empty at the same time" << std::endl;
			return "";
		}
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			std::cerr << "Failed to get ip from remote: " << remote << std::endl;
			return "";
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				std::cerr << "IP address not found: " << ip << std::endl;
				return "";
			}
			//{"request":{"action":"readRs232","terminatorType":"<characters/len","terminator":"<characters/len>","timeout": <ms>}}
			std::string action = "readRs232";
			if (terminatorString != "") {
				std::string readRs232Message = "{\"request\":{\"action\":\"" + action + "\",\"terminatorType\":\"characters\",\"terminator\":\"" + terminatorString + "\",\"timeout\":" + std::to_string(timeout_ms) + "}}";
				if (!sendTcpMessage(ip, readRs232Message.c_str(), readRs232Message.length(), true)) {
					return "";
				}
			}
			else if (lenExpect > 0)
			{
				std::string readRs232Message = "{\"request\":{\"action\":\"" + action + "\",\"terminatorType\":\"len\",\"terminator\":\"" + std::to_string(lenExpect) + "\",\"timeout\":" + std::to_string(timeout_ms) + "}}";
				if (!sendTcpMessage(ip, readRs232Message.c_str(), readRs232Message.length(), true)) {
					return "";
				}
			}
			char revBuf[1024];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength, timeout_ms)) {
				return "";
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				//may be revBuf is normal buffer or cstring, so need to pass responseLength
				if (result == "")
				{
					std::cerr << "Read RS232 port failed" << std::endl;
					std::cerr << "Reason: " << reason << std::endl;
					return "";
				}
				else {
					if (reason == "string")
					{
						return result;
					}
					else if (reason == "hexstring")
					{
						std::string hexString = result;
						std::string data;
						for (size_t i = 0; i < hexString.length(); i += 2) {
							std::string byteString = hexString.substr(i, 2);
							char byte = (char)strtol(byteString.c_str(), nullptr, 16);
							data.push_back(byte);
						}
						return data;
					}
					else {
						return "";
					}
				}
				return result;
			}
			else {
				std::cerr << "Parse response failed" << std::endl;
				return "";
			}

		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			return "";
		}

	}
	std::string ota_hash256OfFile(const std::string& filename) {
		std::ifstream file(filename, std::ios::binary);
		if (!file.is_open()) {
			std::cerr << "Failed to open file: " << filename << std::endl;
			return "";
		}
		std::vector<uint8_t> buffer(1024);
		std::vector<uint8_t> hashBuffer(32);
		mbedtls_sha256_context ctx;
		mbedtls_sha256_init(&ctx);
		mbedtls_sha256_starts(&ctx, 0);
		while (!file.eof()) {
			file.read((char*)buffer.data(), buffer.size());
			size_t bytesRead = file.gcount();
			if (bytesRead > 0) {
				mbedtls_sha256_update(&ctx, buffer.data(), bytesRead);
			}
		}
		mbedtls_sha256_finish(&ctx, hashBuffer.data());
		mbedtls_sha256_free(&ctx);
		file.close();
		std::stringstream ss;
		for (uint8_t byte : hashBuffer) {
			ss << std::hex << std::setw(2) << std::setfill('0') << (int)byte;
		}
		return ss.str();
	}


	bool iobox_api::otaFirmwareDevice(const std::string& remote, const std::string& filename, const std::string& type) {
		if (type != "esp" && type != "mcu") {
			this->_logger.LogFatal("iobox_api::otaFirmwareDevice.", "OTA type must be esp or mcu", __FILE__, __LINE__);
			return false;
		}
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);

		//check file name valid
		if (filename == "") {
			this->_logger.LogFatal("iobox_api::otaFirmwareDevice.", "Filename cannot be empty", __FILE__, __LINE__);
			return false;
		}
		//get len of file
		std::ifstream file(filename, std::ios::binary | std::ios::ate);
		if (!file.is_open()) {
			this->_logger.LogFatal("iobox_api::otaFirmwareDevice. Failed to open file:", filename, __FILE__, __LINE__);
			return false;
		}
		size_t lenBytes = file.tellg();
		file.close();
		if (lenBytes == 0) {
			this->_logger.LogFatal("iobox_api::otaFirmwareDevice. File is empty:", filename, __FILE__, __LINE__);
			return false;
		}
		std::cout << "File size: " << lenBytes << " bytes" << std::endl;
		std::string hash256 = ota_hash256OfFile(filename);
		if (hash256 == "") {
			this->_logger.LogFatal("iobox_api::otaFirmwareDevice. Failed to calculate hash of file:", filename, __FILE__, __LINE__);
			return false;
		}
		std::cout << "Hash: " << hash256 << std::endl;

		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogFatal("iobox_api::otaFirmwareDevice. Failed to get ip from remote:", remote, __FILE__, __LINE__);
			return false;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::otaFirmwareDevice. IP address not found:", ip, __FILE__, __LINE__);
				return false;
			}
			//{"request":{"action":"ota_start","otaType":"<mcu/esp","otaTotalLen": <lenBytes>,"hash":"<hash_hexstring>"}}
			std::string action = "ota_start";
			std::string otaStartMessage = "{\"request\":{\"action\":\"" + action + "\",\"otaType\":\"" + type + "\",\"otaTotalLen\":" + std::to_string(lenBytes) + ",\"hash\":\"" + hash256 + "\"}}";
			if (!sendTcpMessage(ip, otaStartMessage.c_str(), otaStartMessage.length(), true)) {
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength, 10000)) {
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "OTA start success" << std::endl;
				}
				else {
					this->_logger.LogError("iobox_api::otaFirmwareDevice. OTA start failed:", result, __FILE__, __LINE__);
					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::otaFirmwareDevice. Parse response failed:", response, __FILE__, __LINE__);
				return false;
			}

			//{"request":{"action":"ota_running","otaDataPackage":"<hexstring>","otaCurrentLen": <lenBytes>}}
			std::ifstream otaFile(filename, std::ios::binary);
			if (!otaFile.is_open()) {
				this->_logger.LogError("iobox_api::otaFirmwareDevice. Failed to open file :", filename, __FILE__, __LINE__);
				return false;
			}

			size_t maxChunkSize = 256;
			std::vector<char> buffer(maxChunkSize);
			size_t totalBytesSent = 0;

			while (!otaFile.eof()) {
				otaFile.read(buffer.data(), maxChunkSize);
				size_t bytesRead = otaFile.gcount();
				std::cout << "Read " << bytesRead << " bytes" << std::endl;
				if (bytesRead > 0) {
					std::string hexStringData = stringToHexString(std::string(buffer.data(), bytesRead));
					std::string otaRunningMessage = "{\"request\":{\"action\":\"ota_running\",\"otaDataPackage\":\"" + hexStringData + "\",\"otaCurrentLen\":" + std::to_string(totalBytesSent + bytesRead) + "}}";

					if (!sendTcpMessage(ip, otaRunningMessage.c_str(), otaRunningMessage.length(), true)) {
						otaFile.close();
						return false;
					}

					char revBuf[256];
					size_t responseLength = sizeof(revBuf);
					if (!receiveTcpMessage(ip, revBuf, responseLength)) {
						otaFile.close();
						return false;
					}

					revBuf[responseLength] = '\0';
					std::string response(revBuf);
					std::string result;
					std::string reason;
					iobox_info_t info;
					if (!parseResponeCommon(response, "ota_running", result, reason, info) || result != "success") {
						this->_logger.LogError("iobox_api::otaFirmwareDevice. OTA running failed :", result, __FILE__, __LINE__);
						otaFile.close();
						return false;
					}

					totalBytesSent += bytesRead;
					std::cout << "TotalBytesSent " << totalBytesSent << " bytes" << std::endl;

				}
			}
			otaFile.close();

			//{"request":{"action":"ota_end"}}
			std::string otaEndMessage = "{\"request\":{\"action\":\"ota_end\"}}";
			if (!sendTcpMessage(ip, otaEndMessage.c_str(), otaEndMessage.length(), true)) {
				return false;
			}
			char revBufEnd[256];
			size_t responseLengthEnd = sizeof(revBufEnd);
			if (!receiveTcpMessage(ip, revBufEnd, responseLengthEnd, 22000)) {
				return false;
			}
			revBufEnd[responseLengthEnd] = '\0';
			std::string responseEnd(revBufEnd);
			std::string resultEnd;
			std::string reasonEnd;
			iobox_info_t infoEnd;
			if (parseResponeCommon(responseEnd, "ota_end", resultEnd, reasonEnd, infoEnd)) {
				std::cout << "Parse response success" << std::endl;
				if (resultEnd == "success") {
					std::cout << "OTA end success" << std::endl;
				}
				else {
					this->_logger.LogError("iobox_api::otaFirmwareDevice. OTA end failed :", reasonEnd, __FILE__, __LINE__);
					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::otaFirmwareDevice. Parse response failed :", responseEnd, __FILE__, __LINE__);
				return false;
			}


			std::cout << "OTA update completed successfully" << std::endl;


			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::otaFirmwareDevice.", e.what(), __FILE__, __LINE__);
			return false;
		}
	}



	bool iobox_api::checkTcpConnectStatus(const std::string& remote) {
		try {
			std::lock_guard<std::recursive_mutex> lock(this->_mutex);
			std::string ip = checkIpFromRemote(remote);
			if (ip == "") {
				this->_logger.LogWarn("iobox_api::checkTcpConnectStatus. Failed to get ip from remote:", remote, __FILE__, __LINE__);
				return false;
			}
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogWarn("iobox_api::checkTcpConnectStatus. IP address not found:", ip, __FILE__, __LINE__);
				return false;
			}
			if (profile->sock_tcp == INVALID_SOCKET) {
				this->_logger.LogWarn("iobox_api::checkTcpConnectStatus.", "Socket is invalid", __FILE__, __LINE__);
				return false;
			}
			std::string action = "ping";
			std::string mid = std::to_string(rand());
			std::string pingMessage = "{\"request\":{\"action\":\"" + action + "\",\"mid\":\"" + mid + "\"}}";
			if (!sendTcpMessage(ip, pingMessage.c_str(), pingMessage.length(), false)) {
				disconnectToIobox(ip);
				this->_logger.LogWarn("iobox_api::checkTcpConnectStatus.", "Please retry connect", __FILE__, __LINE__);
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				disconnectToIobox(ip);
				this->_logger.LogWarn("iobox_api::checkTcpConnectStatus.", "Please retry connect", __FILE__, __LINE__);
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == mid) {
					std::cout << "Ping success" << std::endl;
					return true;
				}
				else {
					this->_logger.LogError("iobox_api::checkTcpConnectStatus. Ping failed:", reason, __FILE__, __LINE__);
					// disconnectToIobox(ip);
					std::cout << "Please retry connect" << std::endl;
					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::checkTcpConnectStatus. Parse response failed:", response, __FILE__, __LINE__);
				// disconnectToIobox(ip);
				std::cout << "Please retry connect" << std::endl;
				return false;
			}
			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::checkTcpConnectStatus.", e.what(), __FILE__, __LINE__);
			return false;
		}	
	}
	std::vector<std::string> iobox_api::advancedConnectToIobox(const std::string& remote, int port, const std::string& macAddress, const std::string& username, const std::string& password) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::vector<std::string> result;
		std::string macAddressInUppercase = toUpperCase(macAddress);
		bool needToScan = false; // We will scan for list of SN -> IP (store in map)
		// 1. Check if we do need to scan for IP
		std::string ip = remote;
		if (!ip.empty()) {
			ip = checkIpFromRemote(remote);
		}
		if ((ip == "") || (ip.empty())) { // we need to check if any mac address is valid
			if (macAddress.empty() || (macAddress == ""))
			{
				this->_logger.LogError("iobox_api::advancedConnectToIobox. Failed to get ip from remote:", remote, __FILE__, __LINE__);
				return result;
			}
			else {
				// we check if we can find ip address from mac address from map otherwise we need to scan
				if (!macToIpMap.empty()) {
					auto it = macToIpMap.find(macAddressInUppercase);
					if (it != macToIpMap.end()) {
						ip = it->second;
					}
					else {
						needToScan = true; // we need to scan for list of SN -> IP (store in map)
					}
				}
				else {
					needToScan = true; // we need to scan for list of SN -> IP (store in map)
				}
			}
		}
		//2. We will need to find valid IP address
		std::string validIp = ip;
		if (needToScan) {
			// 2.1 We will use multcast to find valid IP address
			std::vector<std::string> multiCastResults = scanNetworkDevicesMulticast(5);
			if (multiCastResults.empty()) {
				// We will manual scan for IP address
				std::vector<std::string> uniCastResults = scanNetworkDevicesManually(5);
				if (uniCastResults.empty()) {
					this->_logger.LogError("iobox_api::advancedConnectToIobox. ", "Failed to find any iobox devices", __FILE__, __LINE__);
					return result;
				}
			}
			if (macToIpMap.empty()) {
				this->_logger.LogError("iobox_api::advancedConnectToIobox. ", "Failed to find any iobox devices", __FILE__, __LINE__);
				return result;
			}
			// 2.2 Find valid IP address from mac
			auto it = macToIpMap.find(macAddressInUppercase);
			if (it != macToIpMap.end()) {
				validIp = it->second;
			}
			else {
				this->_logger.LogError("iobox_api::advancedConnectToIobox. ", "MAC address not found.", __FILE__, __LINE__);
				return result;
			}

		}
		// 3. We will connect to iobox
		std::string deviceInfo = connectToIobox(validIp, port, username, password);
		if (deviceInfo.empty()) {
			// We try a second time to rescan
			// 3.1 We will use multcast to find valid IP address
			std::vector<std::string> multiCastResults = scanNetworkDevicesMulticast(5);
			if (multiCastResults.empty()) {
				// We will manual scan for IP address
				std::vector<std::string> uniCastResults = scanNetworkDevicesManually(5);
				if (uniCastResults.empty()) {
					this->_logger.LogError("iobox_api::advancedConnectToIobox. ", "Failed to find any iobox devices", __FILE__, __LINE__);
					return result;
				}
			}
			if (macToIpMap.empty()) {
				this->_logger.LogError("iobox_api::advancedConnectToIobox. ", "Failed to find any iobox devices", __FILE__, __LINE__);
				return result;
			}
			// 3.2 Find valid IP address from mac
			auto it = macToIpMap.find(macAddressInUppercase);
			if (it != macToIpMap.end()) {
				validIp = it->second;
			}
			else {
				this->_logger.LogError("iobox_api::advancedConnectToIobox. ", "MAC address not found.", __FILE__, __LINE__);
				return result;
			}
			deviceInfo = connectToIobox(validIp, port, username, password);
			if (deviceInfo.empty()) {
				this->_logger.LogError("iobox_api::advancedConnectToIobox. ", "Failed to connect to iobox", __FILE__, __LINE__);
				return result;
			}
		}

		// 4. Get device info
		std::vector<std::string> deviceInfos;
		// 4.1 Search for device infos from ipToDeviceMap
		auto it = ipToDeviceMap.find(validIp);
		if (it != ipToDeviceMap.end()) {
			deviceInfos = it->second;
		}
		if (!deviceInfos.empty()) {
			if (!macAddress.empty()) {
				std::string firstInfoRecord = deviceInfos[0];
				// Split the record into parts
				std::vector<std::string> parts;
				std::stringstream ss(firstInfoRecord);
				std::string segment;
				while (std::getline(ss, segment, '-')) {
					parts.push_back(segment);
				}
				// Validate structure
				if (parts.size() >= 4) {
					std::string macAddressFromRecord = parts[2];  // 3rd element: MacAddress
					if (toUpperCase(macAddressFromRecord) == macAddressInUppercase) {
						result = deviceInfos;
						return result;
					}
				}
			}
			else {
				result = deviceInfos;
				return result;
			}
		}
		// 4.2 If not found, we will get device info from iobox
		result = getDeviceChannelNames(validIp);
		if (!result.empty()) {
			if (ipToDeviceMap.find(validIp) == ipToDeviceMap.end()) {
				ipToDeviceMap[validIp] = result;
			}
		}
		return result;
	}
	std::vector<std::string> iobox_api::advancedScan(int timeout) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::vector<std::string> result;
		// 1. We will use multcast to find valid IP address
		std::vector<std::string> multiCastResults = scanNetworkDevicesMulticast(timeout);
		if (multiCastResults.empty()) {
			// We will manual scan for IP address
			std::vector<std::string> uniCastResults = scanNetworkDevicesManually(timeout);
		}
		// 2. By now we do have ip address and mac map
		if (!macToIpMap.empty()) {
			// we go throught ip address found this the list by checking each map element
			for (const auto& pair : macToIpMap) {
				std::string macAddress = toUpperCase(pair.first);
				std::string ipAddress = pair.second;
				std::vector<std::string> deviceInfos;
				auto it = ipToDeviceMap.find(ipAddress);
				if (it != ipToDeviceMap.end()) {
					deviceInfos = it->second;
				}
				if (!deviceInfos.empty()) {
					for (auto& deviceInfo : deviceInfos) {
						result.push_back(deviceInfo);
					}
				}
			}
		}
		else {
			this->_logger.LogError("iobox_api::advancedScan. ", "Failed to find any iobox devices", __FILE__, __LINE__);
		}
		return result;
	}
	bool iobox_api::setValue(const std::string& remote, const std::string& channelName, const std::string& value) {
		// Validate inputs
		if (remote.empty() || channelName.empty()) {
			this->_logger.LogError("iobox_api::setValue. ", "Invalid parameters - remote or channelName is empty", __FILE__, __LINE__);
			return false;
		}
		try {
			auto startTime = std::chrono::steady_clock::now();
			auto timeoutDuration = std::chrono::milliseconds(IoboxConfig::TOTAL_TIMEOUT_MS);

			for (int attempt = 1; attempt <= IoboxConfig::MAX_RETRIES; ++attempt) {
				// Check if we've exceeded the total timeout
				auto elapsed = std::chrono::steady_clock::now() - startTime;
				if (elapsed >= timeoutDuration) {
					this->_logger.LogError("iobox_api::setValue. Total timeout exceeded after",
						std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count()), __FILE__, __LINE__);
					break;
				}

				bool result = setValueDataStringIoboxFromChannelName(remote, channelName, value);
				if (result) {
					if (attempt > 1) {
						this->_logger.LogDebug("iobox_api::setValue. Succeeded on attempt", std::to_string(attempt), __FILE__, __LINE__);
					}
					return true;
				}

				// Log retry attempt (but not on first failure to avoid spam)
				if (attempt == 2) {
					this->_logger.LogWarn("iobox_api::setValue. First attempt failed, starting retries for channel:",
						channelName, __FILE__, __LINE__);
				}
				else if (attempt % 10 == 0) {
					this->_logger.LogWarn("iobox_api::setValue. Attempt", std::to_string(attempt), __FILE__, __LINE__);
				}

				// Don't sleep on the last attempt
				if (attempt < IoboxConfig::MAX_RETRIES) {
					std::this_thread::sleep_for(std::chrono::milliseconds(IoboxConfig::RETRY_DELAY_MS));
				}
			}

			this->_logger.LogError("iobox_api::setValue. All retry attempts failed for channel after retries:", std::to_string(IoboxConfig::MAX_RETRIES), __FILE__, __LINE__);
			return false;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::setValue. Exception:", e.what(), __FILE__, __LINE__);
			return false;
		}
		catch (...) {
			this->_logger.LogFatal("iobox_api::setValue.", "Unknown exception occurred", __FILE__, __LINE__);
			return false;
		}
	}
	bool iobox_api::getValue(const std::string& remote, const std::string& channelName, std::string& outValue) {
		// Clear output parameter
		outValue.clear();

		// Validate inputs
		if (remote.empty() || channelName.empty()) {
			this->_logger.LogError("iobox_api::getValue. Invalid parameters - ", "remote or channelName is empty", __FILE__, __LINE__);
			return false;
		}

		try {
			auto startTime = std::chrono::steady_clock::now();
			auto timeoutDuration = std::chrono::milliseconds(IoboxConfig::TOTAL_TIMEOUT_MS);

			for (int attempt = 1; attempt <= IoboxConfig::MAX_RETRIES; ++attempt) {
				// Check if we've exceeded the total timeout
				auto elapsed = std::chrono::steady_clock::now() - startTime;
				if (elapsed >= timeoutDuration) {
					this->_logger.LogError("iobox_api::getValue. Total timeout exceeded after",
						std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count()), __FILE__, __LINE__);
					break;
				}

				// Use the improved version based on which option you chose earlier
				std::string tempValue;
				bool result = false;

				// Option 1: If using the output parameter version
				result = getValueDataStringIoboxFromChannelName(remote, channelName, tempValue);

				// Option 2: If using the std::optional version (uncomment if you chose this)
				/*
				auto optionalResult = getValueDataStringIoboxFromChannelName(remote, channelName);
				if (optionalResult.has_value()) {
					tempValue = *optionalResult;
					result = true;
				}
				*/

				// Option 3: If using the IoboxResult version (uncomment if you chose this)
				/*
				auto resultEx = getValueDataStringIoboxFromChannelNameEx(remote, channelName);
				if (resultEx.isSuccess()) {
					tempValue = resultEx.value;
					result = true;
				}
				*/

				if (result && !tempValue.empty()) {
					outValue = tempValue;
					if (attempt > 1) {
						this->_logger.LogDebug("iobox_api::getValue. Succeeded, value: ", outValue, __FILE__, __LINE__);
					}
					return true;
				}

				// Log retry attempt (but not on first failure to avoid spam)
				if (attempt == 2) {
					this->_logger.LogWarn("iobox_api::getValue. First attempt failed, starting retries for channel:",
						channelName, __FILE__, __LINE__);
				}
				else if (attempt % 10 == 0) {
					this->_logger.LogWarn("iobox_api::getValue. Attempt", std::to_string(attempt), __FILE__, __LINE__);
				}

				// Don't sleep on the last attempt
				if (attempt < IoboxConfig::MAX_RETRIES) {
					std::this_thread::sleep_for(std::chrono::milliseconds(IoboxConfig::RETRY_DELAY_MS));
				}
			}

			this->_logger.LogError("iobox_api::getValue. All retry attempts failed for channel:", std::to_string(IoboxConfig::MAX_RETRIES), __FILE__, __LINE__);
			return false;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::getValue. Exception:", e.what(), __FILE__, __LINE__);
			return false;
		}
		catch (...) {
			this->_logger.LogFatal("iobox_api::getValue.", "Unknown exception occurred", __FILE__, __LINE__);
			return false;
		}
	}
	bool iobox_api::toggleIobox(const std::string& remote, const std::string& channelName,
		int timeOut, bool revertFlag, bool resetFlag, bool asyncMode) {
		// Validate inputs
		if (timeOut < 0) {
			this->_logger.LogError("iobox_api::toggleIobox. Invalid timeout value", std::to_string(timeOut), __FILE__, __LINE__);
			return false;
		}

		if (remote.empty() || channelName.empty()) {
			this->_logger.LogError("iobox_api::toggleIobox. Invalid parameters", "", __FILE__, __LINE__);
			return false;
		}

		std::string key = remote + ":" + channelName;
		std::shared_ptr<ToggleInfo> toggleInfo;
		bool isNewOperation = false;

		// Critical section for map access
		{
			std::lock_guard<std::mutex> mapLock(toggleMapMutex);

			auto it = activeToggles.find(key);
			if (it != activeToggles.end()) {
				// Existing operation found for this channel
				if (!resetFlag) {
					this->_logger.LogWarn("iobox_api::toggleIobox. Toggle already active", channelName, __FILE__, __LINE__);
					return false; // Reject new call when resetFlag is false
				}

				// Reset the timeout for existing operation
				toggleInfo = it->second;
				{
					std::lock_guard<std::mutex> infoLock(toggleInfo->mtx);
					toggleInfo->endTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeOut);
					toggleInfo->cancelled = false; // Reset cancellation flag
					toggleInfo->cv.notify_one();
				}
				this->_logger.LogDebug("iobox_api::toggleIobox. Reset timeout", channelName, __FILE__, __LINE__);
				return true; // Reset successful
			}
			else {
				// Create new operation for this channel
				toggleInfo = std::make_shared<ToggleInfo>();
				toggleInfo->endTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeOut);
				toggleInfo->revertFlag = revertFlag;
				toggleInfo->active = true;
				toggleInfo->cancelled = false;

				// Get original state before starting toggle using retry logic
				bool getResult = getValue(remote, channelName, toggleInfo->originalState);
				if (!getResult || toggleInfo->originalState.empty()) {
					this->_logger.LogError("iobox_api::toggleIobox. Failed to read current state", channelName, __FILE__, __LINE__);
					return false; // Failed to read current state
				}

				activeToggles[key] = toggleInfo;
				isNewOperation = true;
				this->_logger.LogDebug("iobox_api::toggleIobox. Started new toggle operation", channelName, __FILE__, __LINE__);
			}
		}

		// Only new operations continue from here
		if (!isNewOperation) return true;

		// For async mode, start operation in separate thread and return immediately
		if (asyncMode) {
			std::thread asyncWorker([this, remote, channelName, toggleInfo, key]() {
				this->executeToggleOperation(remote, channelName, toggleInfo, key);
				});
			asyncWorker.detach(); // Detach thread to run independently
			this->_logger.LogDebug("iobox_api::toggleIobox. Started async toggle", channelName, __FILE__, __LINE__);
			return true; // Return immediately for async mode
		}

		// For sync mode, execute operation in current thread
		return executeToggleOperation(remote, channelName, toggleInfo, key);
	}

	// Enhanced helper function with comprehensive verification
	bool iobox_api::executeToggleOperation(const std::string& remote, const std::string& channelName,
		std::shared_ptr<ToggleInfo> toggleInfo, const std::string& key) {

		// RAII cleanup helper
		auto cleanup = [this, &key, &channelName]() {
			std::lock_guard<std::mutex> mapLock(toggleMapMutex);
			activeToggles.erase(key);
			this->_logger.LogDebug("iobox_api::executeToggleOperation. Cleaned up toggle", channelName, __FILE__, __LINE__);
			};

		try {
			// Define ON/OFF states (make these configurable if needed)
			const std::string onState = "1";
			const std::string offState = "0";

			// Determine toggle states - create complete toggle cycle regardless of current state
			std::string firstState, secondState;

			if (toggleInfo->revertFlag) {
				// Revert mode: go to OFF first, then back to ON
				firstState = offState;   // Always go to OFF first
				secondState = onState;   // Then revert to ON
				this->_logger.LogDebug("iobox_api::executeToggleOperation. Revert mode OFF-ON", channelName, __FILE__, __LINE__);
			}
			else {
				// Normal mode: go to ON first, then to OFF
				firstState = onState;    // Always go to ON first  
				secondState = offState;  // Then go to OFF (complete the toggle cycle)
				this->_logger.LogDebug("iobox_api::executeToggleOperation. Normal mode ON-OFF", channelName, __FILE__, __LINE__);
			}

			// Phase 1: Set the initial toggle state with verification
			this->_logger.LogDebug("iobox_api::executeToggleOperation. Setting initial state", firstState, __FILE__, __LINE__);

			if (!setValue(remote, channelName, firstState)) {
				this->_logger.LogError("iobox_api::executeToggleOperation. Failed to set initial state", channelName, __FILE__, __LINE__);
				cleanup();
				return false;
			}

			// Verify the initial state was set correctly using getValue (which includes retries)
			std::string verifyValue;
			if (!getValue(remote, channelName, verifyValue)) {
				this->_logger.LogError("iobox_api::executeToggleOperation. Failed to verify initial state", channelName, __FILE__, __LINE__);
				cleanup();
				return false;
			}

			if (verifyValue != firstState) {
				this->_logger.LogError("iobox_api::executeToggleOperation. Initial state verification failed", firstState, __FILE__, __LINE__);
				cleanup();
				return false;
			}

			this->_logger.LogDebug("iobox_api::executeToggleOperation. Initial state verified", channelName, __FILE__, __LINE__);

			// Phase 2: Wait for timeout with possible resets from other threads
			auto waitStartTime = std::chrono::steady_clock::now();
			{
				std::unique_lock<std::mutex> lock(toggleInfo->mtx);
				while (toggleInfo->active && !toggleInfo->cancelled &&
					std::chrono::steady_clock::now() < toggleInfo->endTime) {

					auto waitResult = toggleInfo->cv.wait_until(lock, toggleInfo->endTime);

					// Check if we were cancelled
					if (toggleInfo->cancelled) {
						this->_logger.LogDebug("iobox_api::executeToggleOperation. Toggle cancelled", channelName, __FILE__, __LINE__);
						cleanup();
						return false;
					}

					// Log if timeout was extended
					if (waitResult == std::cv_status::no_timeout) {
						this->_logger.LogDebug("iobox_api::executeToggleOperation. Timeout extended", channelName, __FILE__, __LINE__);
					}
				}
			}

			auto waitDuration = std::chrono::steady_clock::now() - waitStartTime;
			auto waitMs = std::chrono::duration_cast<std::chrono::milliseconds>(waitDuration).count();
			this->_logger.LogDebug("iobox_api::executeToggleOperation. Wait completed", channelName, __FILE__, __LINE__);

			// Phase 3: Set final state (always execute for complete toggle cycle)
			this->_logger.LogDebug("iobox_api::executeToggleOperation. Setting final state", secondState, __FILE__, __LINE__);

			if (!setValue(remote, channelName, secondState)) {
				this->_logger.LogError("iobox_api::executeToggleOperation. Failed to set final state", channelName, __FILE__, __LINE__);
				cleanup();
				return false;
			}

			// Verify the final state was set correctly
			if (!getValue(remote, channelName, verifyValue)) {
				this->_logger.LogError("iobox_api::executeToggleOperation. Failed to verify final state", channelName, __FILE__, __LINE__);
				cleanup();
				return false;
			}

			if (verifyValue != secondState) {
				this->_logger.LogError("iobox_api::executeToggleOperation. Final state verification failed", secondState, __FILE__, __LINE__);
				cleanup();
				return false;
			}

			// Mark as inactive and cleanup
			toggleInfo->active = false;
			cleanup();

			this->_logger.LogDebug("iobox_api::executeToggleOperation. Toggle completed successfully", channelName, __FILE__, __LINE__);
			return true;

		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::executeToggleOperation. Exception", e.what(), __FILE__, __LINE__);
			cleanup();
			return false;
		}
		catch (...) {
			this->_logger.LogFatal("iobox_api::executeToggleOperation. Unknown exception", "", __FILE__, __LINE__);
			cleanup();
			return false;
		}
	}

	// Optional: Add a method to cancel active toggles
	bool iobox_api::cancelToggle(const std::string& remote, const std::string& channelName) {
		std::string key = remote + ":" + channelName;

		std::lock_guard<std::mutex> mapLock(toggleMapMutex);
		auto it = activeToggles.find(key);
		if (it != activeToggles.end()) {
			auto toggleInfo = it->second;
			{
				std::lock_guard<std::mutex> infoLock(toggleInfo->mtx);
				toggleInfo->cancelled = true;
				toggleInfo->active = false;
				toggleInfo->cv.notify_one();
			}
			this->_logger.LogDebug("iobox_api::cancelToggle. Cancelled toggle for channel", channelName, __FILE__, __LINE__);
			return true;
		}

		this->_logger.LogWarn("iobox_api::cancelToggle. No active toggle found for channel", channelName, __FILE__, __LINE__);
		return false;
	}

	// Optional: Get status of active toggles
	std::vector<std::string> iobox_api::getActiveToggleChannels() {
		std::vector<std::string> activeChannels;
		std::lock_guard<std::mutex> mapLock(toggleMapMutex);

		for (const auto& pair : activeToggles) {
			activeChannels.push_back(pair.first); // key is "remote:channelName"
		}

		return activeChannels;
	}

	// Optional: Get detailed status of a specific toggle
	bool iobox_api::getToggleStatus(const std::string& remote, const std::string& channelName,
		int& remainingTimeMs, std::string& currentPhase) {
		std::string key = remote + ":" + channelName;

		std::lock_guard<std::mutex> mapLock(toggleMapMutex);
		auto it = activeToggles.find(key);
		if (it != activeToggles.end()) {
			auto toggleInfo = it->second;
			std::lock_guard<std::mutex> infoLock(toggleInfo->mtx);

			auto now = std::chrono::steady_clock::now();
			if (now < toggleInfo->endTime) {
				auto remaining = toggleInfo->endTime - now;
				remainingTimeMs = static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count());
				currentPhase = toggleInfo->revertFlag ? "waiting_to_revert" : "toggle_active";
				return true;
			}
			else {
				remainingTimeMs = 0;
				currentPhase = "completing";
				return true;
			}
		}

		remainingTimeMs = 0;
		currentPhase = "not_active";
		return false;
	}

	
	// More function
	//{
	//	"request": {"action":"getStaticIpconfig",
	//	}
	//}

	//{
	//	"response":{"action":"getStaticIpconfig",
	//		"result" : {"enable":true / false,
	//		"ip" : "192.168.1.2",
	//		"gw" : "",
	//		"nm" : ""}
	//	}
	//}
	std::vector<std::string> iobox_api::getStaticIpConfig(const std::string& remote) {
		std::vector<std::string> ipConfig;
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::getStaticIpConfig. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return ipConfig;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::getStaticIpConfig.", "IP address not found.", __FILE__, __LINE__);
				return ipConfig;
			}
			//{"request": {"action":"getStaticIpconfig"}}
			std::string action = "getStaticIpconfig";
			std::string getStaticIpConfigMessage = "{\"request\":{\"action\":\"" + action + "\"}}";
			if (!sendTcpMessage(ip, getStaticIpConfigMessage.c_str(), getStaticIpConfigMessage.length(), true)) {
				return ipConfig;
			}
			char revBuf[512];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				return ipConfig;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				cJSON* root = cJSON_Parse(result.c_str());
				if (root == nullptr) {
					this->_logger.LogError("iobox_api::getStaticIpConfig.", "Failed to parse JSON", __FILE__, __LINE__);
					return ipConfig;
				}
				if (root->type != cJSON_Object) {
					this->_logger.LogError("iobox_api::getStaticIpConfig.", "Failed to get object", __FILE__, __LINE__);
					cJSON_Delete(root);
					return ipConfig;
				}
				cJSON* enableItem = cJSON_GetObjectItem(root, "enable");
				cJSON* ipItem = cJSON_GetObjectItem(root, "ip");
				cJSON* gwItem = cJSON_GetObjectItem(root, "gw");
				cJSON* nmItem = cJSON_GetObjectItem(root, "nm");
				if (enableItem == nullptr || enableItem->type != cJSON_True && enableItem->type != cJSON_False) {
					this->_logger.LogError("iobox_api::getStaticIpConfig.", "Failed to get enable", __FILE__, __LINE__);
					cJSON_Delete(root);
					return ipConfig;
				}
				if (ipItem == nullptr || ipItem->type != cJSON_String) {
					this->_logger.LogError("iobox_api::getStaticIpConfig.", "Failed to get ip", __FILE__, __LINE__);
					cJSON_Delete(root);
					return ipConfig;
				}
				if (gwItem == nullptr || gwItem->type != cJSON_String) {
					this->_logger.LogError("iobox_api::getStaticIpConfig.", "Failed to get gw", __FILE__, __LINE__);
					cJSON_Delete(root);
					return ipConfig;
				}
				if (nmItem == nullptr || nmItem->type != cJSON_String) {
					this->_logger.LogError("iobox_api::getStaticIpConfig.", "Failed to get nm", __FILE__, __LINE__);
					cJSON_Delete(root);
					return ipConfig;
				}
				ipConfig.push_back(enableItem->type == cJSON_True ? "staticIpEnable:true" : "staticIpEnable:false");
				ipConfig.push_back("ip:" + std::string(ipItem->valuestring));
				ipConfig.push_back("gw:" + std::string(gwItem->valuestring));
				ipConfig.push_back("nm:" + std::string(nmItem->valuestring));
				cJSON_Delete(root);
				return ipConfig;

			}
			else {
				this->_logger.LogError("iobox_api::getStaticIpConfig.", "Parse response failed", __FILE__, __LINE__);
				return ipConfig;
			}
		}
		catch (const std::exception& e) {
			std::cerr << e.what() << std::endl;
			this->_logger.LogFatal("iobox_api::getStaticIpConfig.", e.what(), __FILE__, __LINE__);
			return ipConfig;

		}
	}
	//{"request": {"action":"setStaticIpconfig",
	//								  "enable":true/false,
	//								  "ip":"",
	//								  "gw":"",
	//								  "nm":""
	//
	//				  }
	//}
	//
	//{"response":{"action":"setStaticIpconfig",
	//					 "result":"<success/fail>",
	//					 "reason":"<if any error>"
	//					 }
	//}

	bool iobox_api::setStaticIpConfig(const std::string& remote, bool enable, const std::string& ip, const std::string& gw, const std::string& nm)
	{
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ipRemote = checkIpFromRemote(remote);
		if (ipRemote == "") {
			this->_logger.LogError("iobox_api::setStaticIpConfig. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return false;
		}
		if (ip != "" && !isValidIp(ip)) {
			std::cerr << "Invalid ip address: " << ip << std::endl;
			this->_logger.LogError("iobox_api::setStaticIpConfig. Invalid ip address: ", ip, __FILE__, __LINE__);
			return false;
		}
		if (gw != "" && !isValidIp(gw)) {
			this->_logger.LogError("iobox_api::setStaticIpConfig. Invalid gateway address: ", gw, __FILE__, __LINE__);
			return false;
		}
		if (nm != "" && !isValidIp(nm)) {
			this->_logger.LogError("iobox_api::setStaticIpConfig. Invalid netmask address: ", nm, __FILE__, __LINE__);
			return false;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ipRemote);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::setStaticIpConfig. IP address not found: ", ipRemote, __FILE__, __LINE__);
				return false;
			}
			//{"request": {"action":"setStaticIpconfig","enable":true/false,"ip":"","gw":"","nm":""}}
			std::string action = "setStaticIpconfig";
			std::string enableStr = enable ? "true" : "false";
			std::string setStaticIpConfigMessage = "{\"request\":{\"action\":\"" + action + "\",\"enable\":" + enableStr + ",\"ip\":\"" + ip + "\",\"gw\":\"" + gw + "\",\"nm\":\"" + nm + "\"}}";
			if (!sendTcpMessage(ipRemote, setStaticIpConfigMessage.c_str(), setStaticIpConfigMessage.length(), true)) {
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ipRemote, revBuf, responseLength)) {
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "Set static ip config success" << std::endl;
				}
				else {
					this->_logger.LogError("iobox_api::setStaticIpConfig. Set static ip config failed: ", reason, __FILE__, __LINE__);
					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::setStaticIpConfig. ", "Parse response failed", __FILE__, __LINE__);
				return false;
			}
			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogError("iobox_api::setStaticIpConfig. Exception: ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}

	std::string iobox_api::getValueDataStringIoboxFromChannelName(const std::string& remote, const std::string& channelName) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::getValueDataStringIoboxFromChannelName. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return "";
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::getValueDataStringIoboxFromChannelName. IP address not found: ", ip, __FILE__, __LINE__);
				return "";
			}
			//{"request": {"action":"getValue","channel":"<channel>"}}
			std::string action = "getValue";
			std::string getValueMessage = "{\"request\":{\"action\":\"" + action + "\",\"channel\":\"" + channelName + "\"}}";
			if (!sendTcpMessage(ip, getValueMessage.c_str(), getValueMessage.length(), true)) {
				return "";
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				return "";
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				return result;
			}
			else {
				this->_logger.LogError("iobox_api::getValueDataStringIoboxFromChannelName.", " Parse response failed.", __FILE__, __LINE__);

				return "";
			}
		}
		catch (const std::exception& e) {
			this->_logger.LogError("iobox_api::getValueDataStringIoboxFromChannelName.", e.what(), __FILE__, __LINE__);
			return "";
		}
	}

	bool iobox_api::toggleDigitalOutput(const std::string& remote, const std::string& channelName, const std::string& milliseconds, bool invert, bool reset) {

		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::toggleDigitalOutput. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return false;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::toggleDigitalOutput. IP address not found: ", ip, __FILE__, __LINE__);
				return false;
			}

			//not check valid channel name here

			//{"request": {"action":"toggleDO", "channel":"<channel>","milliseconds":"<milliseconds>","invert":true/false,"reset":true/false}}
			std::string action = "toggleDO";
			std::string toggleDOMessage = "{\"request\":{\"action\":\"" + action + "\",\"channel\":\"" + channelName + "\",\"milliseconds\":\"" + milliseconds + "\",\"invert\":" + (invert ? "true" : "false") + ",\"reset\":" + (reset ? "true" : "false") + "}}";
			if (!sendTcpMessage(ip, toggleDOMessage.c_str(), toggleDOMessage.length(), true)) {
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "Toggle digital output success" << std::endl;
				}
				else {
					this->_logger.LogError("iobox_api::toggleDigitalOutput. Toggle digital output failed: ", reason, __FILE__, __LINE__);
					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::toggleDigitalOutput. ", "Parse response failed", __FILE__, __LINE__);
				return false;
			}

			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::toggleDigitalOutput. Exception: ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}
	bool iobox_api::setAIBValueDataStringIoboxFromChannelName(const std::string& remote, const std::string& channelName, const std::string& value) {
		std::lock_guard<std::recursive_mutex> lock(this->_mutex);
		std::string ip = checkIpFromRemote(remote);
		if (ip == "") {
			this->_logger.LogError("iobox_api::setAIBValueDataStringIoboxFromChannelName. Failed to get ip from remote: ", remote, __FILE__, __LINE__);
			return false;
		}
		try {
			iobox_profile_t* profile = findProfileByIp(ip);
			if (profile == nullptr) {
				this->_logger.LogError("iobox_api::setAIBValueDataStringIoboxFromChannelName. IP address not found: ", ip, __FILE__, __LINE__);
				return false;
			}

			//not check valid channel name here

			//{"request": {"action":"setAIB", "channel":"<channel>","value":"<value>"}}
			std::string action = "setAIB";
			std::string setValueMessage = "{\"request\":{\"action\":\"" + action + "\",\"channel\":\"" + channelName + "\",\"value\":\"" + value + "\"}}";
			if (!sendTcpMessage(ip, setValueMessage.c_str(), setValueMessage.length(), true)) {
				return false;
			}
			char revBuf[256];
			size_t responseLength = sizeof(revBuf);
			if (!receiveTcpMessage(ip, revBuf, responseLength)) {
				return false;
			}
			revBuf[responseLength] = '\0';
			std::string response(revBuf);
			std::string result;
			std::string reason;
			iobox_info_t info;
			if (parseResponeCommon(response, action, result, reason, info)) {
				std::cout << "Parse response success" << std::endl;
				if (result == "success") {
					std::cout << "Set value success" << std::endl;
				}
				else {
					this->_logger.LogError("iobox_api::setAIBValueDataStringIoboxFromChannelName. Set value failed: ", reason, __FILE__, __LINE__);
					return false;
				}
			}
			else {
				this->_logger.LogError("iobox_api::setAIBValueDataStringIoboxFromChannelName. ","Parse response failed", __FILE__, __LINE__);
				return false;
			}

			return true;
		}
		catch (const std::exception& e) {
			this->_logger.LogFatal("iobox_api::setAIBValueDataStringIoboxFromChannelName. Exception: ", e.what(), __FILE__, __LINE__);
			return false;
		}
	}

}