// networking.inline.hpp
// Copyright (C) 2006-2009 MicroNeil Research Corporation.
//
// This program is part of the MicroNeil Research Open Library Project. For
// more information go to http://www.microneil.com/OpenLibrary/index.html
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA
//==============================================================================

// Inlined methods for Networking module. See networking.hpp for notes.

////////////////////////////////////////////////////////////////////////////////
// Platform Specific

//// Windows platform

#if defined(WIN32) || (WIN64)

inline int Networking::getLastError() {                                         // In windows you get the last error
    return WSAGetLastError();                                                   // from WSAGetLastError();
}

inline int Networking::setNonBlocking(hSocket socket) {                         // Set a winsock to non-blocking
    unsigned long nonblocking = 1;                                              // Create a flag...
    int result = 0;
    if(0 != ioctlsocket(socket, FIONBIO, &nonblocking)) {                       // Set the state of the socket.
        result = -1;                                                            // If that fails then return -1.
    }
    return result;                                                              // Show 'em my motto!
}

inline int Networking::closeSocket(hSocket socket) {                            // Close a socket in winsock
    return closesocket(socket);                                                 // wraps closesocket().
}

inline bool Networking::WouldBlock(int ErrorCode) {                             // ErrorCode matches [WSA]EWOULDBLOCK.
    return (WSAEWOULDBLOCK == ErrorCode);
}

inline bool Networking::InProgress(int ErrorCode) {                             // ErrorCode matches [WSA]EINPROGRESS.
    return(                                                                     // [WSA]EALREADY also returns true.
      WSAEINPROGRESS == ErrorCode ||                                            // In fact, on Win* platforms we could
      WSAEALREADY == ErrorCode ||                                               // get any of these when retesting
      WSAEWOULDBLOCK == ErrorCode ||                                            // open() for a connection.
      WSAEINVAL == ErrorCode
    );
}

inline bool Networking::IsConnected(int ErrorCode) {                            // ErrorCode matches [WSA]EISCONN.
    return(WSAEISCONN == ErrorCode);
}

#else

//// GNU platform

inline int Networking::getLastError() {                                         // In GNU you get the last error
    return errno;                                                               // from errno;
}

inline int Networking::setNonBlocking(hSocket socket) {                         // Set a socket to non-blocking
    int flags, result;                                                          // Grab a place to hold the flags.
    flags = fcntl(socket, F_GETFL, 0);                                          // Get the current flags.
    result = fcntl(socket, F_SETFL, flags | O_NONBLOCK);                        // Set the NONBLOCK flag & return.
    return result;                                                              // Return the result.
}

inline int Networking::closeSocket(hSocket socket) {                            // Close a socket in GNU
    return close(socket);                                                       // wraps close().
}

inline bool Networking::WouldBlock(int ErrorCode) {                             // ErrorCode matches [WSA]EWOULDBLOCK.
    return (EWOULDBLOCK == ErrorCode);
}

inline bool Networking::InProgress(int ErrorCode) {                             // ErrorCode matches [WSA]EINPROGRESS.
    return(                                                                     // [WSA]EALREADY also returns true.
      EINPROGRESS == ErrorCode ||
      EALREADY == ErrorCode
    );
}

inline bool Networking::IsConnected(int ErrorCode) {                            // ErrorCode matches [WSA]EISCONN.
    return(EISCONN == ErrorCode);
}

#endif

// End Platform Specific
////////////////////////////////////////////////////////////////////////////////
// Begin Platform Agnostic

//// class IP4Address //////////////////////////////////////////////////////////

inline IP4Address::IP4Address():IP(0){}                                         // Blank constructor IP = 0.0.0.0
inline IP4Address::IP4Address(const unsigned long int newIP):IP(newIP){}        // Constructor given unsigned long
inline IP4Address::IP4Address(const IP4Address& newIP):IP(newIP.IP){}           // Constructor given an IP4Address

inline IP4Address::IP4Address(const char* newIP) { (*this) = newIP; }           // Construcing with a cstring.
inline IP4Address::IP4Address(const string& newIP) { (*this) = newIP; }         // Constructing with a cppstring.

inline IP4Address&
  IP4Address::operator=(const unsigned long int Right) { IP = Right; }          // Convert from unsigned long int.

inline IP4Address& IP4Address::operator=(const char* Right) {                   // Convert from c string.
    IP = ntohl(inet_addr(Right));
}

inline IP4Address& IP4Address::operator=(const string& Right) {                 // Convert from cpp string.
    IP = ntohl(inet_addr(Right.c_str()));
}

inline bool IP4Address::operator<(const IP4Address Right) const {               // < Comparison.
    return (IP < Right.IP);
}

inline bool IP4Address::operator>(const IP4Address Right) const {               // > Comparison.
    return (IP > Right.IP);
}

inline bool IP4Address::operator==(const IP4Address Right) const {              // == Comparison.
    return (IP == Right.IP);
}

inline bool IP4Address::operator!=(const IP4Address Right) const {              // != Comparison.
    return (IP != Right.IP);
}

inline bool IP4Address::operator<=(const IP4Address Right) const {              // <= Comparison.
    return (IP <= Right.IP);
}

inline bool IP4Address::operator>=(const IP4Address Right) const {              // >= Comparison.
    return (IP >= Right.IP);
}

//// class SocketAddress ///////////////////////////////////////////////////////

inline void SocketAddress::clear() {
    memset(&Address, 0, sizeof(Address));                                       // Zero out the address strcuture
    Address.sin_family = AF_INET;                                               // Internet Address Family ip4
    Address.sin_addr.s_addr = htonl(INADDR_ANY);                                // Any IP address
    Address.sin_port = 0;                                                       // Zero means any port.
}

inline SocketAddress::SocketAddress() {                                         // Constructor sets up w/ wildcards
    clear();                                                                    // Conveniently, we can use clear() :-)
}

inline struct sockaddr_in* SocketAddress::getPtr_sockaddr_in() {                // Returns a pointer to sockaddr_in.
    return &Address;                                                            // Simply return it's address.
}

inline struct sockaddr* SocketAddress::getPtr_sockaddr() {                      // Returns a pointer to sockaddr.
    return (struct sockaddr*) &Address;
}


inline socklen_t SocketAddress::getAddressSize() {
    return sizeof(Address);                                                     // Return the size of the structure.
}

inline void SocketAddress::setAddress(unsigned long ipAddress) {                // Set the IP address from an unsigned int
    Address.sin_addr.s_addr = htonl(ipAddress);                                 // Convert to network order and assign.
}

inline void SocketAddress::setAddress(char* ipString) {                         // Set the IP address from a cstring
    Address.sin_addr.s_addr = inet_addr(ipString);                              // Convert to number and assign.
}

inline unsigned long SocketAddress::getAddress() {                              // Get the IP address as an unsigned int
    return ntohl(Address.sin_addr.s_addr);                                      // Convert to host order and return.
}

inline void SocketAddress::setPort(unsigned short port) {                       // Set the port address from an int
    Address.sin_port = htons(port);                                             // Convert to network order and set.
}

inline void SocketAddress::setPort(char* port) {                                // Set the port address from a cstring
    setPort(atoi(port));                                                        // Convert to int and set.
}

inline unsigned short SocketAddress::getPort() {                                // Get the port address as an unsigned int
    return ntohs(Address.sin_port);                                             // Convert to host order and return.
}

inline const char* SocketAddress::getPort(char* str) {                          // Get the port address into a cstring.
    if(NULL == str) {                                                           // If the caller did not provide a
        str = PortStringBuffer;                                                 // buffer to use then we will use ours.
    }
    sprintf(str,"%d",getPort());                                                // Get the port and convert to cstring.
    return str;                                                                 // Return the string we got.
}

//// class Socket //////////////////////////////////////////////////////////////

inline Socket::Socket() :                                                       // When starting up we are
  Handle(INVALID_SOCKET), OpenSucceeded(false) {                                // not yet valid.
}

inline Socket::~Socket() {                                                      // When shutting down, be sure
    close();                                                                    // any open socket is closed.
}

inline void Socket::close() {                                                   // When we close,
    if(INVALID_SOCKET != Handle) {                                              // If the handle is open then
        if(Network.closeSocket(Handle)) {                                       // close the handle and check for error.
            LastError = Network.getLastError();                                 // If there was an error record it.
            if(!Network.WouldBlock(LastError)) {                                // If the error was not WOULDBLOCK
                throw Networking::ControlError(                                 // then throw a ControlError exception.
                  Network.DescriptiveError(
                    "Socket::close()", LastError));
            }
        } else {                                                                // If there was no error then
            LastError = 0;                                                      // reset the LastError value.
        }
        Handle = INVALID_SOCKET;                                                // and reset the handle to INVALID.
        NonBlocking = false;                                                    // The default is Blocking.
        OpenSucceeded = false;                                                  // After close, forget we opened.
    }
}

inline hSocket Socket::getHandle() {                                            // Returns the current Socket handle.
    return Handle;
}

inline bool Socket::isNonBlocking() {                                           // Returns true if socket is NonBlocking
    return NonBlocking;
}

inline void Socket::makeNonBlocking() {                                         // Sets the socket to NonBlocking mode.
    if(0 > Network.setNonBlocking(Handle)) {                                    // Feed the call through Network.
        LastError = Network.getLastError();                                     // If it didn't work, go get the error.
        NonBlocking = false;                                                    // We are NOT NonBlocking.
        throw Networking::ControlError(                                         // Throw a control error.
          Network.DescriptiveError(
            "Socket::makeNonBlocking()", LastError));
    } else {
        NonBlocking = true;                                                     // If we didn't throw, we're ON.
    }
}

inline bool Socket::isReuseAddress() { return ReuseAddress; }                   // True if socket is set SO_REUSEADDR.
inline bool Socket::isReuseAddress(bool set) { return (ReuseAddress = set); }   // Changes SO_REUSEADDR setting.

inline bool Socket::isOpen() {                                                  // True if the socket is open.
    return(
      INVALID_SOCKET != Handle &&                                               // A valid handle and
      true == OpenSucceeded                                                     // a successful open operation
    );                                                                          // means we're open.
}

inline int Socket::getLastError() {                                             // Returns the last error for this socket.
    return LastError;
}

//// class TCPClient ///////////////////////////////////////////////////////////

inline TCPClient::TCPClient(TCPListener& L, hSocket H, SocketAddress& A) :      // How to create a TCPClient.
    MyListener(L) {                                                             // Capture our listener.
    Handle = H;                                                                 // Capture the new socket handle.
    RemoteAddress = A;                                                          // Capture the client address.
    ReadPointer = ReadBuffer;                                                   // Set the read position to zero.
    DataLength = 0;                                                             // There is no data yet.
    OpenSucceeded = true;                                                       // We're getting an open socket.
}

inline TCPClient::~TCPClient() {                                                // When destroying a TCPClient
    if(isOpen()) close();                                                       // Close when being destroyed.
}

inline void TCPClient::open() {                                                 // We provide open() as unsupported.
    throw Networking::NotSupportedError(                                        // Throw an exception if this is called.
      Network.DescriptiveError(
        "TCPClient::open()", LastError));
}

inline bool TCPClient::ReadBufferIsEmpty() {                                    // True if the ReadBuffer is empty.
    return (0 >= DataLength);                                                   // We can check that with DataLength.
}

inline void TCPClient::fillReadBuffer() {                                       // Fills the buffer from the socket.
    LastError = 0;                                                              // Clear the LastError value.
    ReadPointer = ReadBuffer;                                                   // Reset the ReadPointer.
    DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), NOFLAGS);         // Try to read some data.

    if(0 >= DataLength) {                                                       // If there was an error then
        LastError = Network.getLastError();                                     // Grab the last error code.
        DataLength = 0;                                                         // Correct the DataLength.
        if(Network.WouldBlock(LastError)) {                                     // If the error was WouldBlock then
            return;                                                             // simply return - it's ok.
        } else {                                                                // If it was a different error
            throw Networking::SocketReadError(                                  // then throw a ReadError.
              Network.DescriptiveError(
                "TCPClient::fillReadBuffer()", LastError));
        }
    }                                                                           // If we succeeded then our ReadBuffer
}                                                                               // assembly is in good shape.

inline bool TCPClient::isNonBlocking() {                                        // Provided for MessagePort.
    return Socket::isNonBlocking();
}

inline unsigned long TCPClient::getRemoteIP() {                                 // Get remote IP as long.
    return RemoteAddress.getAddress();
}

inline const char* TCPClient::getRemoteIP(char* str) {                          // Get IP as string.
    return RemoteAddress.getAddress(str);
}

inline unsigned short TCPClient::getRemotePort() {                              // Get remote Port as unsigned short.
    return RemoteAddress.getPort();
}

inline const char* TCPClient::getRemotePort(char* str) {                        // Get Port as string.
    return RemoteAddress.getPort(str);
}

//// class TCPHost /////////////////////////////////////////////////////////////

inline TCPHost::~TCPHost() {                                                    // When destroying a TCPHost
    if(isOpen()) close();                                                       // Close when being destroyed.
}

inline bool TCPHost::ReadBufferIsEmpty() {                                      // True if the ReadBuffer is empty.
    return (0 >= DataLength);                                                   // We can check that with DataLength.
}

inline void TCPHost::fillReadBuffer() {                                         // Fills the buffer from the socket.
    LastError = 0;                                                              // Clear the LastError value.
    ReadPointer = ReadBuffer;                                                   // Reset the ReadPointer.
    DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), NOFLAGS);         // Try to read some data.

    if(0 >= DataLength) {                                                       // If there was an error then
        LastError = Network.getLastError();                                     // Grab the last error code.
        DataLength = 0;                                                         // Correct the DataLength.
        if(Network.WouldBlock(LastError)) {                                     // If the error was WouldBlock then
            return;                                                             // simply return - it's ok.
        } else {                                                                // If it was a different error
            throw Networking::SocketReadError(                                  // then throw a ReadError.
              Network.DescriptiveError(
                "TCPHost::fillReadBuffer()", LastError));
        }
    }                                                                           // If we succeeded then our ReadBuffer
}                                                                               // assembly is in good shape.

inline bool TCPHost::isNonBlocking() {                                          // Provided for MessagePort.
    return Socket::isNonBlocking();
}

//// class TCPListener /////////////////////////////////////////////////////////

inline TCPListener::~TCPListener() {                                            // Close when deleting.
    close();
}
