//  Copyright (c) 2007, Monju System Architects
//
//  Released under the Monju-Public copyright license:
//
//  Permission is hereby granted, free of charge, to any person or entity
//  obtaining a copy of this software and associated documentation files
//  (the "Software"), to deal in the Software without restriction,
//  including without limitation the rights to use, copy, modify, merge,
//  publish, distribute, sublicense, and/or sell copies and modifications
//  of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be
//  included in all copies or substantial portions of published, sold, or
//  ownership-transferred versions of the Software. The notices are not
//  required for binary-only releases or binary-only derivatives of the
//  Software, or documentation provided with binary-only releases and
//  binary-only derivatives.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
//  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
//  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.




//  INCLUDES.
#ifndef _WIN32
	#include <unistd.h>
#endif

#include <stdlib.h>
#include <string.h>

#include "public/socket.h"
#include "public/file.h"



//  TYPE DEFINITIONS.
#ifdef _WIN32
	typedef int ssize_t;
#endif



//  acceptSocket()
bool acceptSocket(SOCKET_TYPE& listener, SOCKET_TYPE* pNewSocket, CLIENT_ADDRESS_TYPE* pClientAddress) {
    CLIENT_ADDRESS_TYPE clientAddress;
	socklen_t           clientLength    = sizeof(clientAddress);
	SOCKET_TYPE         newSocket       = accept(listener, (struct sockaddr*) &clientAddress, &clientLength);
	#ifdef _WIN32
		if (newSocket == INVALID_SOCKET) {
		    return false;
		}
	#else
	    if (newSocket < 0) {
	        return false;
	    }
	#endif
	if (pClientAddress != NULL) {
	    *pClientAddress = clientAddress;
	}
	*pNewSocket = newSocket;
	return true;
}
	


//  bindSocket()
void bindSocket(SOCKET_TYPE& theSocket, PORT_TYPE port) {
    bindSocket(theSocket, NULL, port);
}
void bindSocket(SOCKET_TYPE& theSocket, const char* pAddressString, PORT_TYPE port) {

	//  CREATE ADDREsS INFO.
	struct sockaddr_in address;
	memset(&address, sizeof(address), 0);
	if (pAddressString == NULL) {
	    address.sin_addr.s_addr = INADDR_ANY;
	}
	else {
	    address.sin_addr.s_addr = inet_addr(pAddressString);
	}	    
	address.sin_family  = AF_INET;
	address.sin_port    = htons(port);

	//BIND.
	if (0 != bind(theSocket, (const sockaddr*)&address, sizeof(address))) {
	    throw("bind() failed.");
	}
}

//  closeSocket()
void closeSocket(SOCKET_TYPE& theSocket) {
	#ifdef _WIN32
	    closesocket(theSocket);
	#else
	    close(theSocket);
	#endif
}



//  createTcpSocket()
SOCKET_TYPE createTcpSocket() {
    return socket(AF_INET, SOCK_STREAM, 0);
}



//  listenSocket()
void listenSocket(SOCKET_TYPE& theSocket) {
    listen(theSocket, 5);
}



//  readSocket()
bool readSocket(SOCKET_TYPE& theSocket, char* pBuffer, int* pInOutAmount, double timeout) {
    setReadTimeoutSocket(theSocket, timeout);
    ssize_t amount = recv(theSocket, pBuffer, *pInOutAmount, 0);
    if (amount < 0) {
        return false;
    }
    *pInOutAmount = amount;
    return true;
}
bool readSocket(SOCKET_TYPE& theSocket, char* pBuffer, int* pInOutAmount, int timeout) {
    setReadTimeoutSocket(theSocket, timeout);
    ssize_t amount = recv(theSocket, pBuffer, *pInOutAmount, 0);
    if (amount < 0) {
        return false;
    }
    *pInOutAmount = amount;
    return true;
}
bool readSocket(SOCKET_TYPE& theSocket, char* pBuffer, int* pInOutAmount) {
    setBlockingSocket(theSocket);
    ssize_t amount = recv(theSocket, pBuffer, *pInOutAmount, 0);
    if (amount < 0) {
        return false;
    }
    *pInOutAmount = amount;
    return true;
}



//  setBlockingSocket()
void setBlockingSocket(SOCKET_TYPE& theSocket) {
	#ifdef _WIN32
	    u_long mode = 0;
	    ioctlsocket(theSocket, FIONBIO, &mode);
	#else
	    setBlockingFile(theSocket);
	#endif
}



//  setNonBlockingSocket()
void setNonBlockingSocket(SOCKET_TYPE& theSocket) {
	#ifdef _WIN32
	    u_long mode = 1;
	    ioctlsocket(theSocket, FIONBIO, &mode);
	#else
	    setNonBlockingFile(theSocket);
	#endif
}



//  setReadTimeoutSocket()
void setReadTimeoutSocket(SOCKET_TYPE& theSocket, double seconds) {
	#ifdef _WIN32
	    DWORD secondsInt    = (DWORD)seconds;
	    DWORD milliseconds  = (DWORD)((seconds - (double)secondsInt) * 1000.0) ;
	    if (milliseconds < 0) {
	        milliseconds = 0;
	    }
	    milliseconds += secondsInt * 1000;
	    setsockopt(theSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&milliseconds, sizeof(DWORD));
	#else
		struct timeval timeValue;
	    int secondsInt = (int)seconds;
	    int uSeconds   = (int)((seconds - (double)secondsInt) * 1000000.0) ;
	    if (uSeconds < 0) {
	        uSeconds = 0;
	    }
	    timeValue.tv_sec  = secondsInt;
	    timeValue.tv_usec = uSeconds;
	    setsockopt(theSocket, SOL_SOCKET, SO_RCVTIMEO, &timeValue, sizeof(SOCKET_TYPE));
	#endif
}
void setReadTimeoutSocket(SOCKET_TYPE& theSocket, int seconds) {
	#ifdef _WIN32
	    DWORD milliseconds = seconds * 1000;
	    setsockopt(theSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&milliseconds, sizeof(DWORD));
	#else
	    struct timeval timeValue;
	    timeValue.tv_sec  = seconds;
	    timeValue.tv_usec = 0;
	    setsockopt(theSocket, SOL_SOCKET, SO_RCVTIMEO, &timeValue, sizeof(struct timeval));
	#endif
}



//  writeSocket()
void writeSocket(SOCKET_TYPE& theSocket, const char* pString) {
    send(theSocket, pString, strlen(pString), 0);
}






