// networking.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
//==============================================================================

// The networking module abstracts network communications and provides a set
// of objects for handling most tasks.

// 20080313 _M Refactored to throw proper runtime_error exceptions.

// Include only once...
#ifndef M_Networking
#define M_Networking

#include <stdexcept>
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>

using namespace std;

#include <cstdlib>
#include <cstdio>
#include <cerrno>

//// Platform specific includes...

#if defined(WIN32) || defined(WIN64)

//// Windows headers...

#include <winsock2.h>
typedef int socklen_t;                                                          // Posix uses socklen_t so we mimic it.
typedef SOCKET hSocket;                                                         // Winx handles Socket is opaque.

#else

//// GNU Headers...

#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/file.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>

typedef int hSocket;                                                            // *nix uses int to handle a Socket.
const hSocket INVALID_SOCKET = -1;                                              // -1 is the invalid Socket.

#endif

//// Handling SIGPIPE //////////////////////////////////////////////////////////

#ifndef MSG_NOSIGNAL
const int MSG_NOSIGNAL = 0;                                                     // Fake this if it isn't defined.
#endif

#ifndef SO_NOSIGPIPE
const int SO_NOSIGPIPE = 0;                                                     // Fake this if it isn't defined.
#endif

//// Tuning and Constants //////////////////////////////////////////////////////

const unsigned long LOCALHOST = 0x7F000001;                                     // 127.0.0.1 as an integer.

const int DefaultMaxPending = 5;                                                // Default connection queue size.

const int TCPClientBufferSize = 4096;                                           // TCP Client buffer size.
const int TCPHostBufferSize = 4096;                                             // TCP Host buffer size.

const int NOFLAGS = 0;                                                          // Magic number for no flags.

////////////////////////////////////////////////////////////////////////////////
// IP4address class
//
// The IP4address class makes it easy to manipulate IPs.

class IP4Address {                                                              // IP4Address manipulator.
  private:
    unsigned long int IP;                                                       // The actual data.

  public:
    IP4Address();                                                               // Blank constructor IP = 0.0.0.0
    IP4Address(const unsigned long int newIP);                                  // Constructor given unsigned long
    IP4Address(const IP4Address&);                                              // Constructor given an IP4Address

    IP4Address(const char* newIP);                                              // Construcing with a cstring.
    IP4Address(const string& newIP);                                            // Constructing with a cppstring.

    IP4Address& operator=(const unsigned long int Right);                       // Convert from unsigned long int.
    IP4Address& operator=(const char* Right);                                   // Convert from c string.
    IP4Address& operator=(const string& Right);                                 // Convert from cpp string.

    operator unsigned long int() const;
    operator string() const;

    bool operator<(const IP4Address Right) const;                               // < Comparison.
    bool operator>(const IP4Address Right) const;                               // > Comparison.
    bool operator==(const IP4Address Right) const;                              // == Comparison.
    bool operator!=(const IP4Address Right) const;                              // != Comparison.
    bool operator<=(const IP4Address Right) const;                              // <= Comparison.
    bool operator>=(const IP4Address Right) const;                              // >= Comparison.
};


/*    static unsigned long int&
      operator=(unsigned long int& Out, const IP4Address& In);                  // Assign to unsigned long

    static string&
      operator=(string& Out, const IP4Address& In);                             // Assign to cpp string
*/


////////////////////////////////////////////////////////////////////////////////
// Network Core class
//
// The Networking class acts as a central point for setup, cleanup, and access
// to network services. For example, when using WinSock, the DLL initialization
// must occur once when the program starts up and the shutdown must occur once
// as the program shuts down. The constructor and destructor of the "Network"
// instances of this class handles that work. There should only be one instance
// of this class anywhere in the program and that instance is created when this
// module is included. DON'T MAKE MORE INSTANCES OF THIS :-)
//
// Part of the reason for this class is to handle all of the cross-platform
// weirdness involved in handling sockets and conversions. This way all of the
// ifdef switched code can be consolidated into this utility class and the
// code for the remaining classes can remain nice and clean by using this
// class to handle those tasks.

class Networking {
  private:

  public:

    class NotSupportedError : public runtime_error {                            // Thrown when something can't be done.
        public: NotSupportedError(const string& w):runtime_error(w) {}
    };
    class InitializationError : public runtime_error {                          // Thrown if initialization fails.
        public: InitializationError(const string& w):runtime_error(w) {}
    };
    class ControlError : public runtime_error {                                 // Thrown if control functions fail.
        public: ControlError(const string& w):runtime_error(w) {}
    };
    class SocketCreationError : public runtime_error {                          // Thrown if a call to socket() fails.
        public: SocketCreationError(const string& w):runtime_error(w) {}
    };
    class SocketSetSockOptError : public runtime_error {
        public: SocketSetSockOptError(const string& w):runtime_error(w) {}      // Thrown if a call to setsockopt() fails.
    };
    class SocketBindError : public runtime_error {                              // Thrown if a call to bind() fails.
        public: SocketBindError(const string& w):runtime_error(w) {}
    };
    class SocketListenError : public runtime_error {                            // Thrown if a call to listen() fails.
        public: SocketListenError(const string& w):runtime_error(w) {}
    };
    class SocketConnectError : public runtime_error {                           // Thrown if a call to connect() fails.
        public: SocketConnectError(const string& w):runtime_error(w) {}
    };
    class SocketAcceptError : public runtime_error {                            // Thrown if a call to accept() fails.
        public: SocketAcceptError(const string& w):runtime_error(w) {}
    };
    class SocketReadError : public runtime_error {                              // Thrown if a socket read call fails.
        public: SocketReadError(const string& w):runtime_error(w) {}
    };
    class SocketWriteError : public runtime_error {                             // Thrown if a socket write call fails.
        public: SocketWriteError(const string& w):runtime_error(w) {}
    };

    static string DescriptiveError(string Msg, int Errno);                     // Form a descriptive error w/ errno.

    Networking();
    ~Networking();

    int getLastError();                                                         // WSAGetLastError or errno
    int setNonBlocking(hSocket socket);                                         // Set socket to non-blocking.
    int closeSocket(hSocket socket);                                            // closesocket() or close()

    bool WouldBlock(int ErrorCode);                                             // ErrorCode matches [WSA]EWOULDBLOCK
    bool InProgress(int ErrorCode);                                             // ErrorCode matches [WSA]EINPROGRESS
    bool IsConnected(int ErrorCode);                                            // ErrorCode matches [WSA]EISCONN

};

extern Networking Network;                                                      // There is ONE Network object ;-)

// End of Network Core Class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// SocketName class
// This class represents a communications end-point on a TCP/IP network. All
// conversions from/to strings and for byte orders are handled in this class
// as well as lookups for ports/services and IPaddresses/host-names.
//
// Note that the cstring conversions expect the buffer to be large enough.

const int IPStringBufferSize = 40;                                              // Safe size for IP as text conversion.
const int PortStringBufferSize = 20;                                            // Safe size for Port as text conversion.

class SocketAddress {
  private:
    struct sockaddr_in Address;                                                 // Socket address structure.

    char IPStringBuffer[IPStringBufferSize];                                    // Handy conversion buffer.
    char PortStringBuffer[PortStringBufferSize];                                // Handy conversion buffer.

  public:

    SocketAddress();                                                            // Constructor sets ANY address.

    struct sockaddr_in* getPtr_sockaddr_in();                                   // Returns a pointer to sockaddr_in.
    struct sockaddr* getPtr_sockaddr();                                         // Returns a pointer to sockaddr.
    socklen_t getAddressSize();                                                 // How big is that structure anyway?

    void setAddress(unsigned long ipAddress);                                   // Set the IP address from an unsigned int
    void setAddress(char* ipString);                                            // Set the IP address from a cstring
    unsigned long getAddress();                                                 // Get the IP address as an unsigned int
    const char* getAddress(char* str);                                          // Get the IP address into a cstring
    void getAddress(int& a0, int& a1, int& a2, int& a3);                        // Get the IP address into 4 ints

    void setPort(unsigned short port);                                          // Set the port address from an int
    void setPort(char* port);                                                   // Set the port address from a cstring
    unsigned short getPort();                                                   // Get the port address as an unsigned int
    const char* getPort(char* str);                                             // Get the port address into a cstring

    void clear();                                                               // Initialize the address.
};

// End of SocketName class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Socket class
// This class abstracts the underlying socket and adds some functionality
// for this module. The derived class is expected to setup the socket before
// it can be opened. In fact, the derivative class must provide the open()
// function :-) Open is expected to call socket, bind it, and set the socket
// into the appropriate mode for it's use in the derived object.

class Socket {
  protected:
    hSocket Handle;                                                             // Our socket handle.
    bool NonBlocking;                                                           // True if the socket is NonBlocking.
    bool ReuseAddress;                                                          // True if SO_REUSEADDR should be used.
    bool OpenSucceeded;                                                         // Successful open occurred.

    int LastError;                                                              // Last error result for this socket.

    SocketAddress LocalAddress;                                                 // Our local address data.
    SocketAddress RemoteAddress;                                                // Our remote address data.

  public:
    Socket();                                                                   // Constructor sets initial state.
    virtual ~Socket();                                                          // Destructor closes Socket if open.

    hSocket getHandle();                                                        // Returns the current SocketId.
    bool isNonBlocking();                                                       // Returns true if socket is NonBlocking
    void makeNonBlocking();                                                     // Sets the socket to NonBlocking mode.
    bool isReuseAddress();                                                      // True if socket is set SO_REUSEADDR.
    bool isReuseAddress(bool set);                                              // Changes SO_REUSEADDR setting.
    bool isOpen();                                                              // True if the socket is open.
    int getLastError();                                                         // Returns the last error for this socket.

    virtual void open() = 0;                                                    // Derived class specifies open();
    void close();                                                               // Close politely.
};

// End of Socket class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// MessagePort class
// Interface that Sends and Receives messages - possibly a bit at a time. This
// interface standardizes things so that multiple technologies can go beneith
// such as UNIX domain pipes or named pipes or sockets etc. There is also a
// special function to improve the efficiency of delimited transfers (such as
// email). The function checks for the delimited byte inside an optimized loop
// so that the port doesn't have to be read one byte at a time by the caller.
// In the case of non-blocking ports, these methods may return before all of
// the data has been transferred. In these cases the caller is expected to know
// if it's got the complete message and is expected to repeat it's call until
// it does.

class MessagePort {

  public:

    virtual bool isNonBlocking() = 0;                                           // True if we should expect partial xfrs.
    virtual int transmit(const char* bfr, int size) = 0;                        // How to send a buffer of data.
    virtual int receive(char* bfr, int size) = 0;                               // How to receive a buffer of data.
    virtual int delimited_receive(char* bfr, int size, char delimiter) = 0;     // How to receive delimited data.
};

////////////////////////////////////////////////////////////////////////////////
// Message class
// This is a base class for representing messages that are sent to or received
// from MessagePorts. The basic Message has 3 modes. Unfixed width, fixed width,
// or delimeted. More complex messaging schemes can be built up from these
// basics. A message must know how to send and recieve itself using the
// MessagePort API and must be able to indicate if the latest transfer request
// is complete or needs to be continued. The MessagePort may be blocking or
// non-blocking. If it is blocking then a writeTo() or readFrom() operation
// should not return until the transfer is completed. If the MessagePort is in
// a non-blocking mode then writeTo() and readFrom() will do as much as they
// can before returning but if the transfer was not completed then the app
// lication may need to transferMore().

class Message {

    char* Data;                                                                 // Pointer to message data.
    int DataBufferSize;                                                         // Size of buffer to hold data.
    int DataSize;                                                               // Size of Data.
    char* RWPointer;                                                            // RW position in buffer.
    bool TransferInProgress;                                                    // True if read or write is not complete.
    bool Delimited;                                                             // Delimited Message Flag.
    char Delimiter;                                                             // Delimiter character.

  public:
/** All of this is yet to be built! **/
    Message(const Message& M);                                                  // Copy constructor.
    Message(int Size);                                                          // Construct empty of Size.
    Message(int Size, char Delimiter);                                          // Construct empty with delimiter.
    Message(char* NewData, int Size);                                           // Construct non-delimited message.
    Message(char* NewData, int Size, char Delimiter);                           // Construct delimited message.

    void writeTo(MessagePort &P);                                               // Initiate an outbound transfer.
    void readFrom(MessagePort &P);                                              // Initiate an inbound transfer.
    bool isBusy();                                                              // True if the transfer isn't complete.
    void transferMore();                                                        // Do more of the transfer.
    void abortTransfer();                                                       // Forget about the transfer.

    bool isDelimited();                                                         // True if the message is delimited.
    char getDelimiter();                                                        // Read the delimiter cahracter.
    char* getData();                                                            // Access the data buffer.
    int getDataSize();                                                          // How much data is there.

};

// End of Message class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// TCPListener class
// This class represents a local socket used to listen for new connections. The
// application can poll this object for new inbound connections which are then
// delivered as TCPClient objects.

class TCPClient;                                                                // Hint about the coming client class.

class TCPListener : public Socket {
  private:

    bool OpenStage1Complete;                                                    // First stage of open() complete.
    bool OpenStage2Complete;                                                    // Second stage of open() complete.

  public:

    TCPListener(unsigned short Port);                                           // Set up localhost on this Port.
    TCPListener(SocketAddress& WhereToBind);                                    // Set up specific "name" for listening.
    ~TCPListener();                                                             // Close when destructing.

    int MaxPending;                                                             // Maximum inbound connection queue.

    virtual void open();                                                        // Open when ready.

    TCPClient* acceptClient();                                                  // Accept a client connection.
};

// End of TCPListener class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// TCPClient class
// This class represents a TCP network client connection. It is created by
// a TCPListener object if/when a TCP connection is made to the Listener and
// accepted by the application.

class TCPClient : public Socket, public MessagePort {
  private:
    TCPListener& MyListener;

    char ReadBuffer[TCPClientBufferSize];                                       // Buffer for delimited reading.
    char* ReadPointer;                                                          // Read position.
    int DataLength;                                                             // Length of data in buffer.

    bool ReadBufferIsEmpty();                                                   // True if DataLength is zero.
    void fillReadBuffer();                                                      // Fill the ReadBuffer from the socket.

  public:

    TCPClient(TCPListener& L, hSocket H, SocketAddress& A);                     // How to create a TCPClient.
    ~TCPClient();                                                               // Destructor for cleanup.

    TCPListener& getMyListener();                                               // Where did I come from?

    bool isNonBlocking();                                                       // Provided for MessagePort.
    virtual int transmit(const char* bfr, int size);                            // How to send a buffer of data.
    virtual int receive(char* bfr, int size);                                   // How to receive a buffer of data.
    virtual int delimited_receive(char* bfr, int size, char delimiter);         // How to receive delimited data.
    virtual void open();                                                        // We provide open() as unsupported.

    unsigned long getRemoteIP();                                                // Get remote IP as long.
    const char* getRemoteIP(char* str);                                         // Get IP as string.
    unsigned short getRemotePort();                                             // Get remote Port as unsigned short.
    const char* getRemotePort(char* str);                                       // Get Port as string.

};

// End of TCPClient class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// TCPHost class
// This class represents a TCP network server connection. A client application
// creates this object when it wants to connect to a given TCP service.

class TCPHost : public Socket, public MessagePort {
  private:
    char ReadBuffer[TCPHostBufferSize];                                         // Buffer for delimited reading.
    char* ReadPointer;                                                          // Read position.
    int DataLength;                                                             // Length of data in buffer.

    bool ReadBufferIsEmpty();                                                   // True if DataLength is zero.
    void fillReadBuffer();                                                      // Fill the ReadBuffer from the socket.

    bool OpenStage1Complete;                                                    // Skip stage 1 of open() after done.

  public:

    TCPHost(unsigned short Port);                                               // Will connect to localhost on Port.
    TCPHost(SocketAddress& Remote);                                             // Will connect to Remote address/port.
//    TCPHost(SocketAddress& Local, SocketAddress& Remote);                       // Will connect to Remote from Local.
    ~TCPHost();                                                                 // Clean up when we go away.

    bool isNonBlocking();                                                       // Provided for MessagePort.
    virtual int transmit(const char* bfr, int size);                            // How to send a buffer of data.
    virtual int receive(char* bfr, int size);                                   // How to receive a buffer of data.
    virtual int delimited_receive(char* bfr, int size, char delimiter);         // How to receive delimited data.
    virtual void open();                                                        // We provide open().

};

// End of TCPHost class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// UDPListener class
// This class represents a local UPD port set up to listen for UDP requests. In
// this case, each UDP packet that arrives is assumed to be a single request so
// for each a UDPRequest object is created that links back to this Listener.
// The application can then use that UDPRequest to .respond() with a Message.
// the response is sent back to the original requester and the UDPRequest is
// considered satisfied.

class UDPListener : public Socket {

};

// End of UDPListener class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// UDPRequest class
// This class is created by a UDPListener when a packet is received. The object
// contains all of the necessary information about the source for the request
// so that the application can .respond() to them through this object. The
// response UDP packtes are sent through the UDPListener socket.

class UDPRequest : public MessagePort {

};

// End of UDPRequest class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// UDPHost class
// This class represents a server/host on the network that uses the UDP
// protocol. The application can use this object to send a .request() Message
// and getReply(). Each request becomes a UDP packet. Each received UDP packet
// from the specified UDPHost becomes a reply Message. (Connected UDP socket).

class UDPHost : public Socket, public MessagePort {

};

// End of UDPHost class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// UDPReceiver class
// This class is used to receive UDP packets on a particular port, but does not
// create UDPRequest objects from them - they are considered to be simply
// Messages. A UDPReceiver is most likely to be used in ad-hoc networking and
// to receive advertisements and/or broadcasts from other peers.

class UDPReceiver : public Socket, public MessagePort {

};

// End of UDPReceiver class
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// UDPBroadcaster class
// This class is used to advertise / broadcast Messages using UDP.

class UDPBroadcaster : public Socket, public MessagePort {

};

// End of UDPBroadcaster class
////////////////////////////////////////////////////////////////////////////////

//// Include Inline methods and functions...

#include "networking.inline.hpp"

#endif
// End include Networking.hpp only once...