#ifndef _IO_BOX_API_H_
#define _IO_BOX_API_H_
#define ANSIO_API __declspec(dllexport)
#include "LabVIEWHeader/extcode.h"
#include <vector>
#include <string>
#include <iostream>
#include <mutex>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h> // For PIP_ADAPTER_INFO
#include <chrono>
#include <memory>
#include <unordered_map>
#include "ANSLicense.h"
#pragma comment(lib,"WS2_32")
#pragma comment(lib, "Iphlpapi.lib")

// #pragma comment(lib, "Ws2_32.lib")
// #define TEST_FIX_IP
#define DEVICE_TCP_PORT 502
#define DEVICE_DATA_ADDRESS 100
#define DEVICE_DATA_LENGTH 17
#define MESSAGE_CRYPT_ENABLE 0

// Configuration constants - make these configurable if needed
namespace IoboxConfig {
	constexpr int MAX_RETRIES = 30;           // Reduced from 50 for better timeout control
	constexpr int RETRY_DELAY_MS = 100;       // Increased from 50ms for better reliability
	constexpr int TOTAL_TIMEOUT_MS = 3000;    // 3 seconds total timeout
}
namespace ANSCENTER {		
	typedef struct {
		std::string  model;
		std::string  ip_address;  //local ip
		std::string  ip_public_address; //public ip
		std::string  mac_address;
		std::string  serial_number;
		std::string  firmware_version;
		std::string  hardware_version;
		std::vector<std::string>  channelNameList;
	} iobox_info_t;
	typedef struct {
		int counting_get;
		bool is_connected;
		bool is_anthenticated;
		SOCKET sock_tcp;
		iobox_info_t iobox_info;
	} iobox_profile_t;
	typedef struct {
		std::string adapter_index;
		std::string adapter_name;
		std::string adapter_description;
		std::string adapter_mac;
		std::string adapter_ip;
		std::string adapter_subnet;
		std::string adapter_gateway;
	} network_adapter_t;
	class ANSIO_API iobox_api
	{
		private:
			// Define the map with MAC address as key and IP address as value
			std::unordered_map<std::string, std::string> macToIpMap;
			// Define unordered_map to store IP address and device information (std:vector<std::string>)
			std::unordered_map<std::string, std::vector<std::string>> ipToDeviceMap;
			std::string ip_mcast;       //ip multicast
			int port_mcast;				//port multicast
			std::string _username;		// default username
			std::string _password;		// default password
			int _port;
			const char* aes_key = "a1b2c3d4e5f6g7h8";
			std::recursive_mutex _mutex;
			std::vector<iobox_profile_t> iobox_profiles;
			iobox_profile_t* findProfileByIp(const std::string& ip);
			void save_info_iobox(const iobox_info_t& iobox_info);
			void setSocketTimeout(SOCKET sock, int timeout);
			void sendUnicastMessage(SOCKET sock, const char* ip, const std::string& message);
			void sendMulticastMessage(const std::string& message);
			bool sendTcpMessage(const std::string& ip, const char* buffer, size_t length, bool needCheckAuthen);
			bool receiveTcpMessage(const std::string& ip, char* buffer, size_t& length, int timeout = 3000);
			void show_profile_iobox(const iobox_profile_t& profile);
			bool connectToIoboxWithoutAuthen(const std::string& remote, int port);
			void remove_last_info_iobox();
			uint16_t getIndexIoboxFromIp(const std::string& ip);
			std::vector<std::string> scanNetworkDevicesManuallyOnNetworkAdapter(network_adapter_t adapter, int timeout);
			std::vector<network_adapter_t> getNetworkAdapters();
			bool performSetValue(const std::string& ip, const std::string& channelName, const std::string& value);
			bool performGetValue(const std::string& ip, const std::string& channelName, std::string& outValue);
			bool disconnectToIobox(const std::string& remote);


	private:
		SPDLogger& _logger = SPDLogger::GetInstance("IOBOX", true);
		struct ToggleInfo {
			std::chrono::steady_clock::time_point endTime;
			std::string originalState;
			bool revertFlag;
			std::atomic<bool> active{ true };
			std::atomic<bool> cancelled{ false };
			std::mutex mtx;
			std::condition_variable cv;

			// Constructor for better initialization
			ToggleInfo(bool revert = false) : revertFlag(revert) {}
		};

		std::unordered_map<std::string, std::shared_ptr<ToggleInfo>> activeToggles;
		std::mutex toggleMapMutex;

		bool executeToggleOperation(const std::string& remote, const std::string& channelName,
			std::shared_ptr<ToggleInfo> toggleInfo, const std::string& key);
		public:
			iobox_api(const std::string& ip_mcast, int port_mcast);
			~iobox_api() noexcept;
			void show_profile_iobox(const std::string& remote);
			void show_profile_ioboxs();
			[[nodiscard]] std::string generateToken(const std::string& remote);
			[[nodiscard]] std::vector<std::string> scanNetworkDevicesMulticast(int timeout);
			[[nodiscard]] std::vector<std::string> scanNetworkDevicesManually(int timeout);
			[[nodiscard]] std::vector<std::string> CreateDeviceChannel(const std::string& multicastInfo);

			bool setAuthenticationIobox(const std::string& remote, const std::string& username, const std::string& password);
			bool resetAuthenticationIobox(const std::string& remote, const std::string& token);
			bool resetIobox(const std::string& remote, const std::string& token);
			[[nodiscard]] std::string connectToIobox(const std::string& remote, int port, const std::string& username, const std::string& password);
			bool disconnectToIoboxWithResetOutputs(const std::string& remote);

			[[nodiscard]] std::vector<std::string> getDeviceChannelNames(const std::string& remote);
			bool getValueDataStringIoboxFromChannelName(const std::string& remote, const std::string& channelName, std::string& outValue);
			bool setValueDataStringIoboxFromChannelName(const std::string& remote, const std::string& channelName, const std::string& value);
			bool checkTcpConnectStatus(const std::string& remote);
			bool setValue(const std::string& remote, const std::string& channelName, const std::string& value);// Repeat until success or timeout (3s)
			bool getValue(const std::string& remote, const std::string& channelName, std::string& outValue);// Repeat until success or timeout (3s)

			//for RS232
			bool openRs232Port(const std::string& remote, int baudrate, int dataBits, int stopBits, int parityBits);
			bool closeRs232Port(const std::string& remote);
			bool writeRs232Port(const std::string& remote, const std::string& data, int timeout_ms);
			[[nodiscard]] std::string readRs232Port(const std::string& remote, const std::string& terminatorString, int lenExpect, int timeout_ms);

			//for ota: type is "esp" or "mcu"
			bool otaFirmwareDevice(const std::string& remote, const std::string& filename, const std::string& type);

			// advanced functions (it can connect to ip or mac address and return device channel names)
			[[nodiscard]] std::vector<std::string> advancedConnectToIobox(const std::string& remote, int port, const std::string& macAddress, const std::string& username, const std::string& password);
			[[nodiscard]] std::vector<std::string> advancedScan(int timeout);
			// Add to public section:
			bool toggleIobox(const std::string& remote, const std::string& channelName, int timeOut,bool revertFlag, bool resetFlag, bool asyncMode = false);
			bool cancelToggle(const std::string& remote, const std::string& channelName);
			[[nodiscard]] std::vector<std::string> getActiveToggleChannels();
			bool getToggleStatus(const std::string& remote, const std::string& channelName,int& remainingTimeMs, std::string& currentPhase);

			// new functions
			[[nodiscard]] std::vector<std::string> getStaticIpConfig(const std::string& remote);
			bool setStaticIpConfig(const std::string& remote, bool enable, const std::string& ip, const std::string& gw, const std::string& nm);
			[[nodiscard]] std::string getValueDataStringIoboxFromChannelName(const std::string& remote, const std::string& channelName);
			bool toggleDigitalOutput(const std::string& remote, const std::string& channelName, const std::string& milliseconds, bool invert, bool reset);
			bool setAIBValueDataStringIoboxFromChannelName(const std::string& remote, const std::string& channelName, const std::string& value);
	};
}
extern "C" ANSIO_API int  CreateANSIOHandle(ANSCENTER::iobox_api** Handle, const char* multicastIPAddress, int multicastPort);
extern "C" ANSIO_API int  ScanANSIOHandle(ANSCENTER::iobox_api** Handle, int timeout, LStrHandle ipAddresses);
extern "C" ANSIO_API int  ScanANSIOUnicastHandle(ANSCENTER::iobox_api** Handle, int timeout, LStrHandle ipAddresses);
extern "C" ANSIO_API int  AdvancedScanANSIOHandle(ANSCENTER::iobox_api** Handle, int timeout, LStrHandle deviceInfos);
extern "C" ANSIO_API int  AdvancedStaticScanANSIOHandle(int timeout, LStrHandle deviceInfos);

extern "C" ANSIO_API int  ConnectANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, int ioBoxPort, const char* userName, const char* passWord, LStrHandle deviceStr);
extern "C" ANSIO_API int  AdvancedConnectANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, int ioBoxPort, const char* ioSN, const char* userName, const char* passWord, LStrHandle deviceStr);

extern "C" ANSIO_API int  DisconnectANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP);
extern "C" ANSIO_API int  GetChannelNamesANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, int timeout, LStrHandle channelNames);
extern "C" ANSIO_API int  SetValueANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* channelName, const char* value);
extern "C" ANSIO_API int  GetValueANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* channelName, LStrHandle lStrValue);
extern "C" ANSIO_API int  SetAuthenticationANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* userName, const char* passWord);
extern "C" ANSIO_API int  ReleaseANSIOHandle(ANSCENTER::iobox_api** Handle);
extern "C" ANSIO_API int  ResetAuthenticationANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* token);
extern "C" ANSIO_API int  ResetANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* token);
extern "C" ANSIO_API int  GenerateANSIOTokenHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, LStrHandle tokenStr);
extern "C" ANSIO_API int  CheckANSIOStatusHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP);

// RS232
extern "C" ANSIO_API int  OpenANSAIRS232Port(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, int baudrate, int DataBits, int stopBits, int parityBits );
extern "C" ANSIO_API int  CloseANSAIRS232Port(ANSCENTER::iobox_api** Handle, const char* ioBoxIP);
extern "C" ANSIO_API int  WriteANSAIRS232Port(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* data, int timeout_ms);
extern "C" ANSIO_API int  ReadANSAIRS232Port(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* terminatorString, int lenExpect, int timeout_ms, LStrHandle receivedData);

// OTA
extern "C" ANSIO_API int  OTAANSIOFirmwareDevice(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* filename, const char* type);

// C++ interface
extern "C" ANSIO_API int  ScanANSIOHandle_CPP(ANSCENTER::iobox_api** Handle, int timeout, std::string& ipAddresses);
extern "C" ANSIO_API int  ScanANSIOUnicastHandle_CPP(ANSCENTER::iobox_api** Handle, int timeout, std::string& ipAddresses);
extern "C" ANSIO_API int  AdvancedScanANSIOHandle_CPP(ANSCENTER::iobox_api** Handle, int timeout, std::string& deviceInfos);
extern "C" ANSIO_API int  AdvancedStaticScanANSIOHandle_CPP(int timeout, std::string& deviceInfos);

extern "C" ANSIO_API int  GetChannelNamesANSIOHandle_CPP(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, int timeout, std::string& channelNames);
extern "C" ANSIO_API int  GetValueANSIOHandle_CPP(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* channelName, std::string& lStrValue);
extern "C" ANSIO_API int  GenerateANSIOTokenHandle_CPP(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, std::string& tokenStr);
extern "C" ANSIO_API int  ConnectANSIOHandle_CPP(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, int ioBoxPort, const char* userName, const char* passWord, std::string& deviceStr);
extern "C" ANSIO_API int  AdvancedConnectANSIOHandle_CPP(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, int ioBoxPort, const char* ioBoxSN, const char* userName, const char* passWord, std::string& deviceStr);

// toggle
extern "C" ANSIO_API int  ToggleIoboxHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* channelName, int timeOut, int revertFlag, int resetFlag);
extern "C" ANSIO_API int  ToggleIoboxHandleAsync(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* channelName, int timeOut, int revertFlag, int resetFlag);

// new functions
extern "C" ANSIO_API int  GetStaticIpConfigANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, LStrHandle staticIpConfigInfo);
extern "C" ANSIO_API int  SetStaticIpConfigANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, int enable, const char* ip, const char* gw, const char* nm);
extern "C" ANSIO_API int  GetValueDataStringIoboxFromChannelNameANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* channelName, LStrHandle dataValue);
extern "C" ANSIO_API int  ToggleDigitalOutputANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* channelName, int  milliseconds, int invertFlag, int resetFlag);
extern "C" ANSIO_API int  SetAIBValueDataStringIoboxFromChannelNameANSIOHandle(ANSCENTER::iobox_api** Handle, const char* ioBoxIP, const char* channelName, const char* value);

#endif


