/*
 * sdl_net.c - Q3Base SDL network glue
 *
 * Copyright (C) 2005  Ed Schouten <ed@fxq.nl>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "SDL_net.h"

#include "../game/q_shared.h"
#include "../qcommon/qcommon.h"

static UDPsocket sock;

/*
 * NET_Init: Initialise the network layer
 */
void NET_Init ()
{
	int port, i;
	
	/* Don't start when UDP is disabled */
	if (Cvar_Get ("net_noudp", "0", 0)->value) return;

	/* Initialise SDL */
	SDLNet_Init ();

	port = Cvar_Get ("net_port", va ("%i", PORT_SERVER), 0)->value;

	/* Try to find a free port */
	for (i = 0; (i < 10) && !sock; i++)
		sock = SDLNet_UDP_Open (port + i);

	if (sock)
		/* Store the port number */
		Cvar_SetValue ("net_port", port + i - 1);
	else
		/* No free ports left */
		Com_Error (ERR_FATAL, "No free sockets available");
}

/*
 * NET_Shutdown: Shutdown the network layer
 */
void NET_Shutdown ()
{
	/* Close our socket, if any */
	if (sock) SDLNet_UDP_Close (sock);

	/* Shutdown SDL */
	SDLNet_Quit ();
}

/*
 * NET_Restart: Restart the network layer
 */
void NET_Restart ()
{
	NET_Shutdown ();
	NET_Init ();
}

/*
 * Sys_SendPacket: Send a network packet
 */
void Sys_SendPacket (int length, const void *data, netadr_t to)
{
	UDPpacket *packet;

	if (!sock) return;

	/* We can only handle IP packets */
	if ((to.type != NA_IP) && (to.type != NA_BROADCAST))
		return;

	packet = SDLNet_AllocPacket (length);

	/* Set our target */
	packet->address.host = *(Uint32*)&to.ip;
	packet->address.port = to.port;

	/* Copy the data to the packet */
	memcpy (packet->data, data, length);
	packet->len = length;

	/* And go! */
	SDLNet_UDP_Send (sock, -1, packet);
	SDLNet_FreePacket (packet);
}

/*
 * Sys_GetPacket: Receive a network packet
 */
qboolean Sys_GetPacket (netadr_t *net_from, msg_t *net_message)
{
	int ret;
	UDPpacket *packet;

	if (!sock) return qfalse;

	packet = SDLNet_AllocPacket (net_message->maxsize);

	ret = SDLNet_UDP_Recv (sock, packet);

	if (ret != 1)
	{
		/* Receive error or no packet available */
		SDLNet_FreePacket (packet);
		return qfalse;
	}

	/* Copy address from the packet */
	*(Uint32*)&net_from->ip = packet->address.host;
	net_from->port = packet->address.port;
	net_from->type = NA_IP;

	/* Copy data from the packet */
	memcpy (net_message->data, packet->data, packet->len);
	net_message->cursize = packet->len;
	net_message->readcount = 0;

	SDLNet_FreePacket (packet);
	return qtrue;
}

/*
 * Sys_IsLANAddress: Check whether the address is in our current network
 */
qboolean Sys_IsLANAddress (netadr_t adr)
{
	/* Loopback and IPX must imply LAN traffic */
	if ((adr.type == NA_LOOPBACK) || (adr.type == NA_IPX))
		return qtrue;
	
	/* Other protocols probably not */
	if (adr.type != NA_IP)
		return qfalse;
	
	/* Class A - 10.0.0.0/8 */
	if (adr.ip[0] == 0x0a)
		return qtrue;

	/* Class B 172.16.0.0/12 */
	if ((adr.ip[0] == 0xac) && ((adr.ip[1] & 0xf0) == 0x10))
		return qtrue;

	/* Class C 192.168.0.0/16 */
	if ((adr.ip[0] == 0xc0) && (adr.ip[1] == 0xa8))
		return qtrue;

	return qfalse;
}

/*
 * Sys_StringToAdr: Resolve an IP address using DNS
 */
qboolean Sys_StringToAdr (const char *s, netadr_t *a)
{
	IPaddress addr;

	/* Try to resolve it */
	if (SDLNet_ResolveHost (&addr, s, a->port) != 0)
		return qfalse;

	/* Copy the address back to the structure */
	*(Uint32*)&a->ip = addr.host;
	a->type = NA_IP;
	
	return qtrue;
}

/*
 * NET_Sleep: Sleep for a specified period of time or until the socket
 *            is ready - Not implemented
 */
void NET_Sleep (int msec)
{
}

/*
 * Sys_ShowIP: Print the IP address that is used - Not implemented
 */
void Sys_ShowIP ()
{
}
