// Common, group Communication
// Copyright Alexander Liss

#ifndef __SOCKET_H__
#define __SOCKET_H__

// IP only, with window sockets 2, if available;
// when _WIN32_WINNT >= 0x0400, 
//	window sockets 2 is selected automatically;
// use winsock32.dll for sockets 1 and 
//	Ws_32.dll for sockets 2;


#include "win0.h"
#include "strbuf.h"
#include "event.h"

	// DATA CONVERSION

// network byte order is big-endian, Intel's byte order is little-endian
inline unsigned long host2net(unsigned long host_value){return ::htonl(host_value);}
inline unsigned short host2net(unsigned short host_value){return ::htons(host_value);}
inline unsigned long net2host(unsigned long net_value){return ::ntohl(net_value);}
inline unsigned short net2host(unsigned short net_value){return ::ntohs(net_value);}

	// LIBRARY INITIALIZATION AND CLEANUP

struct FunctionSocketLib
{
	int status;
	
FunctionSocketLib(WORD wVersionRequested)
	{WSADATA data; status=::WSAStartup(wVersionRequested,&data);}
~FunctionSocketLib(){if(!status) ::WSACleanup(); }
};

	// INTERNET PROTOCOL ADDRESS

// BYTES IN NETWORK ORDER
struct IPAddress
{
  unsigned long address;

IPAddress(unsigned long z=INADDR_NONE):address(host2net(z)){}
int set_address(unsigned long z)
	{address=host2net(z); return 0;}
int get_dotted(StrBuffer& z)const
	{in_addr a; a.S_un.S_addr=address; z.set(::inet_ntoa(a)); return address==INADDR_NONE;}
int put_dotted(const StrBuffer& z)
	{address=::inet_addr(z.data);return address==INADDR_NONE;}
};

	// address and port

// bytes in network order
struct IPSocketAddress: public IPAddress
{
	unsigned short port;

IPSocketAddress(unsigned short p=0,unsigned long address=INADDR_NONE):
	IPAddress(address),port(host2net(p)){}
int set_port(unsigned short z){port=host2net(z); return 0;}
int get(sockaddr&)const;
int put(const sockaddr&);
};


	// Socket

class Socket
{
public:

	// takes control of previously created handle
	// should be used by a listener only
Socket(SOCKET h):handle(h){}

	// creates a socket
Socket(int type=SOCK_STREAM
#if( _WIN32_WINNT >= 0x0400 )
,DWORD flags=WSA_FLAG_OVERLAPPED,
LPWSAPROTOCOL_INFO pi=0
#endif
);

~Socket(){::closesocket(handle);}

int get_status()const{return handle==INVALID_SOCKET;}
SOCKET get_handle()const{return handle;}
HANDLE get_file_handle()const{return (HANDLE)handle;}

int bind(const IPSocketAddress& own);

// see WSAConnect in the case of socket2
int connect(const IPSocketAddress& peer
#if (_WIN32_WINNT >= 0x0400)
	,LPWSABUF	lpCallerData=0,             
	LPWSABUF	lpCalleeData=0,             
	LPQOS		lpSQOS=0 
#endif
	);

#if (_WIN32_WINNT >= 0x0400)
int connect_multipoint(const IPSocketAddress& peer_or_root
	,LPWSABUF	lpCallerData,             
	LPWSABUF	lpCalleeData,             
	LPQOS		lpSQOS, 
	DWORD		dwFlags
	);
#endif


int reconnect(
#if (_WIN32_WINNT >= 0x0400)
	LPWSABUF	lpCallerData=0,             
	LPWSABUF	lpCalleeData=0,             
	LPQOS		lpSQOS=0 
#endif
			  );

int get_address(IPSocketAddress& );
int get_peer_address(IPSocketAddress& );

int send(DWORD& sent, const VOID *buf,DWORD len,DWORD flags=0);
int receive(DWORD& received, VOID *buf,DWORD limit,DWORD flags=0);
int send_to(DWORD& sent,const VOID *buf,DWORD len,const IPSocketAddress& remote,DWORD flags=0);
int receive_from(DWORD& received,VOID *buf,DWORD limit, IPSocketAddress& remote,DWORD flags=0);
	
int send_loop(DWORD& sent,const VOID *buf,DWORD len,DWORD flags=0);
int receive_loop(DWORD& received, VOID *buf,DWORD limit,DWORD flags=0);
int send_to_loop(DWORD& sent,const VOID *buf,DWORD len,const IPSocketAddress& remote,DWORD flags=0);
int receive_from_loop(DWORD& received,VOID *buf,DWORD limit, IPSocketAddress& remote,DWORD flags=0);

#if (_WIN32_WINNT >= 0x0400)

int send( 
  DWORD&           NumberOfBytesSent,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  DWORD             dwFlags=0)
	{return ::WSASend(handle,lpBuffers,dwBufferCount,&NumberOfBytesSent,dwFlags,0,0);}

int receive( 
  DWORD&           NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  LPDWORD            lpdwFlags=0)
	{return ::WSARecv(handle,lpBuffers,dwBufferCount,&NumberOfBytesReceived,lpdwFlags,0,0);}

int send_to( 
  DWORD&			NumberOfBytesSent,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,   
  const IPSocketAddress&  peer,
  DWORD             dwFlags=0);

int receive_from( 
  DWORD&			NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,  
  IPSocketAddress& peer,
  LPDWORD           lpdwFlags=0);

int send_loop( 
  DWORD&			NumberOfBytesSent,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  DWORD             dwFlags=0);

int receive_loop( 
  DWORD&			NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  LPDWORD           lpdwFlags=0);

int send_to_loop( 
  DWORD&			NumberOfBytesSent,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,   
  const IPSocketAddress&  peer,
  DWORD             dwFlags=0);

int receive_from_loop( 
  DWORD&			NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,  
  IPSocketAddress& peer,
  LPDWORD           lpdwFlags=0);

int receive_ex(char *buf,int limit,int *flags)
	{return ::WSARecvEx(handle,buf,limit,flags);}

int shutdown(int how=SD_BOTH){return ::shutdown(handle,how);}

int io_contrl(
  DWORD              dwIoControlCode,                        
  LPVOID             lpvInBuffer,                            
  DWORD              cbInBuffer,                             
  LPVOID             lpvOutBuffer,                           
  DWORD              cbOutBuffer,                            
  LPDWORD            lpcbBytesReturned
  )                   
{return ::WSAIoctl(handle,dwIoControlCode,lpvInBuffer,cbInBuffer,lpvOutBuffer,cbOutBuffer,lpcbBytesReturned,0,0);}  

// i/o control 
int receive_ready(unsigned long& bytes)
{DWORD cbBytesReturned; return io_contrl(FIONREAD,0,0,&bytes,sizeof(unsigned long),&cbBytesReturned);}

#endif

// old version
int io_control(long command,unsigned long *arguments)
	{return ::ioctlsocket(handle,command,arguments);}

int set_option(int level,int name,const char *value,int len)
	{return ::setsockopt(handle,level,name,value,len);}
int get_option(int level,int name,char *value,int *len)
	{return ::getsockopt(handle,level,name,value,len);}                

// socket options
int set_heartbeat();
int linger(int seconds);
int get_send_buffer_size(int & size);
int set_send_buffer_size(int size);
int get_receive_buffer_size(int & size);
int set_receive_buffer_size(int size);

void abrupt_shutdown();

// uses select
bool receive_data_ready();
bool send_data_ready();

private:
	SOCKET handle;
};

	// SocketPtr - A BASE CLASS

class SocketPtr
{
public:

SocketPtr():socket(0){}
SocketPtr(const SocketPtr& z):socket(z.socket){}
SocketPtr(Socket& z):socket(&z){}
SocketPtr& operator==(const SocketPtr& z)
	{if(this!=&z) socket=z.socket; return *this;}

Socket& get_socket(){return *socket;}
int set_socket(Socket& z){socket=&z;return 0;}
bool is_set(){return socket!=0;}
	
protected:	
	Socket* socket;
};


	// Listener

class Listener: public SocketPtr
{
Listener(const Listener&);
Listener& operator==(const Listener&);
public:

Listener(){}
Listener(Socket& s):SocketPtr(s){}

int listen(int backlog=SOMAXCONN)
	{return ::listen(socket->get_handle(),backlog);}

Socket * new_connection(IPSocketAddress& peer 		   
#if (_WIN32_WINNT >= 0x0400)
  ,LPCONDITIONPROC  lpfnCondition=0,  
  DWORD				dwCallbackData=0
#endif
  );

};


	// asynchronous socket operations through file

class SocketFile
{
SocketFile(const SocketFile&);
SocketFile& operator=(const SocketFile&);
public:

SocketFile(){clear();}
SocketFile(Socket& s){clear();set_socket(s);}
virtual ~SocketFile(){}

int set_socket(Socket& z){handle=z.get_file_handle();return 0;}

BOOL get_result(DWORD& dwTransfer,BOOL bWait=TRUE);

// cancels operations created by calling thread
BOOL cancel();

protected:
	HANDLE handle;
	OVERLAPPED overlapped;   
	void clear();
};


class SocketFileOperation:public SocketFile
{
SocketFileOperation(const SocketFileOperation&);
SocketFileOperation& operator=(const SocketFileOperation&);
public:

SocketFileOperation(){}
SocketFileOperation(Socket& s):SocketFile(s){}
SocketFileOperation(Socket& s, Event& e):SocketFile(s){set_event(e);}
~SocketFileOperation(){}

int set_event(Event& z){overlapped.hEvent=z.get_handle();return 0;}

// RETURNS 0 IF DONE AND -1 IF PENDING, RETURNS >0 IF ERROR
int send(DWORD& actual,const VOID *buffer,DWORD bytes);
int receive(DWORD& actual, VOID *buffer, DWORD bytes_limit);

// blocking, returns -1 if timeout and >0 if error
int send_loop(DWORD& actual,const VOID *buffer,DWORD bytes,DWORD timeout, DWORD wait_slice);
int receive_loop(DWORD& actual,const VOID *buffer,DWORD bytes_limit,DWORD timeout, DWORD wait_slice);	
};


class SocketFileOperationEx: public SocketFile
{
SocketFileOperationEx(const SocketFileOperationEx&);
SocketFileOperationEx& operator=(const SocketFileOperationEx&);
public:

SocketFileOperationEx(){}
SocketFileOperationEx(Socket& s):SocketFile(s){}
SocketFileOperationEx(Socket& s, LPOVERLAPPED_COMPLETION_ROUTINE z):SocketFile(s){}
~SocketFileOperationEx(){}

int set_routine(LPOVERLAPPED_COMPLETION_ROUTINE z){completion_routine=z;return 0;}  

// one or the other
int set_data(VOID *z){overlapped.hEvent=(HANDLE)z;return 0;}
int set_event(Event& z){overlapped.hEvent=z.get_handle();return 0;}

// returns 0 if done and -1 if pending, returns >0 if error
int send(const VOID *buffer,DWORD bytes);
int receive( VOID *buffer, DWORD bytes_limit);

private:
	
	VOID (CALLBACK* completion_routine) (DWORD dwErrorCode,DWORD dwNumberOfBytesTransfered,LPOVERLAPPED lpOverlapped);
};



#if (_WIN32_WINNT >= 0x0400)

	// SocketEvent

class SocketEvent
{
	SocketEvent(const SocketEvent&);
	SocketEvent& operator=(const SocketEvent&);
public:
    
SocketEvent(){handle=WSACreateEvent();}
~SocketEvent(){if(!status())WSACloseEvent(handle);}	
    
WSAEVENT get_handle()const{return handle;}

int status()const{return handle==WSA_INVALID_EVENT;}

BOOL set(){return WSASetEvent(handle);}
BOOL reset(){return WSAResetEvent(handle);}

private:
	WSAEVENT  handle;
};

	// network events monitor

class NetworkEvents: public SocketPtr
{
NetworkEvents(const NetworkEvents& z);
NetworkEvents& operator=(const NetworkEvents& z);
public:

SocketEvent event;

NetworkEvents(){}
NetworkEvents(Socket& z):SocketPtr(z){}

int start_record(long events_mask)
{return WSAEventSelect(socket->get_handle(),event.get_handle(),events_mask);}

int report_record(LPWSANETWORKEVENTS lpNetworkEvents)
{return WSAEnumNetworkEvents(socket->get_handle(),event.get_handle(),lpNetworkEvents);}

};

	// asynchronous socket operations

// if completion routine is set, the event is ignored 
class SocketOperation: public SocketPtr
{
SocketOperation(const SocketOperation&);
SocketOperation& operator=(const SocketOperation&);
public:

SocketOperation(){clear();}
SocketOperation(Socket& s):SocketPtr(s){clear();}
SocketOperation(Socket& s, SocketEvent& e):SocketPtr(s){clear();set_event(e);}
~SocketOperation(){}

int set_routine(LPWSAOVERLAPPED_COMPLETION_ROUTINE z){completion_routine=z;return 0;} 

// one or the other
int set_event(SocketEvent& z){overlapped.hEvent=z.get_handle();return 0;}
int set_data(LPVOID z){overlapped.hEvent=(WSAEVENT)z;return 0;}

WSAEVENT get_handle()const{return overlapped.hEvent;}

int io_contrl(
  DWORD              dwIoControlCode,                        
  LPVOID             lpvInBuffer,                            
  DWORD              cbInBuffer,                             
  LPVOID             lpvOutBuffer,                           
  DWORD              cbOutBuffer
  )                   
{DWORD cbBytesReturned;return ::WSAIoctl(socket->get_handle(),dwIoControlCode,lpvInBuffer,cbInBuffer,lpvOutBuffer,cbOutBuffer,&cbBytesReturned,&overlapped,completion_routine);}  

int receive_ready(unsigned long& bytes)
{return io_contrl(FIONREAD,0,0,&bytes,sizeof(unsigned long));}

int send( 
  DWORD&			NumberOfBytesSent,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  DWORD             dwFlags=0)
	{return ::WSASend(socket->get_handle(),lpBuffers,dwBufferCount,&NumberOfBytesSent,dwFlags,&overlapped,completion_routine);}

int receive( 
  DWORD&			NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  LPDWORD            lpdwFlags=0)
	{return ::WSARecv(socket->get_handle(),lpBuffers,dwBufferCount,&NumberOfBytesReceived,lpdwFlags,&overlapped,completion_routine);}

int send_to( 
  DWORD&			NumberOfBytesSent,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,   
  const IPSocketAddress&  peer,
  DWORD             dwFlags=0);

int receive_from( 
  DWORD&			NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,  
  IPSocketAddress&	peer,
  LPDWORD           lpdwFlags=0);

int send_loop(
  DWORD&			NumberOfBytesSent,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  DWORD				timeout_milliseconds, 
  DWORD				wait_slice_milliseconds,
  DWORD             dwFlags=0);

int receive_loop( 
  DWORD&			NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  DWORD				timeout_milliseconds, 
  DWORD				wait_slice_milliseconds,
  LPDWORD           lpdwFlags=0);

int send_to_loop( 
  DWORD&			NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,
  const IPSocketAddress&  peer,
  DWORD				timeout_milliseconds, 
  DWORD				wait_slice_milliseconds,  
  DWORD             dwFlags=0);

int receive_from_loop( 
  DWORD&			NumberOfBytesReceived,
  LPWSABUF          lpBuffers,                                     
  DWORD             dwBufferCount,  
  IPSocketAddress&	peer,
  DWORD				timeout_milliseconds, 
  DWORD				wait_slice_milliseconds,
  LPDWORD           lpdwFlags=0);

BOOL get_result(
	DWORD& dwTransfer,
	BOOL nWait=TRUE,
	LPDWORD lpdwFlags=0
	)
{return WSAGetOverlappedResult(socket->get_handle(),&overlapped,&dwTransfer,nWait,lpdwFlags);}


protected:
	
	WSAOVERLAPPED overlapped;   
	void (CALLBACK *completion_routine)(DWORD dwError, DWORD cbTransferred,LPWSAOVERLAPPED lpOverlapped,DWORD dwFlags);

	void clear();
};



	// SocketWaitableCollection

class SocketWaitableCollection
{
friend DWORD wait(const SocketWaitableCollection&,BOOL,DWORD,BOOL);
public:
SocketWaitableCollection():size(0){}
SocketWaitableCollection(const SocketWaitableCollection & z){copy(z);}
SocketWaitableCollection & operator= (const SocketWaitableCollection & z)
{if(&z!=this) copy(z); return *this;}

LONG get_number()const{return size;}
int add(const SocketEvent&);
int add(const SocketOperation&);
int add(const SocketWaitableCollection & );
private:
 WSAEVENT handles[WSA_MAXIMUM_WAIT_EVENTS];
 LONG size;
 void copy(const SocketWaitableCollection & );
};


int monitor(SocketEvent& signal,const Socket& socket,long event_flags);


	// wait functions

DWORD wait(const SocketEvent&,
		 DWORD milliseconds=WSA_INFINITE,
		 BOOL alertable=FALSE);

DWORD wait(const SocketWaitableCollection&,
		 BOOL bWaitAll=FALSE, 
		 DWORD milliseconds=WSA_INFINITE, 
		 BOOL alertable=FALSE);



#endif // _WIN32_WINNT >= 0x0400

// returns 2=error, 1=timeout, 0=data ready 
// uses select
DWORD wait_receive_ready(Socket& socket,DWORD milliseconds);


	// FUNCTIONS

inline int socket_last_error(){return ::WSAGetLastError();}

// ip address # n
int IP_host(StrBuffer& name,IPAddress& address, int n=0);

#endif