#include "Flatline.h"

thread int UDPServer_Socket = 0;

bool NetUDPServer_Listen(uint16_t port, bool (*packet_callback)(char* data, int datalen, int from))
{
	UDPServer_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	
	if (UDPServer_Socket < 0)
	{
		DDrStartupMessage("could not create socket");
		return false;
	}
	
	sockaddr_in address;
	memset(&address, 0, sizeof(sockaddr_in));
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = htonl(INADDR_ANY);
	address.sin_port = htons(port);
	
	if (bind(UDPServer_Socket, (sockaddr*)&address, sizeof(sockaddr_in)) < 0)
	{
		DDrStartupMessage("could not bind port %d", port);
		closesocket(UDPServer_Socket);
		return false;
	}
	
	char data[65537];
	data[sizeof(data) - 1] = '\0';
	sockaddr_in from;
	int recvlen;
	for (;;)
	{
		int fromlen = sizeof(sockaddr_in);
		memset(&from, 0, sizeof(sockaddr_in));
		recvlen = recvfrom(UDPServer_Socket, data, sizeof(data) - 1, 0, (sockaddr*)&from, (void*)&fromlen);
		if (!packet_callback(data, recvlen, ntohl(from.sin_addr.s_addr)))
			break;
	}
	closesocket(UDPServer_Socket);
	return true;
}

bool NetUDPServer_Send(sockaddr* address, char* data, int datalen)
{
	return NetUDPSocket_Send(UDPServer_Socket, address, data, datalen);
}

int NetUDPSocket_Create(uint16_t port)
{
	int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	
	if (sock < 0)
	{
		DDrStartupMessage("could not create socket");
		return -1;
	}
	
	sockaddr_in address;
	memset(&address, 0, sizeof(sockaddr_in));
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = htonl(INADDR_ANY);
	address.sin_port = htons(port);
	
	if (bind(sock, (sockaddr*)&address, sizeof(sockaddr_in)) < 0)
	{
		DDrStartupMessage("could not bind port %d", port);
		return false;
	}
	
	unsigned long nonBlocking = 1;
	if (ioctlsocket(sock, FIONBIO, &nonBlocking))
	{
		DDrStartupMessage("failed to set non-blocking socket");
		return false;
	}
	
	return sock;
}

void NetUDPSocket_Close(int socket)
{
	closesocket(socket);
}

bool NetUDPSocket_Send(int socket, const sockaddr* address, const char* data, int datalen)
{
	int addr_size;
	switch (address->sa_family)
	{
		case AF_INET:
			addr_size = sizeof(sockaddr_in);
			break;
		case AF_INET6:
			addr_size = sizeof(sockaddr_in6);
			break;
		default:
			addr_size = sizeof(sockaddr_storage);
	}
	return sendto(socket, data, datalen, 0, address, addr_size);
}

bool NetUDPSocket_Recieve(int socket, sockaddr_storage* address, char* data, uint16_t* datalen)
{
	int address_size = sizeof(sockaddr_storage);
	uint32_t msg_size = recvfrom(socket, data, 512, 0, (sockaddr*)address, &address_size);
	if (msg_size == 0)
		return false;
	else
	{
		*datalen = msg_size;
		return true;
	}
}
