#ifndef SOCKETADDRESS_H
#define SOCKETADDRESS_H

#include <string>

#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "config.h"
#include "IpAddress.h"

class SocketAddress
{
private:
	// IPv4-mapped IPv6 prefix: first 12 bytes (::ffff:)
	constexpr static uint8_t Ipv4MappedPrefix[12] = {
	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	    0x00, 0x00, 0x00, 0x00, 0xff, 0xff
	};
public:
	SocketAddress() = default;

	SocketAddress(const sockaddr_in6& addrIn)
	{
		address.sin6_family = AF_INET6;
		address.sin6_port = addrIn.sin6_port;
		memcpy(&address.sin6_addr, &addrIn.sin6_addr, sizeof(address.sin6_addr));
	}

	SocketAddress(const IpAddress& ipAddress, const uint16_t port)
	{
		address.sin6_family	= AF_INET6;
		address.sin6_port	= htons(port);
		ipAddress.CopyTo(address.sin6_addr);
	}

	bool operator==(const SocketAddress& other) const
	{
		return GetIpAddress() == other.GetIpAddress() && GetPort() == other.GetPort();
	}

	bool operator!=(const SocketAddress& other) const
	{
		return !(*this == other);
	}

	const sockaddr* GetAddress()		const { return (const sockaddr*)&address;	}
	size_t		GetAddressLen()		const { return sizeof(address);			}
	IpAddress	GetIpAddress()		const { return IpAddress(address.sin6_addr);	}
	uint16_t	GetPort()		const { return ntohs(address.sin6_port);	}
	std::string	ToString()		const { return ToString(GetIpAddress(), GetPort()); }

	static std::string ToString(const IpAddress& ipAddress, WORD port)
	{
		return  ipAddress.IsV4()
			? ipAddress.ToString() + ":" + std::to_string(port)
			: std::string("[") + ipAddress.ToString() + "]:" + std::to_string(port);
	}

	bool IsAny() const
	{
		return std::memcmp(&address.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0;
	}

	bool IsV4() const
	{
		// Compare the first 12 bytes with the prefix
		return std::memcmp(&address.sin6_addr, Ipv4MappedPrefix, sizeof(Ipv4MappedPrefix)) == 0;
	}

	void CopyTo(sockaddr_in6& addrIn) const
	{
		memcpy(&addrIn, &address, sizeof(addrIn));
	}

private:
	sockaddr_in6 address = {};
};


#endif /* SOCKETADDRESS_H */

