///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#include "gsPlatformSocket.h"
#include "gsPlatformUtil.h"
#include "gsMemory.h"

// mj-ToDo: remove these and include the files int the linker instead. 
// removing reference to other platforms.
// remove all plat specific code from here, move it to the platspecific files.

// Include platform separated functions
#if defined(_X360)
	#include "x360/gsSocketX360.c"
#elif defined(_XBOX)
	#include "xbox/gsSocketXBox.c"
#elif defined(_WIN32)
	#include "win32/gsSocketWin32.c"
#elif defined(_LINUX)
	//#include "linux/gsSocketLinux.c"
#elif defined(_MACOSX)
	//#include "macosx/gsSocketMacOSX.c"
#elif defined(_NITRO)
	#include "nitro/gsSocketNitro.c"
#elif defined(_PS2)
	#include "ps2/gsSocketPs2.c"
#elif defined(_PS3)
	#include "ps3/gsSocketPs3.c"
	#include <sys/select.h>
#elif defined(_PSP)
	#include "psp/gsSocketPSP.c"
#elif defined(_REVOLUTION)
	#include "revolution/gsSocketRevolution.c"
#else
	#error "Missing or unsupported platform"
#endif


// Disable compiler warnings for issues that are unavoidable.
/////////////////////////////////////////////////////////////
#if defined(_MSC_VER) // DevStudio
	// Level4, "conditional expression is constant". 
	// Occurs with use of the MS provided macro FD_SET
	#pragma warning ( disable: 4127 )
#endif // _MSC_VER



///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int SetSockBlocking(SOCKET sock, int isblocking)
{
	int rcode;

#if defined(_REVOLUTION)
	int val;
	
	val = SOFcntl(sock, SO_F_GETFL, 0);
	
	if(isblocking)
		val &= ~SO_O_NONBLOCK;
	else
		val |= SO_O_NONBLOCK;
	
	rcode = SOFcntl(sock, SO_F_SETFL, val);
#elif defined(_NITRO)
	int val;
	
	val = SOC_Fcntl(sock, SOC_F_GETFL, 0);
	
	if(isblocking)
		val &= ~SOC_O_NONBLOCK;
	else
		val |= SOC_O_NONBLOCK;
	
	rcode = SOC_Fcntl(sock, SOC_F_SETFL, val);
#else
	#if defined(_PS2) || defined(_PS3)
		// EENet requires int
		// SNSystems requires int
		// Insock requires int
		// PS3 requires int
		gsi_i32 argp;
	#else
		unsigned long argp;
	#endif
		
		if(isblocking)
			argp = 0;
		else
			argp = 1;

	#ifdef _PS2
		#ifdef SN_SYSTEMS
			rcode = setsockopt(sock, SOL_SOCKET, (isblocking) ? SO_BIO : SO_NBIO, &argp, sizeof(argp));
		#endif

		#ifdef EENET
			rcode = setsockopt(sock, SOL_SOCKET, SO_NBIO, &argp, sizeof(argp));
		#endif

		#ifdef INSOCK
			if (isblocking)
				argp = -1;
			else
				argp = 5; //added longer timeout to 5ms
			sceInsockSetRecvTimeout(sock, argp);
			sceInsockSetSendTimeout(sock, argp);
			sceInsockSetShutdownTimeout(sock, argp);
			GSI_UNUSED(sock);
			rcode = 0;
		#endif
	#elif defined(_PSP)
		rcode = setsockopt(sock, SCE_NET_INET_SOL_SOCKET, SCE_NET_INET_SO_NBIO, &argp, sizeof(argp));
	#elif defined(_PS3)
		rcode = setsockopt(sock, SOL_SOCKET, SO_NBIO, &argp, sizeof(argp));
	#else
		rcode = ioctlsocket(sock, FIONBIO, &argp);
	#endif
#endif

	if(rcode == 0)
	{
		gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment,
			"SetSockBlocking: Set socket %d to %s\r\n", (unsigned int)sock, isblocking ? "blocking":"non-blocking");
		return 1;
	}

	gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment,
			"SetSockBlocking failed: tried to set socket %d to %s\r\n", (unsigned int)sock, isblocking ? "blocking":"non-blocking");
	return 0;
}

int SetSockBroadcast(SOCKET sock)
{
#if !defined(INSOCK) && !defined(_NITRO) && !defined(_REVOLUTION)
	int optval = 1;
	if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval)) != 0)
		return 0;
#else
	GSI_UNUSED(sock);
#endif

	return 1;
}

int DisableNagle(SOCKET sock)
{
#if defined(_WIN32) || defined(_UNIX)
	int rcode;
	int noDelay = 1;

	rcode = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&noDelay, sizeof(int));
	return gsiSocketIsError(rcode);	
#else
	GSI_UNUSED(sock);

	// not supported
	return 0;
#endif // moved this to here to silence VC warning
}


#ifndef INSOCK
	int SetReceiveBufferSize(SOCKET sock, int size)
	{
		int rcode;
		rcode = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char *)&size, sizeof(int));
		return gsiSocketIsNotError(rcode);
	}

	int SetSendBufferSize(SOCKET sock, int size)
	{
		int rcode;
		rcode = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char *)&size, sizeof(int));
		return gsiSocketIsNotError(rcode);
	}

	int GetReceiveBufferSize(SOCKET sock)
	{
		int rcode;
		int size;
		int len;

		len = sizeof(size);

		rcode = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&size, &len);

		if(gsiSocketIsError(rcode))
			return -1;

		return size;
	}

	int GetSendBufferSize(SOCKET sock)
	{
		int rcode;
		int size;
		int len;

		len = sizeof(size);

		rcode = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&size, &len);

		if(gsiSocketIsError(rcode))
			return -1;

		return size;
	}
	
	// Formerly known as ghiSocketSelect
#ifdef SN_SYSTEMS
	#undef FD_SET
	#define FD_SET(s,p)   ((p)->array[((s) - 1) >> SN_FD_SHR] |= \
                       (unsigned int)(1 << (((s) - 1) & SN_FD_BITS)) )

#endif
#endif

#if !defined(_NITRO) && !defined(INSOCK) && !defined(_REVOLUTION)
	int GSISocketSelect(SOCKET theSocket, int* theReadFlag, int* theWriteFlag, int* theExceptFlag)
	{
		fd_set aReadSet;
		fd_set aWriteSet;
		fd_set aExceptSet;
		fd_set * aReadFds   = NULL;
		fd_set * aWriteFds  = NULL;
		fd_set * aExceptFds = NULL;
		int aResult;
// 04-13-2005, Saad Nader
// Added case for SN Systems that would 
// handle errors after performing selects.
#ifdef SN_SYSTEMS
		int aOut, aOutLen = sizeof(aOut);
#endif
		
		struct timeval aTimeout = { 0, 0 };

		if (theSocket == INVALID_SOCKET)
			return -1;

		// Setup the parameters.
		////////////////////////
		if(theReadFlag != NULL)
		{
			FD_ZERO(&aReadSet);
			FD_SET(theSocket,&aReadSet);
			aReadFds = &aReadSet;
		}
		if(theWriteFlag != NULL)
		{
			FD_ZERO(&aWriteSet);
			FD_SET(theSocket, &aWriteSet);
			aWriteFds = &aWriteSet;
		}
		if(theExceptFlag != NULL)
		{
			FD_ZERO(&aExceptSet);
			FD_SET(theSocket, &aExceptSet);
			aExceptFds = &aExceptSet;
		}
#ifdef _PS3
		// to do, port what is below in the else
//int socketselect(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
	aResult = socketselect(FD_SETSIZE, aReadFds, aWriteFds, aExceptFds, &aTimeout);
#else
	// Perform the select
	aResult = select(FD_SETSIZE, aReadFds, aWriteFds, aExceptFds, &aTimeout);
#endif
	if(gsiSocketIsError(aResult))
		return -1;

// 04-13-2005, Saad Nader
// Added case for SN Systems that would 
// handle errors after performing selects.
#ifdef SN_SYSTEMS
		getsockopt(theSocket, SOL_SOCKET, SO_ERROR, (char *)&aOut, &aOutLen);
		if (aOut != 0)
		{
			return 0;
		}		
#endif
		// Check results.
		/////////////////
		if(theReadFlag != NULL)
		{
			if((aResult > 0) && FD_ISSET(theSocket, aReadFds))
				*theReadFlag = 1;
			else
				*theReadFlag = 0;
		}
		if(theWriteFlag != NULL)
		{
			if((aResult > 0) && FD_ISSET(theSocket, aWriteFds))
				*theWriteFlag = 1;
			else
				*theWriteFlag = 0;
		}
		if(theExceptFlag != NULL)
		{
			if((aResult > 0) && FD_ISSET(theSocket, aExceptFds))
				*theExceptFlag = 1;
			else
				*theExceptFlag = 0;
		}
		return aResult; // 0 or 1 at this point
	}
#endif // !nitro && !revolution && !insock
	

// Return 1 for immediate recv, otherwise 0
int CanReceiveOnSocket(SOCKET sock)
{
	int aReadFlag = 0;
	if (1 == GSISocketSelect(sock, &aReadFlag, NULL, NULL))
		return aReadFlag;

	// SDKs expect 0 on SOCKET_ERROR
	return 0;
}

// Return 1 for immediate send, otherwise 0
int CanSendOnSocket(SOCKET sock)
{
	int aWriteFlag = 0;
	if (1 == GSISocketSelect(sock, NULL, &aWriteFlag, NULL))
		return aWriteFlag;

	// SDKs expect 0 on SOCKET_ERROR
	return 0;
}


#if defined(_PS3) || defined (_PSP)

#else

HOSTENT * getlocalhost(void)
{
#ifdef EENET
	#define MAX_IPS  5

	static HOSTENT localhost;
	static char * aliases = NULL;
	static char * ipPtrs[MAX_IPS + 1];
	static unsigned int ips[MAX_IPS];

	struct sceEENetIfname * interfaces;
	struct sceEENetIfname * interface;
	int num;
	int i;
	int count;
	int len;
	u_short flags;
	IN_ADDR address;

	// initialize the host
	localhost.h_name = "localhost";
	localhost.h_aliases = &aliases;
	localhost.h_addrtype = AF_INET;
	localhost.h_length = 0;
	localhost.h_addr_list = ipPtrs;

	// get the local interfaces
	sceEENetGetIfnames(NULL, &num);
	interfaces = (struct sceEENetIfname *)gsimalloc(num * sizeof(struct sceEENetIfname));
	if(!interfaces)
		return NULL;
	sceEENetGetIfnames(interfaces, &num);

	// loop through the interfaces
	count = 0;
	for(i = 0 ; i < num ; i++)
	{
		// the next interface
		interface = &interfaces[i];
		//printf("eenet%d: %s\n", i, interface->ifn_name);

		// get the flags
		len = sizeof(flags);
		if(sceEENetGetIfinfo(interface->ifn_name, sceEENET_IFINFO_IFFLAGS, &flags, &len) != 0)
			continue;
		//printf("eenet%d flags: 0x%X\n", i, flags);

		// check for up, running, and non-loopback
		if(!(flags & (IFF_UP|IFF_RUNNING)) || (flags & IFF_LOOPBACK))
			continue;
		//printf("eenet%d: up and running, non-loopback\n", i);

		// get the address
		len = sizeof(address);
		if(sceEENetGetIfinfo(interface->ifn_name, sceEENET_IFINFO_ADDR, &address, &len) != 0)
			continue;
		//printf("eenet%d: %s\n", i, inet_ntoa(address));

		// add this address
		ips[count] = address.s_addr;
		ipPtrs[count] = (char *)&ips[count];
		count++;
	}

	// free the interfaces
	gsifree(interfaces);

	// check that we got at least one IP
	if(!count)
		return NULL;

	// finish filling in the host struct
	localhost.h_length = (gsi_u16)sizeof(ips[0]);
	ipPtrs[count] = NULL;

	return &localhost;

	////////////////////
	// INSOCK
#elif defined(INSOCK)
	// Global storage
	#define MAX_IPS  sceLIBNET_MAX_INTERFACE
	static HOSTENT   localhost;
	static char    * aliases = NULL;
	static char    * ipPtrs[MAX_IPS + 1];
	static unsigned int ips[MAX_IPS];

	// Temp storage
	int aInterfaceIdArray[MAX_IPS];
	int aNumInterfaces = 0;
	int aInterfaceNum = 0;
	int aCount = 0;
	
	// Get the list of interfaces
	aNumInterfaces = sceInetGetInterfaceList(&gGSIInsockClientData, 
		                 &gGSIInsockSocketBuffer, aInterfaceIdArray, MAX_IPS);
	if (aNumInterfaces < 1)
		return NULL;

	// initialize the HOSTENT
	localhost.h_name      = "localhost";
	localhost.h_aliases   = &aliases;
	localhost.h_addrtype  = AF_INET;
	localhost.h_addr_list = ipPtrs;

	// Look up each address and copy into the HOSTENT structure
	aCount = 0; // count of valid interfaces
	for (aInterfaceNum = 0; aInterfaceNum < aNumInterfaces; aInterfaceNum++)
	{
		sceInetAddress_t anAddr;
		int result = sceInetInterfaceControl(&gGSIInsockClientData, &gGSIInsockSocketBuffer,
			                    aInterfaceIdArray[aInterfaceNum], sceInetCC_GetAddress,
								&anAddr, sizeof(anAddr));
		if (result == 0)
		{
			// Add this interface to the array
			memcpy(&ips[aCount], anAddr.data, sizeof(ips[aCount]));
			ips[aCount] = htonl(ips[aCount]);
			ipPtrs[aCount] = (char*)&ips[aCount];
			aCount++;
		}
	}

	// Set the final hostent data, then return
	localhost.h_length = (gsi_u16)sizeof(ips[0]);
	ipPtrs[aCount]     = NULL;
	return &localhost;
	
#elif defined(_NITRO)
	#define MAX_IPS  5

	static HOSTENT localhost;
	static char * aliases = NULL;
	static char * ipPtrs[MAX_IPS + 1];
	static unsigned int ips[MAX_IPS];

	int count = 0;

	localhost.h_name = "localhost";
	localhost.h_aliases = &aliases;
	localhost.h_addrtype = AF_INET;
	localhost.h_length = 0;
	localhost.h_addr_list = (u8 **)ipPtrs;

	ips[count] = 0;
	IP_GetAddr(NULL, (u8*)&ips[count]);
	if(ips[count] == 0)
		return NULL;
	ipPtrs[count] = (char *)&ips[count];
	count++;

	localhost.h_length = (gsi_u16)sizeof(ips[0]);
	ipPtrs[count] = NULL;

	return &localhost;

#elif defined(_REVOLUTION)
	#define MAX_IPS  5
	static HOSTENT aLocalHost;
	static char * aliases = NULL;
	int aNumOfIps, i;
	int aSizeNumOfIps;
	static IPAddrEntry aAddrs[MAX_IPS];
	int aAddrsSize, aAddrsSizeInitial;
	static u8 * ipPtrs[MAX_IPS + 1];
	static unsigned int ips[MAX_IPS];
	int ret;
	aSizeNumOfIps = sizeof(aNumOfIps);
	ret = SOGetInterfaceOpt(NULL, SO_SOL_CONFIG, SO_CONFIG_IP_ADDR_NUMBER, &aNumOfIps, &aSizeNumOfIps);
	if (ret != 0)
		return NULL;
	
	aAddrsSize = (int)(MAX_IPS * sizeof(IPAddrEntry));
	aAddrsSizeInitial = aAddrsSize;
	ret = SOGetInterfaceOpt(NULL, SO_SOL_CONFIG, SO_CONFIG_IP_ADDR_TABLE, &aAddrs, &aAddrsSize);
	if (ret != 0)
		return NULL;
	
	if (aAddrsSize != aAddrsSizeInitial)
	{
		aNumOfIps = aAddrsSize / (int)sizeof(IPAddrEntry);
	}
	
	aLocalHost.h_name = "localhost";
	aLocalHost.h_aliases = &aliases;
	aLocalHost.h_addrtype = AF_INET;
	aLocalHost.h_length = SO_IP4_ALEN;

	for (i = 0; i < MAX_IPS; i++)
	{
		if (i < aNumOfIps)
		{
			memcpy(&ips[i], &aAddrs[i].addr, sizeof(aAddrs[i].addr));
			ipPtrs[i] = (u8 *)&ips[i];
		}			
		else 
			ipPtrs[i] = NULL;
	}
	aLocalHost.h_addr_list = ipPtrs;
	
	return &aLocalHost;

#elif defined(_X360)
	XNADDR addr;
	DWORD rcode;
	static HOSTENT localhost;
	static char * ipPtrs[2];
	static IN_ADDR ip;

	while((rcode = XNetGetTitleXnAddr(&addr)) == XNET_GET_XNADDR_PENDING)
		msleep(1);

	if((rcode == XNET_GET_XNADDR_NONE) || (rcode == XNET_GET_XNADDR_TROUBLESHOOT))
		return NULL;

	localhost.h_name = "localhost";
	localhost.h_aliases = NULL;
	localhost.h_addrtype = AF_INET;
	localhost.h_length = (gsi_u16)sizeof(IN_ADDR);
	localhost.h_addr_list = (gsi_i8 **)ipPtrs;

	ip = addr.ina;
	ipPtrs[0] = (char *)&ip;
	ipPtrs[1] = NULL;

	return &localhost;

#elif defined(_XBOX)
	return NULL;


#else
	char hostname[256] = "";

	// get the local host's name
	gethostname(hostname, sizeof(hostname));

	// return the host for that name
	return gethostbyname(hostname);
#endif
}
#endif

int IsPrivateIP(IN_ADDR * addr)
{
	int b1;
	int b2;
	unsigned int ip;

	// get the first 2 bytes
	ip = ntohl(addr->s_addr);
	b1 = (int)((ip >> 24) & 0xFF);
	b2 = (int)((ip >> 16) & 0xFF);

	// 10.X.X.X
	if(b1 == 10)
		return 1;

	// 172.16-31.X.X
	if((b1 == 172) && ((b2 >= 16) && (b2 <= 31)))
		return 1;

	// 192.168.X.X
	if((b1 == 192) && (b2 == 168))
		return 1;

	return 0;
}

gsi_u32 gsiGetBroadcastIP(void)
{
#if defined(_NITRO)
	gsi_u32 ip;
	IP_GetBroadcastAddr(NULL, (u8*)&ip);
	return ip;

#elif defined(_REVOLUTION)
	/*
	int length;
	gsi_u32 ip;
	
	length = (gsi_u32)sizeof(ip);

	// IP_GetBroadcastAddr replaced by SOGetInterfaceOpt
	// IP_GetBroadcastAddr(NULL, (u8*)&ip);
	SOGetInterfaceOpt(NULL, SO_SOL_IP, SO_INADDR_BROADCAST, (u8*)&ip, &length);
	*/
	 IPAddrEntry* addrtbl; 
    int addrnum;
    int ret;
	int length;
	gsi_u32 ip;
    length = (int)sizeof( addrnum );

    ret = SOGetInterfaceOpt(NULL,
                            SO_SOL_CONFIG,
                            SO_CONFIG_IP_ADDR_NUMBER,
                            (u8*)&addrnum, &length);

    if( ret >= 0 )
        return 0xFFFFFFFF;


    length = (int)(sizeof( IPAddrEntry ) * addrnum);

    addrtbl = (IPAddrEntry*)gsimalloc( (u32)length );

    if( addrtbl == NULL )
        return 0xFFFFFFFF;

    ret = SOGetInterfaceOpt(NULL,
                            SO_SOL_CONFIG, 
                            SO_CONFIG_IP_ADDR_TABLE, 
                            (u8*)addrtbl, 
                            &length);

    if( ret < 0 )
    {
        gsifree( addrtbl );
        return 0xFFFFFFFF;
    }

    ip = (u32)(addrtbl->bcastAddr[3]
            | (addrtbl->bcastAddr[2] << 8)
            | (addrtbl->bcastAddr[1] << 16)
            | (addrtbl->bcastAddr[0] << 24));

    gsifree( addrtbl );
    

    return ip;

	return ip;
	
#else
	return 0xFFFFFFFF;
#endif
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// Re-enable previously disabled compiler warnings
///////////////////////////////////////////////////
#if defined(_MSC_VER)
#pragma warning ( default: 4127 )
#endif // _MSC_VER

