#ifndef LIVOX_PROTO1_DEVICE_H
#define LIVOX_PROTO1_DEVICE_H

#include <string>
#include <cstdint>
#include <memory>
#include <atomic>
#include <optional>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <boost/asio.hpp>
#include "protocol.h"

// Forward declaration
namespace smo {
	class ComponentThread;
}

namespace livoxProto1 {
namespace comms {

/**		EXPLANATION:
 * This class represents a discovered device. It is used to store the
 * device identifier and IP address of a discovered device.
 */
class DiscoveredDevice
{
public:
	DiscoveredDevice(
		const std::string &deviceIdentifier,
		DeviceType deviceType,
		const std::string &ipAddr);

	// "Conversion" constructor from BroadcastMessage
	DiscoveredDevice(const BroadcastMessage &msg, const std::string &ipAddr);

	~DiscoveredDevice() = default;

	bool operator==(const DiscoveredDevice &other) const
	{
		return comms::deviceIdentifiersEqual(
			deviceIdentifier, other.deviceIdentifier);
	}

	std::string stringify(void) const;
	std::string getDeviceTypeName(void) const;

public:
	std::string deviceIdentifier;
	DeviceType deviceType;
	std::string ipAddr;
};

} // namespace comms

class Device
{
public:
	Device(const std::string &deviceIdentifier,
		const std::shared_ptr<smo::ComponentThread>& componentThread,
		int handshakeTimeoutMs, int retryDelayMs,
		const std::string& smoIp, uint8_t smoSubnetNbits,
		uint16_t dataPort, uint16_t cmdPort, uint16_t imuPort);
	~Device();

public:
	comms::DiscoveredDevice discoveredDevice;

	// Configuration
	std::shared_ptr<smo::ComponentThread> componentThread;
	int handshakeTimeoutMs, retryDelayMs;
	std::string smoIp;
	std::string detectedSmoListeningIp;
	uint8_t smoSubnetNbits;
	uint16_t dataPort, cmdPort, imuPort;

private:
	// Heartbeat mechanism
	void startHeartbeat();
	void sendHeartbeat();
	void onHeartbeatTimer(const boost::system::error_code& error);
	std::string generateClientDeviceIpFromSerialNumber(
		const std::string& broadcastCode);

	// IP detection methods
	std::optional<std::string> detectSmoIp(const std::string& deviceIP);
	uint32_t getSubnetMaskFor(uint8_t nbits);

	class ConnectReq;
	class ConnectToKnownDeviceReq;
	class ConnectByDeviceIdentifierReq;
	class ExecuteHandshakeReq;
	class DisconnectReq;

public:
	// Utility methods
	std::optional<std::string> getSmoIp(const std::string& deviceIP);

	// Callback function type definitions for async methods
	typedef std::function<void(bool success)> connectReqCbFn;
	typedef std::function<
		void(bool success, const std::string& ipAddr, int fd)>
		connectToKnownDeviceReqCbFn;
	typedef std::function<
		void(bool success, const std::string& ipAddr, int fd)>
		connectByDeviceIdentifierReqCbFn;
	typedef std::function<void(bool success, int fd)> executeHandshakeReqCbFn;
	typedef std::function<void(bool success)> disconnectReqCbFn;

	// Async connection methods
	void connectReq(connectReqCbFn callback);
	void connectToKnownDeviceReq(connectToKnownDeviceReqCbFn callback);
	void connectByDeviceIdentifierReq(
		connectByDeviceIdentifierReqCbFn callback);
	void executeHandshakeReq(
		const std::string& deviceIP, executeHandshakeReqCbFn callback);
	void disconnectReq(disconnectReqCbFn callback);

	// Heartbeat state
	std::unique_ptr<boost::asio::deadline_timer> heartbeatTimer;
	int heartbeatFd;  // Socket file descriptor used for heartbeat
	std::atomic<bool> heartbeatActive;
};

} // namespace livoxProto1

#endif // LIVOX_PROTO1_DEVICE_H
