// connection.cpp: Beispielspieler für Asteroids (Verbindung zu Mame)
// Harald Bögeholz / c't; Umstrukturierung Matthias Fuchs

#include <errno.h>
#include <stdio.h>
//#include <stdlib.h>

#if defined(WINDOWS) || defined(_WIN32)
#else
// 2 Includes für socket()
#include <sys/types.h>
#include <sys/socket.h>
// 2 Includes für inet_addr()
#include <netinet/in.h>
#include <arpa/inet.h>
// 2 Includes für fcntl()
#include <unistd.h>
#include <fcntl.h>
// für memset()
#define INVALID_SOCKET -1
#define WSAEWOULDBLOCK EAGAIN
#define WSAGetLastError() errno
#endif

#include "connection.h"

FramePacket::FramePacket(void)
{
	frameno = 0;
	ping = 0;
}


KeysPacket::KeysPacket(void)
{
	signature[0] = 'c';
	signature[1] = 't';
	signature[2] = 'm';
	signature[3] = 'a';
	signature[4] = 'm';
	signature[5] = 'e';
	keys = '@';
	ping = 0;
}

void KeysPacket::clear(void)
{
	keys = '@';
}

void KeysPacket::start(bool b)
{
	if (b)
		keys |= KEY_START;
	else
		keys &= ~KEY_START;
}

void KeysPacket::hyperspace(bool b)
{
	if (b)
		keys |= KEY_HYPERSPACE;
	else
		keys &= ~KEY_HYPERSPACE;
}

void KeysPacket::fire(bool b)
{
	if (b)
		keys |= KEY_FIRE;
	else
		keys &= ~KEY_FIRE;
}

void KeysPacket::thrust(bool b)
{
	if (b)
		keys |= KEY_THRUST;
	else
		keys &= ~KEY_THRUST;
}

void KeysPacket::left(bool b)
{
	if (b)
	{
		keys |= KEY_LEFT;
		keys &= ~KEY_RIGHT;
	}
	else
		keys &= ~KEY_LEFT;
}

void KeysPacket::right(bool b)
{
	if (b)
	{
		keys |= KEY_RIGHT;
		keys &= ~KEY_LEFT;
	}
	else
		keys &= ~KEY_RIGHT;
}

bool KeysPacket::start(void) const
{
	return (keys & KEY_START) != 0;
}

bool KeysPacket::hyperspace(void) const
{
	return (keys & KEY_HYPERSPACE) != 0;
}

bool KeysPacket::fire(void) const
{
	return (keys & KEY_FIRE) != 0;
}

bool KeysPacket::thrust(void) const
{
	return (keys & KEY_THRUST) != 0;
}

bool KeysPacket::right(void) const
{
	return (keys & KEY_RIGHT) != 0;
}

bool KeysPacket::left(void) const
{
	return (keys & KEY_LEFT) != 0;
}


connection_error::connection_error (const char* format, ...)
{
	
	va_list arglist;
	va_start(arglist, format);

	vsprintf(message, format, arglist);

	va_end(arglist);
}


Connection::Connection (const char* server_ip_str)
:  sd (INVALID_SOCKET),
	server_ip (INADDR_NONE)
{
#if defined(WINDOWS) || defined(_WIN32)
	WSADATA wsadata;
	wsadata.wVersion = 0;		// Merker für ungültig
#endif

	try
	{
		server_ip = inet_addr(server_ip_str);
		if (server_ip == INADDR_NONE)
			throw connection_error("Ungueltige IP-Adresse: '%s'\n", server_ip_str);

#if defined(WINDOWS) || defined(_WIN32)
		if (WSAStartup(MAKEWORD(2,2), &wsadata))
			throw connection_error("Fehler beim Initialisieren von Winsock.\n");
#endif

		sd = socket(AF_INET, SOCK_DGRAM, 0);
		if (sd == INVALID_SOCKET)
			throw connection_error("Fehler %d bei socket().\n", WSAGetLastError());

#if defined(WINDOWS) || defined(_WIN32)
		unsigned long enable_nonblocking = 1;
		if (ioctlsocket(sd, FIONBIO, &enable_nonblocking))
#else
		if (-1 == fcntl(sd, F_SETFL, O_NONBLOCK))
#endif
			throw connection_error("Kann Socket nicht auf nonblocking setzen (%d)", WSAGetLastError());
		sockaddr_in sa;
		memset(&sa, 0, sizeof sa);
		sa.sin_family = AF_INET;
		sa.sin_addr.s_addr = 0;
		sa.sin_port = 0;

		if (bind(sd, (struct sockaddr*) &sa, sizeof sa))
			throw connection_error("Fehler %d bei bind().\n", WSAGetLastError());
	}
	catch (connection_error)
	{
		if (sd != INVALID_SOCKET)
			closesocket (sd);
#if defined(WINDOWS) || defined(_WIN32)
		if (wsadata.wVersion != 0)
			WSACleanup();
#endif

		throw;
	}
	catch (...)
	{
		throw;
	}
}


Connection::~Connection()
{
	if (sd != INVALID_SOCKET)
		closesocket (sd);
#if defined(WINDOWS) || defined(_WIN32)
	WSACleanup();
#endif
}


bool Connection::IsLittleEndian (void)
{
	static __int16 littleEndian = 1;

	return *reinterpret_cast<__int8*>(&littleEndian) != 0;
}


void Connection::ReceivePacket(FramePacket &packet)
{
	fd_set readfds, writefds, exceptfds;

	do
	{
		FD_ZERO(&readfds);
		FD_ZERO(&writefds);
		FD_ZERO(&exceptfds);
		FD_SET(sd, &readfds);
		FD_SET(sd, &exceptfds);
		select(sd+1, &readfds, &writefds, &exceptfds, NULL);
		unsigned int bytes_received = recv(sd, (char *)&packet, sizeof packet, 0);
		if (bytes_received != sizeof packet)
			throw connection_error("Fehler %d bei recvfrom().\n", WSAGetLastError());
		FD_ZERO(&readfds);
		FD_ZERO(&writefds);
		FD_ZERO(&exceptfds);
		FD_SET(sd, &readfds);
		timeval zero;
		zero.tv_sec = zero.tv_usec = 0;
		select(sd+1, &readfds, &writefds, &exceptfds, &zero);
	} while(FD_ISSET(sd, &readfds));
}


void Connection::SendPacket(const KeysPacket &packet)
{
	sockaddr_in server;
	memset(&server, 0, sizeof server);
	server.sin_family = AF_INET;
	server.sin_port = htons(1979);
	server.sin_addr.s_addr = server_ip;
	unsigned int bytes_sent = sendto(sd, (char *)&packet, sizeof packet, 0,
		(sockaddr*)&server, sizeof server);
	if (bytes_sent != sizeof packet)
	{
		int err = WSAGetLastError();
		if (err != WSAEWOULDBLOCK)
			throw connection_error("Fehler %d bei sendto().\n", err);
	}
}
