/* [Petteri] Check if compiling for Win32:	*/
#if defined _MSC_VER
#	define __WIN32__
#endif
/* Follow #ifdef __WIN32__ marks */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/* [Petteri] Use Winsock for Win32: */
#ifdef __WIN32__
#	define WIN32_LEAN_AND_MEAN
#	include <windows.h>
#	include <winsock2.h>
#else
#	include <sys/types.h>
#	include <sys/socket.h>
#	include <netinet/in.h>
#	include <arpa/inet.h>
#	include <errno.h>
#	include <unistd.h>
#	include <netdb.h>
#	include <sys/ioctl.h>
#       include <sys/time.h>
#endif

#ifndef __WIN32__
typedef int SOCKET;
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#define closesocket close
#define ioctlsocket ioctl
#define Sleep(x)	usleep (x * 1000)
#else
typedef int socklen_t;
#endif

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "i_net.h"


int						net_socket;
unsigned short int		localport;

netadr_t    net_from;   // address of who sent the packet
byte	    net_message_buffer[MAX_UDP_PACKET];

buf_t       net_message;
int         msg_readcount;
int         msg_badread;


//
// UDPsocket
//
SOCKET UDPsocket (void)
{
	SOCKET s;
		
	// allocate a socket
	s = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (s == INVALID_SOCKET)
     	printf ("can't create socket\n");

	return s;
}

//
// BindToLocalPort
//
bool BindToLocalPort (SOCKET s, u_short &port)
{
	int v;
	struct sockaddr_in address;
		
	memset (&address, 0, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(port);
						
	v = bind (s, (sockaddr *)&address, sizeof(address));
    while (v == SOCKET_ERROR) // changed - find new port if taken
	{
		address.sin_port = htons(port++);
		v = bind (s, (sockaddr *)&address, sizeof(address));
		if(port + 1 >= 65535)
		{
			printf ("BindToPort: error\n");
			return 0;
		}
	}
			
	if (v == SOCKET_ERROR)
	{
    	   printf ("BindToPort: error\n");
		   return 0;
	}
	return 1;
}


void CloseNetwork (void)
{
	closesocket (net_socket);
#ifdef __WIN32__
	WSACleanup ();
#endif	
}


// this is from Quake source code :)

void SockadrToNetadr (struct sockaddr_in *s, netadr_t *a)
{
     *(int *)&a->ip = *(int *)&s->sin_addr;
     a->port = s->sin_port;
}

void NetadrToSockadr (netadr_t *a, struct sockaddr_in *s)
{
     memset (s, 0, sizeof(*s));
     s->sin_family = AF_INET;

     *(int *)&s->sin_addr = *(int *)&a->ip;
     s->sin_port = a->port;
}

char *NET_AdrToString (netadr_t a)
{
     static  char    s[64];

     sprintf (s, "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs(a.port));

     return s;
}

bool NET_StringToAdr (char *s, netadr_t *a)
{
     struct hostent  *h;
     struct sockaddr_in sadr;
     char    *colon;
     char    copy[128];


     memset (&sadr, 0, sizeof(sadr));
     sadr.sin_family = AF_INET;

     sadr.sin_port = 0;

     strcpy (copy, s);
     // strip off a trailing :port if present
     for (colon = copy ; *colon ; colon++)
          if (*colon == ':')
          {
             *colon = 0;
             sadr.sin_port = htons(atoi(colon+1));
          }

     if (copy[0] >= '0' && copy[0] <= '9')
     {
          *(int *)&sadr.sin_addr = inet_addr(copy);
     }
     else
     {
          if (! (h = gethostbyname(copy)) )
                return 0;
          *(int *)&sadr.sin_addr = *(int *)h->h_addr_list[0];
     }

     SockadrToNetadr (&sadr, a);

     return true;
}

bool NET_CompareAdr (netadr_t a, netadr_t b)
{
    if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port)
        return true;
    
	return false;
}



int NET_GetPacket (void)
{
    int                  ret;
    struct sockaddr_in   from;
    socklen_t            fromlen;

    fromlen = sizeof(from);
    ret = recvfrom (net_socket, (char *)net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr *)&from, &fromlen);
    
    if (ret == -1) 
    { 
#ifdef __WIN32__
        errno = WSAGetLastError();

        if (errno == WSAEWOULDBLOCK)
            return false;

        if (errno == WSAEMSGSIZE) 
		{
             printf ("Warning:  Oversize packet from %s\n",
                      NET_AdrToString (net_from));
             return false;
        }

        printf ("NET_GetPacket: %s", strerror(errno));
		return false;
#else
        if (errno == EWOULDBLOCK)
            return false;
        if (errno == ECONNREFUSED)
            return false;
	    
        printf ("NET_GetPacket: %s\n", strerror(errno));
        return false;
#endif
    }
    net_message.cursize = ret;
    SockadrToNetadr (&from, &net_from);
	
    msg_readcount = 0;
    msg_badread = false;

    return ret;
}


void NET_SendPacket (int length, byte *data, netadr_t to)
{
    int                   ret;
    struct sockaddr_in    addr;

    NetadrToSockadr (&to, &addr);
    
    ret = sendto (net_socket, (const char*)data, length, 0, (struct sockaddr *)&addr, sizeof(addr) );
    
    if (ret == -1) 
    {
#ifdef __WIN32__
          int err = WSAGetLastError();

          // wouldblock is silent
          if (err == WSAEWOULDBLOCK)
              return;
#else	  
          if (errno == EWOULDBLOCK)
              return;
          if (errno == ECONNREFUSED)
              return;
          printf ("NET_SendPacket: %s\n", strerror(errno));
#endif	  
    }
}


void SZ_Clear (buf_t *buf)
{
    buf->cursize = 0;
    buf->overflowed = false;
}


byte *SZ_GetSpace(buf_t *b, int length)
{
	byte *data;
	
	if (b->cursize + length >= b->maxsize)
	{
       SZ_Clear (b);
	   b->overflowed = true;
       printf ("SZ_GetSpace: overflow\n");
	}

	data = b->data + b->cursize;
	b->cursize += length;
	
	return data;
}

void SZ_Write (buf_t *b, void *data, int length)
{
    memcpy (SZ_GetSpace(b, length), data, length);
}


void MSG_WriteByte (buf_t *b, int c)
{
    byte    *buf;

    buf = SZ_GetSpace (b, 1);
    buf[0] = c;
}

void MSG_WriteShort (buf_t *b, int c)
{
    byte    *buf;

    buf = SZ_GetSpace (b, 2);
    buf[0] = c&0xff;
    buf[1] = c>>8;
}


void MSG_WriteLong (buf_t *b, int c)
{
    byte    *buf;

    buf = SZ_GetSpace(b, 4);
    buf[0] = c&0xff;
    buf[1] = (c>>8)&0xff;
    buf[2] = (c>>16)&0xff;
    buf[3] = c>>24;
}


void MSG_WriteString (buf_t *b, char *s)
{
    if (!s)
        MSG_WriteByte(b, 0);
    else
	{
        SZ_Write (b, s, strlen(s));
        MSG_WriteByte(b, 0);
	}
}



int MSG_ReadByte (void)
{
    int     c;

    if (msg_readcount+1 > net_message.cursize)
    {
        msg_badread = true;
        return -1;
    }

    c = (unsigned char)net_message.data[msg_readcount];
    msg_readcount++;

    return c;
}

int MSG_ReadShort (void)
{
    int     c;

    if (msg_readcount+2 > net_message.cursize)
    {
        msg_badread = true;
        return -1;
    }

    c = (short)(net_message.data[msg_readcount]
    + (net_message.data[msg_readcount+1]<<8));

    msg_readcount += 2;

    return c;
}


int MSG_ReadLong (void)
{
    int c; 

	if (msg_readcount+4 > net_message.cursize)
    {
        msg_badread = true;
        return -1;
    }

    c = net_message.data[msg_readcount]
     + (net_message.data[msg_readcount+1]<<8)
     + (net_message.data[msg_readcount+2]<<16)
     + (net_message.data[msg_readcount+3]<<24);

    msg_readcount += 4;

    return c;
}

char *MSG_ReadString (void)
{
    static char     string[2048];
    signed char     c;
    unsigned int    l;

    l = 0;
    do
    {
       c = (signed char)MSG_ReadByte();
       if (c == 0)
           break;
       string[l] = c;
       l++;
	} while (l < sizeof(string)-1);

    string[l] = 0;

    return string;
}


//
// InitNetCommon
//
bool InitNetCommon(void)
{
   unsigned long _true = true;	

#ifdef __WIN32__
   WSADATA   wsad;
   WSAStartup( 0x0101, &wsad );
#endif

   net_socket = UDPsocket ();
   if(!BindToLocalPort (net_socket, localport))
   {
	   CloseNetwork();
	   return false;
   }
   if (ioctlsocket (net_socket, FIONBIO, &_true) == -1)
       printf ("UDPsocket: ioctl FIONBIO:", strerror(errno));

   net_message.data = net_message_buffer;
   net_message.maxsize = sizeof(net_message_buffer);
   SZ_Clear(&net_message);
}


void I_SetPort(netadr_t &addr, int port)
{
   addr.port = htons(port);
}

#ifdef __WIN32__

void I_DoSelect (void)
{
    struct timeval   timeout;
    fd_set           fdset;

    FD_ZERO(&fdset);
    FD_SET(net_socket, &fdset);
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    if (select (net_socket+1, &fdset, NULL, NULL, &timeout) == -1)
        return;
}

#else

int stdin_ready;

void I_DoSelect (void)
{
    struct timeval   timeout;
    fd_set           fdset;

    FD_ZERO(&fdset);
    FD_SET(0, &fdset);
    FD_SET(net_socket, &fdset);
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    if (select (net_socket+1, &fdset, NULL, NULL, &timeout) == -1)
        return;
    
    stdin_ready = FD_ISSET(0, &fdset);
}

#endif
