/// \file
///
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
///
/// Usage of RakNet is subject to the appropriate license agreement.


#include "RakNetTypes.h"
#include "RakAssert.h"
#include <string.h>
#include <stdio.h>
#include "WindowsIncludes.h"
#include "WSAStartupSingleton.h"
#include "SocketDefines.h"


#if   defined(_WIN32)
// extern __int64 _strtoui64(const char*, char**, int); // needed for Code::Blocks. Does not compile on Visual Studio 2010
// IP_DONTFRAGMENT is different between winsock 1 and winsock 2.  Therefore, Winsock2.h must be linked againt Ws2_32.lib
// winsock.h must be linked against WSock32.lib.  If these two are mixed up the flag won't work correctly
#include <winsock2.h>

#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#include <string.h> // strncasecmp
#include "Itoa.h"
#include "SocketLayer.h"
#include "SuperFastHash.h"
#include <stdlib.h>

using namespace RakNet;

const char *IPV6_LOOPBACK="::1";
const char *IPV4_LOOPBACK="127.0.0.1";

AddressOrGUID::AddressOrGUID( Packet *packet )
{
	rakNetGuid=packet->guid;
	systemAddress=packet->systemAddress;
}

unsigned long AddressOrGUID::ToInteger( const AddressOrGUID &aog )
{
	if (aog.rakNetGuid!=UNASSIGNED_RAKNET_GUID)
		return RakNetGUID::ToUint32(aog.rakNetGuid);
	return SystemAddress::ToInteger(aog.systemAddress);
}
const char *AddressOrGUID::ToString(bool writePort) const
{
	if (rakNetGuid!=UNASSIGNED_RAKNET_GUID)
		return rakNetGuid.ToString();
	return systemAddress.ToString(writePort);
}
void AddressOrGUID::ToString(bool writePort, char *dest) const
{
	if (rakNetGuid!=UNASSIGNED_RAKNET_GUID)
		return rakNetGuid.ToString(dest);
	return systemAddress.ToString(writePort,dest);
}
bool RakNet::NonNumericHostString( const char *host )
{
	if ( host[ 0 ] >= '0' && host[ 0 ] <= '9' )
		return false;

	if ( (host[ 0 ] == '-') && ( host[ 1 ] >= '0' && host[ 1 ] <= '9' ) )
		return false;

	if ( strstr(host,":") )
		return false;

	return true;
}

SocketDescriptor::SocketDescriptor() {port=0; hostAddress[0]=0; remotePortRakNetWasStartedOn_PS3_PSP2=0; extraSocketOptions=0; socketFamily=AF_INET;}
SocketDescriptor::SocketDescriptor(unsigned short _port, const char *_hostAddress)
{
	remotePortRakNetWasStartedOn_PS3_PSP2=0;
	port=_port;
	if (_hostAddress)
		strcpy(hostAddress, _hostAddress);
	else
		hostAddress[0]=0;
	extraSocketOptions=0;
	socketFamily=AF_INET;
}

// Defaults to not in peer to peer mode for NetworkIDs.  This only sends the localSystemAddress portion in the BitStream class
// This is what you want for client/server, where the server assigns all NetworkIDs and it is unnecessary to transmit the full structure.
// For peer to peer, this will transmit the systemAddress of the system that created the object in addition to localSystemAddress.  This allows
// Any system to create unique ids locally.
// All systems must use the same value for this variable.
//bool RAK_DLL_EXPORT NetworkID::peerToPeerMode=false;

SystemAddress& SystemAddress::operator = ( const SystemAddress& input )
{
	memcpy(&address, &input.address, sizeof(address));
	systemIndex = input.systemIndex;
	debugPort = input.debugPort;
	return *this;
}
bool SystemAddress::EqualsExcludingPort( const SystemAddress& right ) const
{
	return (address.addr4.sin_family==AF_INET && address.addr4.sin_addr.s_addr==right.address.addr4.sin_addr.s_addr)
#if RAKNET_SUPPORT_IPV6==1
		|| (address.addr4.sin_family==AF_INET6 && memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))==0)
#endif
		;
}
unsigned short SystemAddress::GetPort(void) const
{
	return ntohs(address.addr4.sin_port);
}
unsigned short SystemAddress::GetPortNetworkOrder(void) const
{
	return address.addr4.sin_port;
}
void SystemAddress::SetPort(unsigned short s)
{
	address.addr4.sin_port=htons(s);
	debugPort=s;
}
void SystemAddress::SetPortNetworkOrder(unsigned short s)
{
	address.addr4.sin_port=s;
	debugPort=ntohs(s);
}
bool SystemAddress::operator==( const SystemAddress& right ) const
{
	return address.addr4.sin_port == right.address.addr4.sin_port && EqualsExcludingPort(right);
}

bool SystemAddress::operator!=( const SystemAddress& right ) const
{
	return (*this==right)==false;
}

bool SystemAddress::operator>( const SystemAddress& right ) const
{
	if (address.addr4.sin_port == right.address.addr4.sin_port)
	{
#if RAKNET_SUPPORT_IPV6==1
		if (address.addr4.sin_family==AF_INET)
			return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr;
		return memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))>0;
#else
		return address.addr4.sin_addr.s_addr>right.address.addr4.sin_addr.s_addr;
#endif
	}
	return address.addr4.sin_port>right.address.addr4.sin_port;
}

bool SystemAddress::operator<( const SystemAddress& right ) const
{
	if (address.addr4.sin_port == right.address.addr4.sin_port)
	{
#if RAKNET_SUPPORT_IPV6==1
		if (address.addr4.sin_family==AF_INET)
			return address.addr4.sin_addr.s_addr<right.address.addr4.sin_addr.s_addr;
		return memcmp(address.addr6.sin6_addr.s6_addr, right.address.addr6.sin6_addr.s6_addr, sizeof(address.addr6.sin6_addr.s6_addr))>0;
#else
		return address.addr4.sin_addr.s_addr<right.address.addr4.sin_addr.s_addr;
#endif
	}
	return address.addr4.sin_port<right.address.addr4.sin_port;
}
int SystemAddress::size(void)
{
#if RAKNET_SUPPORT_IPV6==1
	return sizeof(sockaddr_in6) + sizeof(char);
#else
	return sizeof(unsigned long) + sizeof(unsigned short) + sizeof(char);
#endif
}
unsigned long SystemAddress::ToInteger( const SystemAddress &sa )
{
	unsigned int lastHash = SuperFastHashIncremental ((const char*) & sa.address.addr4.sin_port, sizeof(sa.address.addr4.sin_port), sizeof(sa.address.addr4.sin_port) );
#if RAKNET_SUPPORT_IPV6==1
	if (sa.address.addr4.sin_family==AF_INET)
		return SuperFastHashIncremental ((const char*) & sa.address.addr4.sin_addr.s_addr, sizeof(sa.address.addr4.sin_addr.s_addr), lastHash );
	else
		return SuperFastHashIncremental ((const char*) & sa.address.addr6.sin6_addr.s6_addr, sizeof(sa.address.addr6.sin6_addr.s6_addr), lastHash );
#else
	return SuperFastHashIncremental ((const char*) & sa.address.addr4.sin_addr.s_addr, sizeof(sa.address.addr4.sin_addr.s_addr), lastHash );
#endif
}
unsigned char SystemAddress::GetIPVersion(void) const
{
	if (address.addr4.sin_family==AF_INET)
		return 4;
	return 6;
}
unsigned int SystemAddress::GetIPPROTO(void) const
{
#if RAKNET_SUPPORT_IPV6==1
	if (address.addr4.sin_family==AF_INET)
		return IPPROTO_IP;
	return IPPROTO_IPV6;
#else
	return IPPROTO_IP;
#endif
}
void SystemAddress::SetToLoopback(void)
{
	SetToLoopback(GetIPVersion());
}
void SystemAddress::SetToLoopback(unsigned char ipVersion)
{
	if (ipVersion==4)
	{
		FromString(IPV4_LOOPBACK, 0, ipVersion);
	}
	else
	{
		FromString(IPV6_LOOPBACK, 0, ipVersion);
	}
}
bool SystemAddress::IsLoopback(void) const
{
	if (GetIPVersion()==4)
	{
		// unsigned long l = htonl(address.addr4.sin_addr.s_addr);
		if (htonl(address.addr4.sin_addr.s_addr)==2130706433)
			return true;
	}
#if RAKNET_SUPPORT_IPV6==1
	else
	{
		const static char localhost[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
		if (memcmp(&address.addr6.sin6_addr, localhost, 16)==0)
			return true;
	}
#endif
	return false;
}
void SystemAddress::ToString_Old(bool writePort, char *dest, char portDelineator) const
{
	if (*this==UNASSIGNED_SYSTEM_ADDRESS)
	{
		strcpy(dest, "UNASSIGNED_SYSTEM_ADDRESS");
		return;
	}

	char portStr[2];
	portStr[0]=portDelineator;
	portStr[1]=0;












	in_addr in;
	in.s_addr = address.addr4.sin_addr.s_addr;
	const char *ntoaStr = inet_ntoa( in );
	strcpy(dest, ntoaStr);
	if (writePort)
	{
		strcat(dest, portStr);
		Itoa(GetPort(), dest+strlen(dest), 10);
	}

}
const char *SystemAddress::ToString(bool writePort, char portDelineator) const
{
	static unsigned char strIndex=0;
#if RAKNET_SUPPORT_IPV6==1
	static char str[8][INET6_ADDRSTRLEN+5+1];
#else
	static char str[8][22+5+1];
#endif

	unsigned char lastStrIndex=strIndex;
	strIndex++;
	ToString(writePort, str[lastStrIndex&7], portDelineator);
	return (char*) str[lastStrIndex&7];
}
#if RAKNET_SUPPORT_IPV6==1
void SystemAddress::ToString_New(bool writePort, char *dest, char portDelineator) const
{
	int ret;
	(void) ret;

	if (*this==UNASSIGNED_SYSTEM_ADDRESS)
	{
		strcpy(dest, "UNASSIGNED_SYSTEM_ADDRESS");
		return;
	}
















	if (address.addr4.sin_family==AF_INET)
	{
		ret=getnameinfo((struct sockaddr *) &address.addr4, sizeof(struct sockaddr_in), dest, 22, NULL, 0, NI_NUMERICHOST);
	}
	else
	{
		ret=getnameinfo((struct sockaddr *) &address.addr6, sizeof(struct sockaddr_in6), dest, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
	}
	if (ret!=0)
	{
		dest[0]=0;
	}

	if (writePort)
	{
		unsigned char ch[2];
		ch[0]=portDelineator;
		ch[1]=0;
		strcat(dest, (const char*) ch);
		Itoa(ntohs(address.addr4.sin_port), dest+strlen(dest), 10);
	}

}
#endif // #if RAKNET_SUPPORT_IPV6!=1
void SystemAddress::ToString(bool writePort, char *dest, char portDelineator) const
{

#if RAKNET_SUPPORT_IPV6!=1
	ToString_Old(writePort,dest,portDelineator);
#else
	ToString_New(writePort,dest,portDelineator);
#endif // #if RAKNET_SUPPORT_IPV6!=1
}
SystemAddress::SystemAddress()
{
	address.addr4.sin_family=AF_INET;
	// used for operator ==
	memset(&address,0,sizeof(address)); address.addr4.sin_family=AF_INET;
	systemIndex=(SystemIndex)-1;
	debugPort=0;
}
SystemAddress::SystemAddress(const char *str)
{
	address.addr4.sin_family=AF_INET;
	SetPort(0);
	FromString(str);
	systemIndex=(SystemIndex)-1;
}
SystemAddress::SystemAddress(const char *str, unsigned short port)
{
	address.addr4.sin_family=AF_INET;
	FromStringExplicitPort(str,port);
	systemIndex=(SystemIndex)-1;
}






















#ifdef _MSC_VER
#pragma warning( disable : 4996 )  // The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _strnicmp. See online help for details.
#endif
void SystemAddress::FixForIPVersion(const SystemAddress &boundAddressToSocket)
{
	char str[128];
	ToString(false,str);
	// TODO - what about 255.255.255.255?
	if (strcmp(str, IPV6_LOOPBACK)==0)
	{
		if (boundAddressToSocket.GetIPVersion()==4)
		{
			FromString(IPV4_LOOPBACK,0,4);
		}
	}
	else if (strcmp(str, IPV4_LOOPBACK)==0)
	{
#if RAKNET_SUPPORT_IPV6==1
		if (boundAddressToSocket.GetIPVersion()==6)
		{
			FromString(IPV6_LOOPBACK,0,6);
		}
#endif

// 		if (boundAddressToSocket.GetIPVersion()==4)
// 		{
// 			// Some kind of bug with sendto: returns "The requested address is not valid in its context." if loopback doesn't have the same IP address
// 			address.addr4.sin_addr.s_addr=boundAddressToSocket.address.addr4.sin_addr.s_addr;
// 		}
	}
}
void SystemAddress::SetBinaryAddress(const char *str, char portDelineator)
{
	if ( NonNumericHostString( str ) )
	{

#if defined(_WIN32)
		if (_strnicmp(str,"localhost", 9)==0)
#else
		if (strncasecmp(str,"localhost", 9)==0)
#endif
		{



			address.addr4.sin_addr.s_addr=inet_addr__("127.0.0.1");

			if (str[9])
			{
				SetPort((unsigned short) atoi(str+9));
			}
			return;
		}

		const char *ip = ( char* ) SocketLayer::DomainNameToIP( str );
		if (ip)
		{



			address.addr4.sin_addr.s_addr=inet_addr__(ip);

		}
	}
	else
	{
		//#ifdef _XBOX
		//	binaryAddress=UNASSIGNED_SYSTEM_ADDRESS.binaryAddress;
		//#else
		// Split the string into the first part, and the : part
		int index, portIndex;
		char IPPart[22];
		char portPart[10];
		// Only write the valid parts, don't change existing if invalid
		//	binaryAddress=UNASSIGNED_SYSTEM_ADDRESS.binaryAddress;
		//	port=UNASSIGNED_SYSTEM_ADDRESS.port;
		for (index=0; str[index] && str[index]!=portDelineator && index<22; index++)
		{
			if (str[index]!='.' && (str[index]<'0' || str[index]>'9'))
				break;
			IPPart[index]=str[index];
		}
		IPPart[index]=0;
		portPart[0]=0;
		if (str[index] && str[index+1])
		{
			index++;
			for (portIndex=0; portIndex<10 && str[index] && index < 22+10; index++, portIndex++)
			{
				if (str[index]<'0' || str[index]>'9')
					break;

				portPart[portIndex]=str[index];
			}
			portPart[portIndex]=0;
		}













		if (IPPart[0])
		{



			address.addr4.sin_addr.s_addr=inet_addr__(IPPart);

		}


		if (portPart[0])
		{
			address.addr4.sin_port=htons((unsigned short) atoi(portPart));
			debugPort=ntohs(address.addr4.sin_port);
		}
		//#endif
	}
}

bool SystemAddress::FromString(const char *str, char portDelineator, int ipVersion)
{
#if RAKNET_SUPPORT_IPV6!=1
	(void) ipVersion;
	SetBinaryAddress(str,portDelineator);
	return true;
#else
	if (str==0)
	{
		memset(&address,0,sizeof(address));
		address.addr4.sin_family=AF_INET;
		return true;
	}
#if RAKNET_SUPPORT_IPV6==1
	char ipPart[INET6_ADDRSTRLEN];
#else
	char ipPart[INET_ADDRSTRLEN];
#endif
	char portPart[32];
	int i=0,j;

	// TODO - what about 255.255.255.255?
	if (ipVersion==4 && strcmp(str, IPV6_LOOPBACK)==0)
	{
		strcpy(ipPart,IPV4_LOOPBACK);
	}
	else if (ipVersion==6 && strcmp(str, IPV4_LOOPBACK)==0)
	{
		address.addr4.sin_family=AF_INET6;
		strcpy(ipPart,IPV6_LOOPBACK);
	}
	else if (NonNumericHostString(str)==false)
	{
		for (; i < sizeof(ipPart) && str[i]!=0 && str[i]!=portDelineator; i++)
		{
			if ((str[i]<'0' || str[i]>'9') && (str[i]<'a' || str[i]>'f') && (str[i]<'A' || str[i]>'F') && str[i]!='.' && str[i]!=':' && str[i]!='%' && str[i]!='-' && str[i]!='/')
				break;

			ipPart[i]=str[i];
		}
		ipPart[i]=0;
	}
	else
	{
		strncpy(ipPart,str,sizeof(ipPart));
		ipPart[sizeof(ipPart)-1]=0;
	}

	j=0;
	if (str[i]==portDelineator && portDelineator!=0)
	{
		i++;
		for (; j < sizeof(portPart) && str[i]!=0; i++, j++)
		{
			portPart[j]=str[i];
		}
	}
	portPart[j]=0;










	// needed for getaddrinfo
	WSAStartupSingleton::AddRef();

	// This could be a domain, or a printable address such as "192.0.2.1" or "2001:db8:63b3:1::3490"
	// I want to convert it to its binary representation
	addrinfo hints, *servinfo=0;
	memset(&hints, 0, sizeof hints);
	hints.ai_socktype = SOCK_DGRAM;
	if (ipVersion==6)
		hints.ai_family = AF_INET6;
	else if (ipVersion==4)
		hints.ai_family = AF_INET;
	else
		hints.ai_family = AF_UNSPEC;
	getaddrinfo(ipPart, "", &hints, &servinfo);
	if (servinfo==0)
		return false;
	RakAssert(servinfo);
	
	unsigned short oldPort = address.addr4.sin_port;
#if RAKNET_SUPPORT_IPV6==1
	if (servinfo->ai_family == AF_INET)
	{
// 		if (ipVersion==6)
// 		{
			address.addr4.sin_family=AF_INET6;
// 			memset(&address.addr6,0,sizeof(address.addr6));
// 			memcpy(address.addr6.sin6_addr.s6_addr+12,&((struct sockaddr_in *)servinfo->ai_addr)->sin_addr.s_addr,sizeof(unsigned long));
// 		}
// 		else
// 		{
			address.addr4.sin_family=AF_INET;
			memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in));
//		}
	}
	else
	{
		address.addr4.sin_family=AF_INET6;
		memcpy(&address.addr6, (struct sockaddr_in6 *)servinfo->ai_addr,sizeof(struct sockaddr_in6));
	}
#else
	address.addr4.sin_family=AF_INET4;
	memcpy(&address.addr4, (struct sockaddr_in *)servinfo->ai_addr,sizeof(struct sockaddr_in));
#endif

	freeaddrinfo(servinfo); // free the linked list

	// needed for getaddrinfo
	WSAStartupSingleton::Deref();

	// PORT
	if (portPart[0])
	{
		address.addr4.sin_port=htons((unsigned short) atoi(portPart));
		debugPort=ntohs(address.addr4.sin_port);
	}
	else
	{
		address.addr4.sin_port=oldPort;
	}
#endif // #if RAKNET_SUPPORT_IPV6!=1

	return true;
}
bool SystemAddress::FromStringExplicitPort(const char *str, unsigned short port, int ipVersion)
{
	bool b = FromString(str,(char) 0,ipVersion);
	if (b==false)
		return false;
	address.addr4.sin_port=htons(port);
	debugPort=ntohs(address.addr4.sin_port);
	return true;
}
void SystemAddress::CopyPort( const SystemAddress& right )
{
	address.addr4.sin_port=right.address.addr4.sin_port;
	debugPort=right.debugPort;
}
RakNetGUID::RakNetGUID()
{
	systemIndex=(SystemIndex)-1;
	*this=UNASSIGNED_RAKNET_GUID;
}
bool RakNetGUID::operator==( const RakNetGUID& right ) const
{
	return g==right.g;
}
bool RakNetGUID::operator!=( const RakNetGUID& right ) const
{
	return g!=right.g;
}
bool RakNetGUID::operator > ( const RakNetGUID& right ) const
{
	return g > right.g;
}
bool RakNetGUID::operator < ( const RakNetGUID& right ) const
{
	return g < right.g;
}
const char *RakNetGUID::ToString(void) const
{
	static unsigned char strIndex=0;
	static char str[8][64];

	unsigned char lastStrIndex=strIndex;
	strIndex++;
	ToString(str[lastStrIndex&7]);
	return (char*) str[lastStrIndex&7];
}
void RakNetGUID::ToString(char *dest) const
{
	if (*this==UNASSIGNED_RAKNET_GUID)
		strcpy(dest, "UNASSIGNED_RAKNET_GUID");

	//sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]);
	sprintf(dest, "%" PRINTF_64_BIT_MODIFIER "u", (long long unsigned int) g);
	// sprintf(dest, "%u.%u.%u.%u.%u.%u", g[0], g[1], g[2], g[3], g[4], g[5]);
}
bool RakNetGUID::FromString(const char *source)
{
	if (source==0)
		return false;



#if   defined(WIN32)
	g=_strtoui64(source, NULL, 10);


#else
	// Changed from g=strtoull(source,0,10); for android
	g=strtoull(source, (char **)NULL, 10);
#endif
	return true;

}
unsigned long RakNetGUID::ToUint32( const RakNetGUID &g )
{
	return ((unsigned long) (g.g >> 32)) ^ ((unsigned long) (g.g & 0xFFFFFFFF));
}
