
#ifndef IPHELPERS_H
#define IPHELPERS_H


#include "ichannel.h"


// Loops that poll sockets should Sleep for this amount of time between iterations
// so they don't hog all the CPU.
#define LOOP_POLL_INTERVAL	5


// Useful for putting the arguments into a printf statement.
#define EXPAND_ADDR( x ) (x).ip[0], (x).ip[1], (x).ip[2], (x).ip[3], (x).port


// This is a simple wrapper layer for UDP sockets.
class CIPAddr
{
public:
					CIPAddr();
					CIPAddr( const int inputIP[4], const int inputPort );
					CIPAddr( int ip0, int ip1, int ip2, int ip3, int ipPort );
	
	void			Init( int ip0, int ip1, int ip2, int ip3, int ipPort );
	bool			operator==( const CIPAddr &o ) const;
	bool			operator!=( const CIPAddr &o ) const;

	// Setup to send to the local machine on the specified port.
	void			SetupLocal( int inPort );

public:

	unsigned char	ip[4];
	unsigned short	port;
};



// The chunk walker provides an easy way to copy data out of the chunks as though it were a
// single contiguous chunk of memory.s
class CChunkWalker
{
public:
					CChunkWalker( void const * const *pChunks, const int *pChunkLengths, int nChunks );

	int				GetTotalLength() const;
	void			CopyTo( void *pOut, int nBytes );	

private:
	
	void const * const		*m_pChunks;
	const int				*m_pChunkLengths;
	int						m_nChunks;
	
	int						m_iCurChunk;
	int						m_iCurChunkPos;

	int						m_TotalLength;
};


// This class makes loop that wait on something look nicer. ALL loops using this class
// should follow this pattern, or they can wind up with unforeseen delays that add a whole
// lot of lag.
//
// CWaitTimer waitTimer( 5.0 );
// while ( 1 )
// {
//		do your thing here like Recv() from a socket.
//
//		if ( waitTimer.ShouldKeepWaiting() )
//			Sleep() for some time interval like 5ms so you don't hog the CPU
//		else
//			BREAK HERE
// }
class CWaitTimer
{
public:
			CWaitTimer( double flSeconds );

	bool	ShouldKeepWaiting();	

private:
	unsigned long	m_StartTime;
	unsigned long	m_WaitMS;
};


// Helper function to get time in milliseconds.
unsigned long SampleMilliseconds();


class ISocket
{
public:

	// Call this when you're done.	
	virtual void	Release() = 0;

	
	// Bind the socket so you can send and receive with it.
	// If you bind to port 0, then the system will select the port for you.
	virtual bool	Bind( const CIPAddr *pAddr ) = 0;
	virtual bool	BindToAny( const unsigned short port ) = 0;

	
	// Broadcast some data.
	virtual bool	Broadcast( const void *pData, const int len, const unsigned short port ) = 0;
	
	// Send a packet.
	virtual bool	SendTo( const CIPAddr *pAddr, const void *pData, const int len ) = 0;
	virtual bool	SendChunksTo( const CIPAddr *pAddr, void const * const *pChunks, const int *pChunkLengths, int nChunks ) = 0;

	// Receive a packet. Returns the length received or -1 if no data came in.
	// If pFrom is set, then it is filled in with the sender's IP address.
	virtual int		RecvFrom( void *pData, int maxDataLen, CIPAddr *pFrom ) = 0;

	// How long has it been since we successfully received a packet?
	virtual double	GetRecvTimeout() = 0;
};

// Create a connectionless socket that you can send packets out of.
ISocket* CreateIPSocket();

// This sets up the socket to receive multicast data on the specified group.
// By default, localInterface is INADDR_ANY, but if you want to specify a specific interface
// the data should come in through, you can.
ISocket* CreateMulticastListenSocket( 
	const CIPAddr &addr, 
	const CIPAddr &localInterface = CIPAddr() 
	);


// Setup a CIPAddr from the string. The string can be a dotted IP address or
// a hostname, and it can be followed by a colon and a port number like "1.2.3.4:3443"
// or "myhostname.valvesoftware.com:2342".
//
// Note: if the string does not contain a port, then pOut->port will be left alone.
bool ConvertStringToIPAddr( const char *pStr, CIPAddr *pOut );

// Do a DNS lookup on the IP.
// You can optionally get a service name back too.
bool ConvertIPAddrToString( const CIPAddr *pIn, char *pOut, int outLen );


void IP_GetLastErrorString( char *pStr, int maxLen );

void SockAddrToIPAddr( const struct sockaddr_in *pIn, CIPAddr *pOut );
void IPAddrToSockAddr( const CIPAddr *pIn, struct sockaddr_in *pOut );


#endif

