/******************************************************************************
 *
 * Copyright (c) 1999	TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *	Socket.cpp
 *
 *****************************************************************************/

// SOL++2000
// 1999.06.12 Added a sendAll method 
// 1999.08.22 Added a printf method.

#include <sol\Socket.h>
#include <sol\InvalidSocketException.h>
#include <time.h>


Socket::Socket() 
{
    domain   = AF_INET;
    type     = SOCK_STREAM;
    protocol = 0;
	fd       = INVALID_SOCKET;
	buffer   = null;
}


Socket::Socket(int domain, int type,  int protocol) 
{
    this -> domain = domain;
    this -> type   = type;
    this -> protocol = protocol;
	buffer = null;

    fd = ::socket(domain, type, protocol);

	if(fd == INVALID_SOCKET) {
		throw InvalidSocketException("Failed to create a socket",
								::GetLastError());
	}
}

Socket:: ~Socket() 
{
    if(fd != INVALID_SOCKET) {
        ::closesocket(fd);
    }

	delete [] buffer; //1999.08.22
}


SOCKET Socket::accept(sockaddr* addr, int* addrlen)
{	
	SOCKET newSocket = INVALID_SOCKET;
	if(fd != INVALID_SOCKET) {
		newSocket = ::accept(fd, addr, addrlen);
	}
	return newSocket;
}
 
// 1999.08.10 int address -> unsigned long
int Socket::bind(int port, unsigned long address) 
{
	int rc = Error;
    if(fd != INVALID_SOCKET && domain == AF_INET) {
		sockaddr_in inet;
        memset(&inet, 0, sizeof(inet));
        inet.sin_family = domain;
        inet.sin_port   = htons(port);
        inet.sin_addr.s_addr = htonl(address);
        rc = ::bind(fd, (sockaddr*)&inet, sizeof(inet));
    }
	return rc;
}

// 1999.08.10
int Socket::bind(InetAddress& address)
{
	int rc = Error;
    if(fd != INVALID_SOCKET && domain == AF_INET) {
		sockaddr_in* addr = address.getAddress();
		rc = ::bind(fd, (sockaddr*)addr, address.getSize());
    }
	return rc;
	
}


int Socket::bind(sockaddr* inet, int size) 
{
	int rc = Error;
    if(fd != INVALID_SOCKET && domain == AF_INET) {
		rc = ::bind(fd, inet, size);
    }
	return rc;
}



BOOL Socket::close() 
{
	BOOL rc = False;
    if(fd != INVALID_SOCKET) {
        ::closesocket(fd);
        fd = INVALID_SOCKET;
        rc= True;
    }
	return rc;
}


SOCKET Socket::create(int domain, int type, int protocol) 
{
    if(fd != INVALID_SOCKET) {
        return fd;
    }
    else {
        this -> domain = domain;
        this -> type   = type;
        this -> protocol = protocol;
        fd = ::socket(domain, type, protocol);
        return fd;
    }
}

int Socket::getOption(int optname, char* optval, int* optlen) 
{
    int rc = Error;
    if(fd != INVALID_SOCKET) {
        rc = ::getsockopt(fd, SOL_SOCKET, optname, optval, optlen);
    }
    return rc;
}


int Socket::getName(InetAddress& address) 
{
	sockaddr_in* addr = address.getAddress();
	return getName(addr);
}

int Socket::getName(sockaddr_in* addr) 
{
    int rc = Error;
    memset(addr, 0, sizeof(sockaddr_in));
    if(domain == AF_INET && addr && fd != INVALID_SOCKET) {
        int addrlen = sizeof(sockaddr_in);
        rc = ::getsockname(fd, (sockaddr*)addr, &addrlen);
    }
    return rc;
}


int Socket::getPeerName(InetAddress& address)
{
	sockaddr_in* addr = address.getAddress();
	return getPeerName(addr);
}


int Socket::getPeerName(sockaddr_in* addr) 
{
    int rc = Error;
    memset(addr, 0, sizeof(sockaddr_in));
    if(domain == AF_INET && addr && fd != INVALID_SOCKET ) {
        int addrlen = sizeof(sockaddr_in);
        rc = ::getpeername(fd, (sockaddr*)addr, &addrlen);
    }
    return rc;
}


int Socket::isFDSet(fd_set* fdset) 
{
    if(fdset) {
        return FD_ISSET(fd, fdset);
    }
    else {
        return 0;
    }
}


int Socket::listen(int backlog) 
{
	int rc = Error;
	if(fd != INVALID_SOCKET) {
		rc = ::listen(fd, backlog);
	}
	return rc;
}


// 1999.08.22 
int Socket::printf(const char* format,...)
{
	if (buffer == null) {
		buffer = new char[1024];
	}

	va_list pos;
	va_start(pos, format);
	vsprintf(buffer, format, pos);
	va_end(pos);

	return sendAll(buffer);
}


int Socket::recv(char* buff, int len, int flag) 
{
	int rc = -1;
    if(fd != INVALID_SOCKET) {
        rc = ::recv(fd, buff, len, flag);
    }
	return rc;
}
    
int Socket::send(const char* buff, int len, int flag) 
{
	int rc = -1;
    if(fd != INVALID_SOCKET) {
        rc = ::send(fd, buff, len, flag);
    }
	return rc;
}

// 1999.06.12 to-arai
// 1999.08.16 Added a timeout argument. Specify the timeout in second.
int Socket::sendAll(const char* buff, int len, int flag, long timeout)
{
	int	rc = -1;
	int sentBytes = 0;

    if(fd != INVALID_SOCKET) {
		const char* ptr = buff;
		int  orglen = len;
		long startTime = time(null);
		while (len >0) {
			sockaddr_in peer;

			if( getPeerName(&peer) == SOCKET_ERROR) {
				rc = -3;
				break;
			}
			if (peer.sin_port == 0) {
				rc = -3;
				break;
			}
			
			long currentTime = time(null);

			if ((currentTime - startTime) > timeout) {
				if (sentBytes < orglen) { 
					return rc = -2;	// timeout;
				}
				if (sentBytes == orglen) {
					return sentBytes;
				}
			}

			if (fd == INVALID_SOCKET) {
				rc = -2;
				break;
			}
			int size = ::send(fd, ptr, len, flag);

			if (size < 0 && len <=0) {	// 1999.08.14
				break;
			}

			if (size >0) {
				sentBytes += size;
				ptr += size;
				len -= size;
			}
		}

		return sentBytes;
    }
	return rc;
}


int Socket::sendAll(const char* string) 
{
	int rc = 0;
    if (string) {
		rc = sendAll(string, strlen(string), 0);
	}
	return rc;
}

    
int Socket::sendAll(String& string) 
{
	const char* text = (char*)string;
	int rc = 0;
	if (text) {
		rc = sendAll(text, strlen(text), 0);
	}
	return rc;
}
	

int Socket::sendAll(StringBuffer& buffer) 
{
	const char* text = buffer.getBuffer();
	int rc = 0;
	if (text) {
		rc = sendAll(text, strlen(text), 0);
	}
	return rc;
}



void Socket::setFDSet(fd_set* fdset) 
{
    if(fdset) {
        FD_SET(fd, fdset);
    }
}



int Socket::setOption(int optname, char* optval, int optlen) 
{
    int rc = Error;
    if(fd != INVALID_SOCKET) {
        rc = ::setsockopt(fd, SOL_SOCKET, optname, optval, optlen);
    }
    return rc;
}


int Socket::shutdown(int how) 
{
	int rc = Error;
    if(fd != INVALID_SOCKET) {
        rc = ::shutdown(fd, how);
    }
	return rc;
   
}


int Socket::select(HWND hwnd, unsigned int msg, long event)
{ 
    int rc = Error;
    if(fd != INVALID_SOCKET) {
		rc = ::WSAAsyncSelect(fd, hwnd, msg, event);
	}
	return rc;
}

