//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include "winsock.h"
#elif _LINUX
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>

#else
#error "implement me"
#endif

#include "netapi.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#pragma warning(disable: 4706) // warning C4706: assignment within conditional expression

//-----------------------------------------------------------------------------
// Purpose: Implements INetAPI
//-----------------------------------------------------------------------------
class CNetAPI : public INetAPI
{
public:
	virtual void		NetAdrToSockAddr( netadr_t *a, struct sockaddr *s );
	virtual void		SockAddrToNetAdr( struct sockaddr *s, netadr_t *a );

	virtual char		*AdrToString( netadr_t *a );
	virtual bool		StringToAdr( const char *s, netadr_t *a );

	virtual void		GetSocketAddress( int socket, netadr_t *a );

	virtual bool		CompareAdr( netadr_t *a, netadr_t *b );

	virtual void	GetLocalIP(netadr_t *a);
};

// Expose interface
static CNetAPI g_NetAPI;
INetAPI *net = ( INetAPI * )&g_NetAPI;

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *a - 
//			*s - 
//-----------------------------------------------------------------------------
void CNetAPI::NetAdrToSockAddr (netadr_t *a, struct sockaddr *s)
{
	memset (s, 0, sizeof(*s));

	if (a->type == NA_BROADCAST)
	{
		((struct sockaddr_in *)s)->sin_family = AF_INET;
		((struct sockaddr_in *)s)->sin_port = a->port;
		((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST;
	}
	else if (a->type == NA_IP)
	{
		((struct sockaddr_in *)s)->sin_family = AF_INET;
		((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip;
		((struct sockaddr_in *)s)->sin_port = a->port;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *s - 
//			*a - 
//-----------------------------------------------------------------------------
void CNetAPI::SockAddrToNetAdr( struct sockaddr *s, netadr_t *a )
{
	if (s->sa_family == AF_INET)
	{
		a->type = NA_IP;
		*(int *)&a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr;
		a->port = ((struct sockaddr_in *)s)->sin_port;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *a - 
// Output : char
//-----------------------------------------------------------------------------
char *CNetAPI::AdrToString( netadr_t *a )
{
	static	char	s[64];

	memset(s, 0, 64);

	if ( a )
	{
		if ( a->type == NA_LOOPBACK )
		{
			sprintf (s, "loopback");
		}
		else if ( a->type == NA_IP )
		{
			sprintf(s, "%i.%i.%i.%i:%i", a->ip[0], a->ip[1], a->ip[2], a->ip[3], ntohs( a->port ) );
		}
	}
	return s;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *s - 
//			*sadr - 
// Output : static bool
//-----------------------------------------------------------------------------
static bool StringToSockaddr( const char *s, struct sockaddr *sadr )
{
	struct hostent	*h;
	char	*colon;
	char	copy[128];
	struct sockaddr_in *p;
	
	memset (sadr, 0, sizeof(*sadr));
	
	p = ( struct sockaddr_in * )sadr;
	p->sin_family = AF_INET;
	p->sin_port = 0;

	strcpy (copy, s);

	// strip off a trailing :port if present
	for ( colon = copy ; *colon ; colon++ )
	{
		if (*colon == ':')
		{
			// terminate
			*colon = 0;
			// Start at next character
			p->sin_port = htons( ( short )atoi( colon + 1 ) );	
		}
	}
	
	// Numeric IP, no DNS
	if ( copy[0] >= '0' && copy[0] <= '9' && strstr( copy, "." ) )
	{
		*(int *)&p->sin_addr = inet_addr( copy );
	}
	else
	{
		// DNS it
		if ( !( h = gethostbyname( copy ) ) )
		{
			return false;
		}
		// Use first result
		*(int *)&p->sin_addr = *(int *)h->h_addr_list[0];
	}
	
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *s - 
//			*a - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNetAPI::StringToAdr( const char *s, netadr_t *a )
{
	struct sockaddr sadr;
	
	if ( !strcmp ( s, "localhost" ) )
	{
		memset ( a, 0, sizeof( *a ) );
		a->type = NA_LOOPBACK;
		return true;
	}

	if ( !StringToSockaddr (s, &sadr) )
	{
		return false;
	}
	
	SockAddrToNetAdr( &sadr, a );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Lookup the IP address for the specified IP socket
// Input  : socket - 
//			*a - 
//-----------------------------------------------------------------------------
void CNetAPI::GetSocketAddress( int socket, netadr_t *a )
{
	char	buff[512];
	struct sockaddr_in	address;
	int		namelen;
//	int     net_error = 0;

	memset( a, 0, sizeof( *a ) );
	gethostname(buff, 512);
	// Ensure that it doesn't overrun the buffer
	buff[512-1] = 0;

	StringToAdr(buff, a );
		
	namelen = sizeof(address);
	if ( getsockname( socket, (struct sockaddr *)&address, (int *)&namelen) == 0 )
	{
		a->port = address.sin_port;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Full IP address compare
// Input  : *a - 
//			*b - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CNetAPI::CompareAdr( netadr_t *a, netadr_t *b )
{
	if ( a->type != b->type )
	{
		return false;
	}

	if ( a->type == NA_LOOPBACK )
	{
		return true;
	}

	if ( a->type == NA_IP &&
		 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;
}

//-----------------------------------------------------------------------------
// Purpose: Full IP address compare
// Input  : *a - 
//			*b - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------

void CNetAPI::GetLocalIP(netadr_t *a)
{
	char s[64];

	if(!::gethostname(s,64))
	{
		struct hostent *localip = ::gethostbyname(s);
		if(localip)
		{
			a->type=NA_IP;
			a->port=0;
			memcpy(a->ip,localip->h_addr_list[0],4);
		}
	}
}
