//============================================================================
/// Summary  : Simple Socket
/// Filename : simpleSocket.cc
/// Author   : reed@bluefinrobotics.com
/// Project  : Simple Socket Library
/// Revision : 0.0
/// Created  : 2002.02.26
/// Modified : 2002.02.26
//============================================================================

#include "SimpleSocket.h"

SimpleSocket::SimpleSocket()
{
#ifdef DEBUG_SS
    Syslog::write("SS: Executing constructor...");
#endif
    networkAddress.ready = false;
    networkAddress.descriptor = -1;
    networkAddress.addressLength = sizeof(networkAddress.address);
    networkAddress.optionValue = 1;
    networkAddress.optionValueLength = sizeof(networkAddress.optionValue);
}

SimpleSocket::~SimpleSocket()
{
#ifdef DEBUG_SS
    Syslog::write("SS: Executing destructor...");
#endif
    SimpleSocket::destroy();
}

/// Create a TCP/UDP socket.
int SimpleSocket::create(Type type, Domain domain, Protocol protocol)
{
#ifdef DEBUG_SS
    Syslog::write("SS::create: create socket...");
    Syslog::write("SS::create: Type = %d, Domain = %d, Protocol = %d",type,domain,protocol);
#endif
    // Create socket.
    errno = 0;
    networkAddress.descriptor = socket(domain, type, protocol);
    if ( networkAddress.descriptor < 0 ) 
    {
	networkAddress.ready = false;
	Syslog::write("Error create socket failed (errno=%d).",errno);
    }

    return(networkAddress.descriptor);
}


/// Create UDP Socket for Datagrams.
int SimpleSocket::createInetDgram()
{
#ifdef DEBUG_SS
    Syslog::write("SS:createInetDgram: create socket Internet Datagram...");
#endif
    return(SimpleSocket::create(DGRAM));
}

/// Close (Destroy) Socket using Socket Descriptor.
int SimpleSocket::destroy()
{
    int returnVal = 0;
#ifdef DEBUG_SS
    Syslog::write("SS:destroy: destroy (close) socket %d...",networkAddress.descriptor);
#endif
    // Close (destroy) socket.
    if(networkAddress.descriptor > 0)
    {
	networkAddress.ready = false;
	returnVal = close(networkAddress.descriptor);
	networkAddress.descriptor = -1;
	return(returnVal);
    }
    else
    {
	// Error, a socket doesn't exist.
	Syslog::write("Attempt to close non-existing socket, no action taken!");
	return (-1);
    }
}


/// Set Socket Options
int SimpleSocket::setOptions(int level,
			     int options,
			     const void *optionValue,
			     int optionValueLength)
{
#ifdef DEBUG_SS
    Syslog::write("SS:setOptions: ...");
#endif
    int returnVal=0;
    errno = 0;

    returnVal = setsockopt(networkAddress.descriptor,
			   level,
			   options,
			   optionValue,
			   optionValueLength);
    if(returnVal < 0)
    {
        Syslog::write("Error setting socket options (errno=%d)!\n",errno);
	SimpleSocket::destroy();
    }
    return(returnVal);
}

/// Set Socket Broadcast
int SimpleSocket::setOptionsDgramBroadcast()
{
#ifdef DEBUG_SS
    Syslog::write("SS:setOptionsDgramBroadcast: ...");
#endif
    // non null item
    int intVal=0;

    // set socket options to broadcast
    return(setOptions(SOL_SOCKET, 
		      SO_BROADCAST,
		      &(networkAddress.optionValue),
		      networkAddress.optionValueLength));
}


/// Set Socket Address
int SimpleSocket::setAddress( unsigned short port,
			      unsigned long ipAddress,
			      unsigned short family)
{
#ifdef DEBUG_SS
    Syslog::write("SS:setAddress: port      = %04d...",port);
    Syslog::write("SS:setAddress: ipAddress = 0x%08d...",ipAddress);
    Syslog::write("SS:setAddress: family    = %02d...",family);
#endif
    networkAddress.address.sin_family = family;
    networkAddress.address.sin_addr.s_addr = htonl(ipAddress);
    networkAddress.address.sin_port = htons(port);
#ifdef DEBUG_SS
    Syslog::write("SS:setAddress: address family = 0x%X...",networkAddress.address.sin_family);
    Syslog::write("SS:setAddress: address s_addr = 0x%08X...",networkAddress.address.sin_addr.s_addr);
    Syslog::write("SS:setAddress: address port   = 0x%04X...",networkAddress.address.sin_port);
#endif
    // If a socket descriptor exists, set socket to ready state.
    if(networkAddress.descriptor > 0)
    {
	networkAddress.ready = true;
    }
    else
    {
	networkAddress.ready = false;
    }

    return(0);
}

/// Set Socket Address
int SimpleSocket::setAddress( unsigned short port,
			      char *ipAddressString,
			      unsigned short family)
{
    int returnVal = 0;
    unsigned long hexIpAddress = 0;

    hexIpAddress = SimpleSocket::string2hexIpAddress(ipAddressString);

#ifdef DEBUG_SS
    Syslog::write("SS:setAddress: port            = %4d.",port);
    Syslog::write("SS:setAddress: ipAddressString = %s." ,ipAddressString);
    Syslog::write("SS:setAddress: ipAddressLong   = 0x%08X.",hexIpAddress);
    Syslog::write("SS:setAddress: family          = %2d.",family);
#endif
    returnVal = SimpleSocket::setAddress(port, hexIpAddress, family);

    return(returnVal);
}


/// Create Broadcast Datagram Socket
/// returns on success : socket descriptor
/// returns on failure : -1
int SimpleSocket::createBroadcastDatagramSend( unsigned short port, unsigned long ipAddress)
{
#ifdef DEBUG_SS
    Syslog::write("SS:createBroadcastDatagramSend: port = %4d...,",port);
    Syslog::write("SS:createBroadcastDatagramSend: ipAddress = 0x%08x...",ipAddress);
#endif
    int returnVal=0;

    returnVal = SimpleSocket::createInetDgram();
    if(returnVal < 0)
    {
	Syslog::write("Unable to create Internet Datagram Socket!");
	return (returnVal);
    }
    else
    {
	// Received socket descriptor on createInetDgram success.
	networkAddress.descriptor = returnVal;
    }

    // setup the socket for broadcasting
    returnVal = SimpleSocket::setOptionsDgramBroadcast();
    if(returnVal < 0)
    {
	// close the socket on failure to setup socket
	returnVal = SimpleSocket::destroy();
	return(-1);
    }

    /// Note that socket connection exists.
    if (networkAddress.descriptor > 0)
    { 
#ifdef DEBUG_SS
	Syslog::write("SS:createBroadcastDatagramSend: port      = %4d.",port);
	Syslog::write("SS:createBroadcastDatagramSend: ipAddress = 0x%0x.",ipAddress);
	Syslog::write("SS:createBroadcastDatagramSend: family    = %2d.",INET);
#endif
	/// set socket address
	returnVal = SimpleSocket::setAddress( port,
					      ipAddress,
					      INET);
    }

    return(returnVal);
}

// Create a DataGram Broadcast port using IP address string.
int SimpleSocket::createBroadcastDatagramSend( unsigned short port, char *ipAddressString)
{
    int returnVal = 0;
    unsigned long hexIpAddress = 0;

    hexIpAddress = SimpleSocket::string2hexIpAddress(ipAddressString);
#ifdef DEBUG_SS
    Syslog::write("SS:createBroadcastDatagramSend: port            = %04d.",port);
    Syslog::write("SS:createBroadcastDatagramSend: ipAddressString = %s.",ipAddressString);
    Syslog::write("SS:createBroadcastDatagramSend: hexIpAddress    = 0x%08x.", hexIpAddress);
#endif
    returnVal = SimpleSocket::createBroadcastDatagramSend(port, hexIpAddress);

    return(returnVal);
}


/// send to socket explicit
int SimpleSocket::sendTo(const void *message,
			 int messageLength,
			 int flags)
{
    int bytesSent=0;
    errno = 0;

#ifdef DEBUG_SS

    //Syslog::write("SS:sendTo: message = %s.",message);
    Syslog::write("SS:sendTo: messageLength = %d.",messageLength);
    Syslog::write("SS:sendTo: flags = %d.",flags);
    Syslog::write("SS:sendTo: networkAddress.descriptor              = %d.",networkAddress.descriptor);
    Syslog::write("SS:sendTo: networkAddress.address.sin_family      = %d (0x%X).",
		  networkAddress.address.sin_family, networkAddress.address.sin_family);
    Syslog::write("SS:sendTo: networkAddress.address.sin_addr.s_addr = %d (0x%X).",
		  networkAddress.address.sin_addr.s_addr, networkAddress.address.sin_addr.s_addr);
    Syslog::write("SS:sendTo: networkAddress.address.sin_port        = %d (0x%X).",
		  networkAddress.address.sin_port, networkAddress.address.sin_port);
    Syslog::write("SS:sendTo: networkAddress.addressLength           = %d.",networkAddress.addressLength);
#endif
    bytesSent = sendto( networkAddress.descriptor,
			message,
			messageLength,
			flags,
			(const struct sockaddr *)&networkAddress.address,
			networkAddress.addressLength);
    if(bytesSent < 0)
    {
	Syslog::write("sendTo failed (errno=%d).",errno);
    }
    return(bytesSent);
}

/// send to socket simple
int SimpleSocket::sendDatagram(const void *message,
			       int messageLength)
{
    int bytesSent=0;

#ifdef DEBUG_SS
    Syslog::write("SS:sendDatagram: messageLength = %d.\n",messageLength);
//     Syslog::write("SS:sendDatagram: networkAddress.descriptor = 0x%X (%d).",
// 		  networkAddress.descriptor,networkAddress.descriptor);
//     Syslog::write("SS:sendDatagram: networkAddress.address.family = 0x%X (%d)",
// 		  networkAddress.address.sin_family,
// 		  networkAddress.address.sin_family);
//     Syslog::write("SS:sendDatagram: networkAddress.address.addr = 0x%X (%d)",
// 		  networkAddress.address.sin_addr.s_addr,
// 		  networkAddress.address.sin_addr.s_addr);
//     Syslog::write("SS:sendDatagram: networkAddress.address.port = 0x%X (%d)",
// 		  networkAddress.address.sin_port,
// 		  networkAddress.address.sin_port);
#endif

    bytesSent = sendTo( message,
			messageLength,
			0); // Flags

    return(bytesSent);
};


/// Convert string IP address to a hex address, i.e. "255.255.255.255" --> 0xFFFFFFFF.
unsigned long SimpleSocket::string2hexIpAddress(char *ipAddressString)
{
    int returnVal=0;
    unsigned int network1=0, network2=0, subnet=0, host=0;
    unsigned long hexIpAddress=0;

    // Get a copy of the ip address string.
    strncpy(&networkAddress.ipAddressString[0], ipAddressString, maxIpAddressStringSize);
    // Scan in the IP Address Components.
    returnVal=sscanf(ipAddressString,"%d.%d.%d.%d", &network1, &network2, &subnet, &host);
    // Generate the hex IP Address.
    hexIpAddress=network1;
    hexIpAddress<<=8;
    hexIpAddress+=network2;
    hexIpAddress<<=8;
    hexIpAddress+=subnet;
    hexIpAddress<<=8;
    hexIpAddress+=host;

#ifdef DEBUG_SS
    Syslog::write("SS:string2hexIpAddress(%s) = 0x%8X.",ipAddressString,hexIpAddress);
#endif

    return(hexIpAddress);
}
