// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Network module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Network/Posix/SocketImpl.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/MemoryHelper.hpp>
#include <Nazara/Network/NetBuffer.hpp>
#include <Nazara/Network/Posix/IpAddressImpl.hpp>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <poll.h>
#include <unistd.h>
#include <cstring>
#include <Nazara/Network/Debug.hpp>

namespace Nz
{
	constexpr int SOCKET_ERROR = -1;

	SocketHandle SocketImpl::Accept(SocketHandle handle, IpAddress* address, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		IpAddressImpl::SockAddrBuffer nameBuffer;
		std::fill(nameBuffer.begin(), nameBuffer.end(), 0);

		socklen_t bufferLength = sizeof(sockaddr_in);

		SocketHandle newClient = accept(handle, reinterpret_cast<sockaddr*>(&nameBuffer), &bufferLength);
		if (newClient != InvalidHandle)
		{
			if (address)
				*address = IpAddressImpl::FromSockAddr(reinterpret_cast<const sockaddr*>(&nameBuffer));

			if (error)
				*error = SocketError_NoError;
		}
		else
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());
		}

		return newClient;
	}

	SocketState SocketImpl::Bind(SocketHandle handle, const IpAddress& address, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(address.IsValid(), "Invalid address");

		IpAddressImpl::SockAddrBuffer nameBuffer;
		int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data());

		if (bind(handle, reinterpret_cast<const sockaddr*>(&nameBuffer), bufferLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return SocketState_NotConnected;
		}

		if (error)
			*error = SocketError_NoError;

		return SocketState_Bound;
	}

	SocketHandle SocketImpl::Create(NetProtocol protocol, SocketType type, SocketError* error)
	{
		NazaraAssert(protocol != NetProtocol_Any, "Any protocol is not supported for socket creation");
		NazaraAssert(type <= SocketType_Max, "Type has value out of enum");

		SocketHandle handle = socket(TranslateNetProtocolToAF(protocol), TranslateSocketTypeToSock(type), 0);
		if (handle == InvalidHandle && error != nullptr)
			*error = TranslateErrnoToResolveError(GetLastErrorCode());

		return handle;
	}

	void SocketImpl::Close(SocketHandle handle)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		if (close(handle) == SOCKET_ERROR)
			NazaraWarning("Failed to close socket: " + Error::GetLastSystemError(GetLastErrorCode()));
	}

	void SocketImpl::ClearErrorCode(SocketHandle handle)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		if (GetLastError(handle, nullptr) < 0)
			NazaraWarning("Failed to clear socket error code: " + Error::GetLastSystemError(GetLastErrorCode()));
	}

	SocketState SocketImpl::Connect(SocketHandle handle, const IpAddress& address, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(address.IsValid(), "Invalid address");

		IpAddressImpl::SockAddrBuffer nameBuffer;
		int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data());

		if (error)
			*error = SocketError_NoError;

		// Clear socket error status
		ClearErrorCode(handle);

		if (connect(handle, reinterpret_cast<const sockaddr*>(nameBuffer.data()), bufferLength) == SOCKET_ERROR)
		{
			int errorCode = GetLastErrorCode();
			switch (errorCode) //< Check for "normal errors" first
			{
				case EALREADY:
				case EINPROGRESS:
					return SocketState_Connecting;

				case EISCONN:
					return SocketState_Connected;
			}

			if (error)
			{
				if (errorCode == EADDRNOTAVAIL)
					*error = SocketError_ConnectionRefused; //< ConnectionRefused seems more legit than AddressNotAvailable in connect case
				else
					*error = TranslateErrnoToResolveError(errorCode);
			}

			return SocketState_NotConnected;
		}

		return SocketState_Connected;
	}

	SocketState SocketImpl::Connect(SocketHandle handle, const IpAddress& address, UInt64 msTimeout, SocketError* error)
	{
		SocketState state = Connect(handle, address, error);
		if (state == SocketState_Connecting)
		{
			// http://developerweb.net/viewtopic.php?id=3196
			fd_set localSet;
			FD_ZERO(&localSet);
			FD_SET(handle, &localSet);

			timeval tv;
			tv.tv_sec = static_cast<long>(msTimeout / 1000ULL);
			tv.tv_usec = static_cast<long>((msTimeout % 1000ULL) * 1000ULL);

			int ret = select(handle + 1, nullptr, &localSet, &localSet, (msTimeout > 0) ? &tv : nullptr);
			if (ret == SOCKET_ERROR)
			{
				int code = GetLastErrorCode(handle, error);
				if (code < 0) //< GetLastErrorCode() failed
					return SocketState_NotConnected;

				if (code)
				{
					if (error)
						*error = TranslateErrnoToResolveError(code);

					return SocketState_NotConnected;
				}
			}
			else if (ret == 0)
			{
				if (error)
					*error = SocketError_TimedOut;

				return SocketState_NotConnected;
			}
			else
			{
				if (error)
					*error = TranslateErrnoToResolveError(GetLastErrorCode());

				return SocketState_NotConnected;
			}

			if (error)
				*error = SocketError_NoError;

			state = SocketState_Connected;
		}

		return state;
	}

	bool SocketImpl::Initialize()
	{
		return true;
	}

	SocketError SocketImpl::GetLastError(SocketHandle handle, SocketError* error)
	{
		int code = GetLastErrorCode(handle, error);
		if (code < 0)
			return SocketError_Internal;

		return TranslateErrnoToResolveError(code);
	}

	int SocketImpl::GetLastErrorCode()
	{
		return errno;
	}

	int SocketImpl::GetLastErrorCode(SocketHandle handle, SocketError* error)
	{
		int code;
		unsigned int codeLength = sizeof(code);

		if (getsockopt(handle, SOL_SOCKET, SO_ERROR, &code, &codeLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return -1;
		}

		if (error)
			*error = SocketError_NoError;

		return code;
	}

	SocketState SocketImpl::Listen(SocketHandle handle, const IpAddress& address, unsigned int queueSize, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(address.IsValid(), "Invalid address");

		IpAddressImpl::SockAddrBuffer nameBuffer;
		int bufferLength = IpAddressImpl::ToSockAddr(address, nameBuffer.data());

		if (bind(handle, reinterpret_cast<const sockaddr*>(&nameBuffer), bufferLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return SocketState_NotConnected;
		}

		if (listen(handle, queueSize) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return SocketState_NotConnected;
		}

		if (error)
			*error = SocketError_NoError;

		return SocketState_Bound;
	}

	std::size_t SocketImpl::QueryAvailableBytes(SocketHandle handle, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		u_long availableBytes;
		if (ioctl(handle, FIONREAD, &availableBytes) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return 0;
		}

		if (error)
			*error = SocketError_NoError;

		return availableBytes;
	}

	bool SocketImpl::QueryBroadcasting(SocketHandle handle, SocketError* error)
	{
		bool code;
		socklen_t codeLength = sizeof(code);

		if (getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &code, &codeLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false;
		}

		if (error)
			*error = SocketError_NoError;

		return code;
	}

	bool SocketImpl::QueryKeepAlive(SocketHandle handle, SocketError* error)
	{
		bool code;
		socklen_t codeLength = sizeof(code);

		if (getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &code, &codeLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false;
		}

		if (error)
			*error = SocketError_NoError;

		return code;
	}

	std::size_t SocketImpl::QueryMaxDatagramSize(SocketHandle handle, SocketError* error)
	{
		unsigned int code;
		socklen_t codeLength = sizeof(code);

		if (getsockopt(handle, IPPROTO_IP, IP_MTU, &code, &codeLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return 0;
		}

		if (error)
			*error = SocketError_NoError;

		return code;
	}

	bool SocketImpl::QueryNoDelay(SocketHandle handle, SocketError* error)
	{
		bool code;
		socklen_t codeLength = sizeof(code);

		if (getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &code, &codeLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false;
		}

		if (error)
			*error = SocketError_NoError;

		return code;
	}

	std::size_t SocketImpl::QueryReceiveBufferSize(SocketHandle handle, SocketError* error)
	{
		unsigned int code;
		socklen_t codeLength = sizeof(code);

		if (getsockopt(handle, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&code), &codeLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return 0;
		}

		if (error)
			*error = SocketError_NoError;

		return code;
	}

	IpAddress SocketImpl::QueryPeerAddress(SocketHandle handle, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		IpAddressImpl::SockAddrBuffer nameBuffer;
		std::fill(nameBuffer.begin(), nameBuffer.end(), 0);

		socklen_t bufferLength = sizeof(nameBuffer.size());

		if (getpeername(handle, reinterpret_cast<sockaddr*>(nameBuffer.data()), &bufferLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return IpAddress();
		}

		if (error)
			*error = SocketError_NoError;

		return IpAddressImpl::FromSockAddr(reinterpret_cast<sockaddr*>(nameBuffer.data()));
	}

	std::size_t SocketImpl::QuerySendBufferSize(SocketHandle handle, SocketError* error)
	{
		unsigned int code;
		socklen_t codeLength = sizeof(code);

		if (getsockopt(handle, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&code), &codeLength) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return 0;
		}

		if (error)
			*error = SocketError_NoError;

		return code;
	}

	IpAddress SocketImpl::QuerySocketAddress(SocketHandle handle, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		IpAddressImpl::SockAddrBuffer nameBuffer;
		std::fill(nameBuffer.begin(), nameBuffer.end(), 0);

		socklen_t bufferLength = sizeof(sockaddr_in);

		if (getsockname(handle, reinterpret_cast<sockaddr*>(nameBuffer.data()), &bufferLength) == SOCKET_ERROR)
		{
			if (error)
			{
				int errorCode = GetLastErrorCode();
				if (errorCode == EINVAL)
					*error = SocketError_NoError;
				else
					*error = TranslateErrnoToResolveError(errorCode);
			}

			return IpAddress();
		}

		if (error)
			*error = SocketError_NoError;

		return IpAddressImpl::FromSockAddr(reinterpret_cast<sockaddr*>(nameBuffer.data()));
	}

	int SocketImpl::Poll(PollSocket* fdarray, std::size_t nfds, int timeout, SocketError* error)
	{
		NazaraAssert(fdarray && nfds > 0, "Invalid fdarray");

		static_assert(sizeof(PollSocket) == sizeof(pollfd), "PollSocket size must match WSAPOLLFD size");

		int result = poll(reinterpret_cast<pollfd*>(fdarray), static_cast<nfds_t>(nfds), timeout);
		if (result < 0)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return 0;
		}

		return result;
	}

	bool SocketImpl::Receive(SocketHandle handle, void* buffer, int length, int* read, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(buffer && length > 0, "Invalid buffer");

		int byteRead = recv(handle, reinterpret_cast<char*>(buffer), length, 0);
		if (byteRead == SOCKET_ERROR)
		{
			int errorCode = GetLastErrorCode();
			if (errorCode == EAGAIN)
				errorCode = EWOULDBLOCK;

			switch (errorCode)
			{
				case EWOULDBLOCK:
				{
					// If we have no data and are not blocking, return true with 0 byte read
					byteRead = 0;
					break;
				}

				default:
				{
					if (error)
						*error = TranslateErrnoToResolveError(errorCode);

					return false; //< Error
				}
			}
		}
		else if (byteRead == 0)
		{
			if (error)
				*error = SocketError_ConnectionClosed;

			return false; //< Connection has been closed
		}

		if (read)
			*read = byteRead;

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::ReceiveFrom(SocketHandle handle, void* buffer, int length, IpAddress* from, int* read, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(buffer && length > 0, "Invalid buffer");

		IpAddressImpl::SockAddrBuffer nameBuffer;
		std::fill(nameBuffer.begin(), nameBuffer.end(), 0);

		socklen_t bufferLength = static_cast<socklen_t>(nameBuffer.size());

		IpAddress senderIp;

		int byteRead = recvfrom(handle, buffer, length, 0, reinterpret_cast<sockaddr*>(&nameBuffer), &bufferLength);
		if (byteRead == -1)
		{
			int errorCode = GetLastErrorCode();
			if (errorCode == EAGAIN)
				errorCode = EWOULDBLOCK;

			switch (errorCode)
			{
				case EWOULDBLOCK:
				{
					// If we have no data and are not blocking, return true with 0 byte read
					byteRead = 0;
					senderIp = IpAddress::Invalid;
					break;
				}

				default:
				{
					if (error)
						*error = TranslateErrnoToResolveError(errorCode);

					return false; //< Error
				}
			}
		}
		else if (byteRead == 0)
		{
			if (error)
				*error = SocketError_ConnectionClosed;

			return false; //< Connection closed
		}
		else // else we received something
			senderIp = IpAddressImpl::FromSockAddr(reinterpret_cast<const sockaddr*>(&nameBuffer));

		if (from)
			*from = IpAddressImpl::FromSockAddr(reinterpret_cast<const sockaddr*>(&nameBuffer));

		if (read)
			*read = byteRead;

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::ReceiveMultiple(SocketHandle handle, NetBuffer* buffers, std::size_t bufferCount, IpAddress* from, int* read, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(buffers && bufferCount > 0, "Invalid buffers");

		StackArray<iovec> sysBuffers = NazaraStackAllocation(iovec, bufferCount);
		for (std::size_t i = 0; i < bufferCount; ++i)
		{
			sysBuffers[i].iov_base = buffers[i].data;
			sysBuffers[i].iov_len = buffers[i].dataLength;
		}

		struct msghdr msgHdr;
		std::memset(&msgHdr, 0, sizeof(msgHdr));

		msgHdr.msg_iov = sysBuffers.data();
		msgHdr.msg_iovlen = static_cast<int>(bufferCount);

		IpAddressImpl::SockAddrBuffer nameBuffer;
		std::fill(nameBuffer.begin(), nameBuffer.end(), 0);

		if (from)
		{
			msgHdr.msg_name = nameBuffer.data();
			msgHdr.msg_namelen = static_cast<socklen_t>(nameBuffer.size());
		}

		IpAddress senderIp;

		int byteRead = recvmsg(handle, &msgHdr, MSG_NOSIGNAL);
		if (byteRead == -1)
		{
			int errorCode = GetLastErrorCode();
			if (errorCode == EAGAIN)
				errorCode = EWOULDBLOCK;

			switch (errorCode)
			{
				case EWOULDBLOCK:
				{
					// If we have no data and are not blocking, return true with 0 byte read
					byteRead = 0;
					senderIp = IpAddress::Invalid;
					break;
				}

				default:
				{
					if (error)
						*error = TranslateErrnoToResolveError(errorCode);

					return false; //< Error
				}
			}
		}
		else if (byteRead == 0)
		{
			if (error)
				*error = SocketError_ConnectionClosed;

			return false; //< Connection closed
		}
		else // else we received something
			senderIp = IpAddressImpl::FromSockAddr(reinterpret_cast<const sockaddr*>(nameBuffer.data()));

#ifdef HAS_MSGHDR_FLAGS
		if (msgHdr.msg_flags & MSG_TRUNC)
		{
			if (error)
				*error = SocketError_DatagramSize;

			return false;
		}
#endif

		if (from)
			*from = senderIp;

		if (read)
			*read = byteRead;

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::Send(SocketHandle handle, const void* buffer, int length, int* sent, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(buffer && length > 0, "Invalid buffer");

		int byteSent = send(handle, reinterpret_cast<const char*>(buffer), length, 0);
		if (byteSent == SOCKET_ERROR)
		{
			int errorCode = GetLastErrorCode();
			if (errorCode == EAGAIN)
				errorCode = EWOULDBLOCK;

			switch (errorCode)
			{
				case EWOULDBLOCK:
					byteSent = 0;
					break;

				default:
				{
					if (error)
						*error = TranslateErrnoToResolveError(errorCode);

					return false; //< Error
				}
			}
		}

		if (sent)
			*sent = byteSent;

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SendMultiple(SocketHandle handle, const NetBuffer* buffers, std::size_t bufferCount, const IpAddress& to, int* sent, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(buffers && bufferCount > 0, "Invalid buffers");

		StackArray<iovec> sysBuffers = NazaraStackAllocation(iovec, bufferCount);
		for (std::size_t i = 0; i < bufferCount; ++i)
		{
			sysBuffers[i].iov_base = buffers[i].data;
			sysBuffers[i].iov_len = buffers[i].dataLength;
		}

		struct msghdr msgHdr;
		std::memset(&msgHdr, 0, sizeof(msgHdr));

		IpAddressImpl::SockAddrBuffer nameBuffer;
		msgHdr.msg_namelen = IpAddressImpl::ToSockAddr(to, nameBuffer.data());
		msgHdr.msg_name = nameBuffer.data();
		msgHdr.msg_iov = sysBuffers.data();
		msgHdr.msg_iovlen = static_cast<int>(bufferCount);

		int byteSent = sendmsg(handle, &msgHdr, MSG_NOSIGNAL);
		if (byteSent == SOCKET_ERROR)
		{
			int errorCode = GetLastErrorCode();
			if (errorCode == EAGAIN)
				errorCode = EWOULDBLOCK;

			switch (errorCode)
			{
				case EWOULDBLOCK:
					byteSent = 0;
					break;

				default:
				{
					if (error)
						*error = TranslateErrnoToResolveError(errorCode);

					return false; //< Error
				}
			}
		}

		if (sent)
			*sent = static_cast<int>(byteSent);

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SendTo(SocketHandle handle, const void* buffer, int length, const IpAddress& to, int* sent, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");
		NazaraAssert(buffer && length > 0, "Invalid buffer");

		IpAddressImpl::SockAddrBuffer nameBuffer;
		int bufferLength = IpAddressImpl::ToSockAddr(to, nameBuffer.data());

		int byteSent = sendto(handle, reinterpret_cast<const char*>(buffer), length, 0, reinterpret_cast<const sockaddr*>(nameBuffer.data()), bufferLength);
		if (byteSent == SOCKET_ERROR)
		{
			int errorCode = GetLastErrorCode();
			if (errorCode == EAGAIN)
				errorCode = EWOULDBLOCK;

			switch (errorCode)
			{
				case EWOULDBLOCK:
					byteSent = 0;
					break;

				default:
				{
					if (error)
						*error = TranslateErrnoToResolveError(errorCode);

					return false; //< Error
				}
			}
		}

		if (sent)
			*sent = byteSent;

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SetBlocking(SocketHandle handle, bool blocking, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		u_long block = (blocking) ? 0 : 1;
		if (ioctl(handle, FIONBIO, &block) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SetBroadcasting(SocketHandle handle, bool broadcasting, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		int option = broadcasting;
		if (setsockopt(handle, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<const char*>(&option), sizeof(option)) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SetIPv6Only(SocketHandle handle, bool ipv6Only, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		int option = ipv6Only;
		if (setsockopt(handle, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&option), sizeof(option)) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		int keepAlive = enabled ? 1 : 0;
		int keepIdle = msTime / 1000; // Linux works with seconds.
		int keepInterval = msInterval / 1000; // Linux works with seconds.

		if (setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &keepAlive , sizeof(keepAlive)) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (setsockopt(handle, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(keepIdle)) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (setsockopt(handle, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(keepInterval)) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SetNoDelay(SocketHandle handle, bool nodelay, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		int option = nodelay ? 1 : 0;
		if (setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option)) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SetReceiveBufferSize(SocketHandle handle, std::size_t size, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		int option = static_cast<int>(size);
		if (setsockopt(handle, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const char*>(&option), sizeof(option)) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	bool SocketImpl::SetSendBufferSize(SocketHandle handle, std::size_t size, SocketError* error)
	{
		NazaraAssert(handle != InvalidHandle, "Invalid handle");

		int option = static_cast<int>(size);
		if (setsockopt(handle, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char*>(&option), sizeof(option)) == SOCKET_ERROR)
		{
			if (error)
				*error = TranslateErrnoToResolveError(GetLastErrorCode());

			return false; //< Error
		}

		if (error)
			*error = SocketError_NoError;

		return true;
	}

	SocketError SocketImpl::TranslateErrnoToResolveError(int error)
	{
		switch (error)
		{
			case 0:
				return SocketError_NoError;

			// Engine error
			case EACCES:
			case EBADF:
			case EINVAL:
			case EFAULT:
			case ENOTSOCK:
			case EPROTOTYPE:
				return SocketError_Internal;

			case EADDRNOTAVAIL:
			case EADDRINUSE:
				return SocketError_AddressNotAvailable;

			case EAFNOSUPPORT:
			case EPFNOSUPPORT:
			case EOPNOTSUPP:
			case EPROTONOSUPPORT:
			case ESOCKTNOSUPPORT:
				return SocketError_NotSupported;

			// Those are not errors and should have been handled before the call
			case EALREADY:
			case EISCONN:
			case EWOULDBLOCK:
				return SocketError_Internal;

			case ECONNREFUSED:
				return SocketError_ConnectionRefused;

			case EMSGSIZE:
				return SocketError_DatagramSize;

			case EMFILE:
			case ENOBUFS:
			case ENOMEM:
				return SocketError_ResourceError;

			case ENOTCONN:
			case ESHUTDOWN:
				return SocketError_ConnectionClosed;

			case EHOSTUNREACH:
				return SocketError_UnreachableHost;

			case ENETDOWN:
			case ENETUNREACH:
				return SocketError_NetworkError;

			case ENODATA:
				return SocketError_NotInitialized;

			case ETIMEDOUT:
				return SocketError_TimedOut;
		}

		NazaraWarning("Unhandled POSIX error: " + Error::GetLastSystemError(error) + " (" + String::Number(error) + ')');
		return SocketError_Unknown;
	}

	int SocketImpl::TranslateNetProtocolToAF(NetProtocol protocol)
	{
		NazaraAssert(protocol <= NetProtocol_Max, "Protocol has value out of enum");

		static int addressFamily[] = {
			AF_UNSPEC, //< NetProtocol_Any
			AF_INET,   //< NetProtocol_IPv4
			AF_INET6,  //< NetProtocol_IPv6
			-1         //< NetProtocol_Unknown
		};
		static_assert(sizeof(addressFamily) / sizeof(int) == NetProtocol_Max + 1, "Address family array is incomplete");

		return addressFamily[protocol];
	}

	int SocketImpl::TranslateSocketTypeToSock(SocketType type)
	{
		NazaraAssert(type <= SocketType_Max, "Socket type has value out of enum");

		static int socketType[] = {
			SOCK_RAW,     //< SocketType_Raw
			SOCK_STREAM,  //< SocketType_TCP
			SOCK_DGRAM,   //< SocketType_UDP
			-1            //< SocketType_Unknown
		};
		static_assert(sizeof(socketType) / sizeof(int) == SocketType_Max + 1, "Socket type array is incomplete");

		return socketType[type];
	}

	void SocketImpl::Uninitialize()
	{
	}

	SocketHandle SocketImpl::InvalidHandle = -1;
	SocketImpl::socketID SocketImpl::s_socket;
}
