#ifndef _BLOCKING_SOCKET_H_
#define _BLOCKING_SOCKET_H_

#include <winsock2.h>
// blocksock.h

// needs winsock.h in the precompiled headers

typedef const struct sockaddr* LPCSOCKADDR;

class CBlockingSocketException : public CException
{
	DECLARE_DYNAMIC(CBlockingSocketException)
public:
// Constructor
	CBlockingSocketException(char* pchMessage);

public:
	~CBlockingSocketException() {}
	virtual BOOL GetErrorMessage(LPTSTR lpstrError, UINT nMaxError,
		PUINT pnHelpContext = NULL);
private:
	int m_nError;
	CString m_strMessage;
};


extern void LogBlockingSocketException(LPVOID pParam, char* pch, CBlockingSocketException* pe);


class CSockAddr : public sockaddr_in {
public:
	// constructors
	CSockAddr()
		{ sin_family = AF_INET;
		  sin_port = 0;
		  sin_addr.s_addr = 0; } // Default

	CSockAddr(const SOCKADDR& sa) { memcpy(this, &sa, sizeof(SOCKADDR)); }

	CSockAddr(const SOCKADDR_IN& sin) { memcpy(this, &sin, sizeof(SOCKADDR_IN)); }

	CSockAddr(const ULONG ulAddr, const USHORT ushPort = 0) // parms are host byte ordered
		{ sin_family = AF_INET;
		  sin_port = htons(ushPort);
	      sin_addr.s_addr = htonl(ulAddr); }

	CSockAddr(const char* pchIP, const USHORT ushPort = 0) // dotted IP addr string
		{ sin_family = AF_INET;
		  sin_port = htons(ushPort);
		  sin_addr.s_addr = inet_addr(pchIP); } // already network byte ordered

	// Return the address in dotted-decimal format
	CString DottedDecimal()
		{ return inet_ntoa(sin_addr); } // constructs a new CString object
	// Get port and address (even though they're public)

	USHORT Port() const
		{ return ntohs(sin_port); }
	ULONG IPAddr() const
		{ return ntohl(sin_addr.s_addr); }
	// operators added for efficiency
	const CSockAddr& operator=(const SOCKADDR& sa)
		{ memcpy(this, &sa, sizeof(SOCKADDR));
		  return *this; }
	const CSockAddr& operator=(const SOCKADDR_IN& sin)
		{ memcpy(this, &sin, sizeof(SOCKADDR_IN));
		  return *this; }
	operator SOCKADDR()
		{ return *((LPSOCKADDR) this); }
	operator LPSOCKADDR()
		{ return (LPSOCKADDR) this; }
	operator LPSOCKADDR_IN()
		{ return (LPSOCKADDR_IN) this; }

	CSockAddr & operator=(const CSockAddr &  saddr){
		sin_family = saddr.sin_family;
		sin_port = saddr.sin_port;
		sin_addr.s_addr = saddr.sin_addr.s_addr;
		return *this;
	}

};

// member functions truly block and must not be used in UI threads
// use this class as an alternative to the MFC CSocket class
class CBlockingSocket : public CObject
{
	DECLARE_DYNAMIC(CBlockingSocket)
	SOCKET m_hSocket;
public:
	CBlockingSocket(const CBlockingSocket & rhs){ m_hSocket = rhs.m_hSocket; }
	CBlockingSocket() { m_hSocket = NULL; }
	CBlockingSocket(SOCKET hSocket): m_hSocket(hSocket){}
	SOCKET GetSocket(){return m_hSocket;}
	void Cleanup();
	int Create(int nType = SOCK_STREAM);
	int Close();

	void Bind(LPCSOCKADDR psa);
	void Listen();
	int Connect(LPCSOCKADDR psa); // if connect fail return SOCKET_ERROR;
	BOOL Accept(CBlockingSocket& s, LPSOCKADDR psa);

	int Send(const char* pch, const int nSize);
	int Write(const char* pch, const int nSize);
	int Receive(char* pch, const int nSize);
	int Read(char* pch, const int nSize);
	int SendDatagram(const char* pch, const int nSize, LPCSOCKADDR psa);
	int ReceiveDatagram(char* pch, const int nSize, LPSOCKADDR psa);

	int GetPeerAddr(LPSOCKADDR psa);
	void GetSockAddr(LPSOCKADDR psa);
	static CSockAddr GetHostByName(const char* pchName, const USHORT ushPort = 0);
	static const char* GetHostByAddr(LPCSOCKADDR psa);

	operator SOCKET(){ return m_hSocket; }

	CBlockingSocket & operator= (const CBlockingSocket &rhs){
		m_hSocket = rhs.m_hSocket;
		return *this;
	}
};

#endif