// Sun, 18 Jun 2000
// Y.Takagi

#ifdef WIN32
#include <winsock.h>
#else
#include <net/socket.h>
#include <net/netdb.h>
#endif	// WIN32

#include <cstdio>
#include "Socket.h"
#include "SocketStream.h"

#ifndef INADDR_NONE
#define INADDR_NONE		0xffffffff
#endif

#ifdef WIN32
	#define EADDRINUSE	WSAEADDRINUSE
	#define errno		WSAGetLastError()
#else
	#include <errno.h>
#endif

#ifdef WIN32
class WIN32SOCKET {
public:
	WIN32SOCKET()
	{
		WSADATA winsockdata;
		WSAStartup(MAKEWORD(1,1), &winsockdata);
	}
	~WIN32SOCKET()
	{
		WSACleanup();
	}
};

WIN32SOCKET win32socket;
#endif

Socket::Socket(const char *host, int port)
	: __sock(-1), __is(NULL), __os(NULL), __error(false)
{
	__host         = host;
	__port         = port;
	__localPort    = -1;
	__error_msg[0] = '\0';

	open();
}

Socket::Socket(const char *host, int port, int localPort)
	: __sock(-1), __is(NULL), __os(NULL), __error(false)
{
	__host         = host;
	__port         = port;
	__localPort    = localPort;
	__error_msg[0] = '\0';

	open();
}

Socket::~Socket()
{
	close();
	if (__is) {
		delete __is;
	}
	if (__os) {
		delete __os;
	}
}

istream &Socket::getInputStream()
{
	if (__is == NULL) {
		__is = new isocketstream(this);
	}
	return *__is;
}

ostream &Socket::getOutputStream()
{
	if (__os == NULL) {
		__os = new osocketstream(this);
	}
	return *__os;
}

void Socket::open()
{
	if (__sock == -1 && !__error) {

		sockaddr_in sin;
		memset(&sin, 0, sizeof(sin));

		unsigned long inaddr;
		hostent *host_info;

		if ((inaddr = inet_addr(__host.c_str())) != INADDR_NONE) {
			memcpy(&sin.sin_addr, &inaddr, sizeof(inaddr));
			sin.sin_family = AF_INET;
		} else if ((host_info = gethostbyname(__host.c_str())) != NULL) {
			memcpy(&sin.sin_addr, host_info->h_addr, host_info->h_length);
			sin.sin_family = host_info->h_addrtype;
		} else {
			sprintf(__error_msg, "gethostbyname failed. errno = %d", errno);
			__error = true;
			return;
		}

		if ((__sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
			sprintf(__error_msg, "socket failed. errno = %d", errno);
			__error = true;
		} else {
			if (__localPort >= 0) {
				sockaddr_in cin;
				memset(&cin, 0, sizeof(cin));
				cin.sin_family = AF_INET;
				cin.sin_port   = htons(__localPort);
				if (::bind(__sock, (sockaddr *)&cin, sizeof(cin)) != 0) {
					sprintf(__error_msg, "bind failed. errno = %d", errno);
					::closesocket(__sock);
					__sock = -1;
					__error = true;
				}
			}
			sin.sin_port = htons(__port);
			if (::connect(__sock, (sockaddr *)&(sin), sizeof(sin)) != 0) {
				sprintf(__error_msg, "connect failed. errno = %d", errno);
				::closesocket(__sock);
				__sock = -1;
				__error = true;
			}
		}
	}
}

void Socket::close()
{
	if (__sock != -1) {
		::shutdown(__sock, 2);
		::closesocket(__sock);
		__sock = -1;
	}
}

bool Socket::fail() const
{
	return __sock == -1 || __error;
}

bool Socket::good() const
{
	return !fail();
}

bool Socket::operator !() const
{
	return fail();
}

int Socket::read(char *buffer, int size, int flags)
{
	if (fail()) {
		size = 0;
	} else {
		size = ::recv(__sock, buffer, size, flags);
		if (size <= 0) {
			sprintf(__error_msg, "recv failed. errno = %d", errno);
			__error = true;
			close();
		}
	}
	return size;
}

int Socket::write(const char *buffer, int size, int flags)
{
	if (fail()) {
		size = 0;
	} else {
		size = ::send(__sock, buffer, size, flags);
		if (size <= 0) {
			sprintf(__error_msg, "send failed. errno = %d", errno);
			__error = true;
			close();
		}
	}
	return size;
}
