#ifndef __SOCKET_H_INCLUDED__
#define __SOCKET_H_INCLUDED__

#include <string>
#include <vector>
#include <stdexcept>

typedef int socket_handle;

#include "socket_wrapper.h"

// The ipaddr class represents an IPv4 address

class ipaddr {
  private:
	unsigned int a[4];	// Internally represented as 4 integers

	static bool valid_ip(unsigned int a0, unsigned int a1, unsigned int a2, unsigned int a3);
						// Check the validity of an IP address

  public:
	// CONSTRUCTORS

	// Create an IP address with the value 0.0.0.0
	ipaddr();

	// Create an IP address from a string in dotted notation
	explicit ipaddr(const std::string& s);

	// Create an IP address from its four components
	ipaddr(unsigned int a0, unsigned int a1, unsigned int a2, unsigned int a3);

	// Create an IP address from its 32 bit representation in network
	// byte order
	explicit ipaddr(unsigned int addr);

	// METHODS

	// Convert an IP address to a string in dotted notation
	std::string to_string() const;

	// Converts an IP address to the equivalent 32 bit unsigned
	// integer in network byte order
	unsigned int to_uint() const;

	// OPERATORS

	// Compare two ipaddr objects
	bool operator==(const ipaddr& rhs) const;
	bool operator!=(const ipaddr& rhs) const;
};

// Write an IP address to an output stream in dotted notation
template<class CharType, class CharTrait>
inline std::basic_ostream<CharType, CharTrait>&
operator<<(std::basic_ostream<CharType, CharTrait>& o, const ipaddr& ip)
{
	o << ip.to_string();
	return o;
}

// Read an IP address from an input stream in dotted notation
template<class CharType, class CharTrait>
inline std::basic_istream<CharType, CharTrait>&
operator>>(std::basic_istream<CharType, CharTrait>& i, ipaddr& ip)
{
	std::string s;
	i >> s;
	ip = ipaddr(s);
	return i;
}

// The endpoint class represents the endpoint of a TCP connection. It
// encapsulates an IP address or a host name plus a TCP port. The IP
// address may be 0.0.0.0 representing an arbitrary IP address. The
// port may be 0, representing an arbitrary port number

class endpoint {
  private:
	bool spec_as_ip;					// True if specified as IP address,
										// false if specified as hostname
	mutable bool DNS_lookup;			// True if DNS lookup has been performed
	mutable std::string hostname;		// Hostname
	ipaddr ip_address;					// Main IP address
	std::vector<ipaddr> ipaddresses;	// Additional IP addresses. Currently not used

	int portno;							// Port number

	// Used by constructors
	void initme(const std::string& ip, int port=0);
	static int decode_service(const std::string& service);

  public:
	// CONSTRUCTORS

	// Construct an endpoint with the indicated port number and an IP
	// address of 0.0.0.0
	endpoint(int port=0);

	// Construct an endpoint with the indicated IP address and port
	// number. If the IP string is a host name, name resolution is
	// used.
	explicit endpoint(const std::string& ip, int port=0);

	// Construct an endpoint with the indicated IP address and port
	// number.
	endpoint(const ipaddr& ip, int port=0);

	// Construct an endpoint with the indicated IP address and service
	// name. If the IP string is a host name, name resolution is used.
	endpoint(const std::string& ip, const std::string& service);

	// Construct an endpoint with the indicated IP address and service
	// name.
	endpoint(const ipaddr& ip, const std::string& service);


	// METHODS

	// Return the IP address of the endpoint
	std::string get_ipstring() const;

	// Return the IP address of the endpoint
	ipaddr get_ipaddr() const;

	// Return the host name, if any, of the endpoint
	std::string get_host() const;

	// Return the port number of the endpoint
	int get_port() const;

	// Return the service name, if any, derived from the port number
	// of the endpoint
	std::string get_service() const;

	// Return the IP number and port number in the form "127.0.0.1:80"
	std::string get_ipport() const;

	// Return the host name, if any, and port number in the form
	// "sillyhost.com:80"
	std::string get_hostport() const;

	// OPERATORS

	// Compare two endpoint objects
	bool operator==(const endpoint& rhs) const;
	bool operator!=(const endpoint& rhs) const;
};

// Write an endpoint to an output stream as an IP address and a port number
template<class CharType, class CharTrait>
inline std::basic_ostream<CharType, CharTrait>&
operator<<(std::basic_ostream<CharType, CharTrait>& o, const endpoint& ep)
{
	o << ep.get_ipport();
	return o;
}

// The output_formatter class is an absctract class upon which output
// formatters are built

class output_formatter {
	friend class socket_wrapper;

  private:
	socket_wrapper *sw;	// socket_wrapper using this formatter

  protected:
	// Wrapper around sw->send_no_format(). This function allows
	// output formatters to access the private members of
	// socket_wrapper.
	int formatterout(const void *buf, std::size_t len, int flags);

  public:
	// CONSTRUCTOR

	// Creates an output_formatter
	output_formatter();

	// Concrete output formatters must override this function to
	// perform formatted output
	virtual int output(const void* buf, std::size_t len, int flags) = 0;
};

// The input_formatter class is an abstract class upon which input
// formatters are built

class input_formatter {
	friend class socket_wrapper;

  private:
	socket_wrapper *sw;	// socket_wrapper using this formatter

  protected:
	// Wrapper around sw->recv_noformat(). This function allows
	// input formatters to access the private members of
	// socket_wrapper.
	int formatterin(void *buf, std::size_t len, int flags);

  public:
	// CONSTRUCTOR

	// Creates an input_formatter
	input_formatter();

	// Concrete input formatters must override this function to
	// perform formatted input
	virtual int input(void* buf, std::size_t len, int flags) = 0;
};


// The line_output_formatter is an output formatter that outputs
// records separated by "\r\n"

class line_output_formatter : public output_formatter {
  public:

	// Provide formatted output
	virtual int output(const void* buf, std::size_t len, int flags);
};


// The length_output_formatter is an output formatter that outputs
// records preceded by a length indicator

class length_output_formatter : public output_formatter {
  public:
	// Provide formatted output
	virtual int output(const void* buf, std::size_t len, int flags);
};

// The line_input_formatter is an input formatter that expects input
// records to be separated by "\r\n"

class line_input_formatter : public input_formatter {
  private:
	std::string tmp;	// Buffer for temporary data

  public:

	// GCC complains if this destructor does not exist
	virtual ~line_input_formatter() {}

	// Provide formatted input
	virtual int input(void* buf, std::size_t len, int flags);
};

// The length_input_formatter is an input formatter that expects input
// records to be preceded by a length indicator

class length_input_formatter : public input_formatter {
  private:
	std::string tmp;	// Buffer for temporary data

  public:

	// GCC complains if this destructor does not exist
	virtual ~length_input_formatter() {}

	// Provide formatted input
	virtual int input(void* buf, std::size_t len, int flags);
};


// The fixed_input_formatter is an input formatter that reads input
// records of a fixed length

class fixed_input_formatter : public input_formatter {
  private:
	std::string tmp;	// Buffer for temporary data

  public:
	// GCC complains if this destructor does not exist
	virtual ~fixed_input_formatter() {}

	// Provide formatted input
	virtual int input(void* buf, std::size_t len, int flags);
};



// The tcp_base_socket class is the base of a listening socket and a
// communication socket.
// An object of this class refers to an operating system socket
// created with the operating system call socket(PF_INET,SOCK_STREAM,0).

class tcp_base_socket {
  protected:
	socket_wrapper *sw;	// socket_wrapper containing socket handle and
								// I/O formatters

	// CONSTRUCTORS

	// Create a tcp_base_socket from an existing socket.cket object,
	// but do not create the underlying socket
	tcp_base_socket();

	// Create a tcp_base_socket from an existing socket. Will fail if
	// socket is not of proper type.
	explicit tcp_base_socket(socket_handle s);

	// Copy constructor
	tcp_base_socket(const tcp_base_socket& ts);


	// DESTRUCTOR

	virtual ~tcp_base_socket();


  public:
	// METHODS

	// Assignment operator
	tcp_base_socket& operator=(const tcp_base_socket& tp);

	// Create a socket
	void open();

	// Close the socket
	virtual void close();

	// Is the socket open?
	bool is_open() const;

	// Return a handle to the underlying socket
	socket_handle get_socket_handle() const;

	// Return the endpoint information about this end of the connection
	endpoint my_endpoint() const;

	void abort();
};



// The tcp_socket class represents the endpoint of a TCP connection.

class tcp_socket : public tcp_base_socket {
  public:
	// CONSTRUCTORS

	// Create a tcp_socket object, but do not create the underlying
	// socket
	tcp_socket();

	// Create a tcp_socket from an existing socket. Will fail if socket
	// is not of proper type.
	explicit tcp_socket(socket_handle s);

	// Copy constructor
	tcp_socket(const tcp_socket& ts);


	// METHODS

	// Is this socket connected to a socket on another host?
	bool is_connected() const;

	// Return the endpoint information about the other end of the
	// connection
	endpoint peer() const;

	// Return the number of bytes waiting to be read
	int fionread() const;

	// Read data from the socket
	int recv(void *buf, std::size_t len, int flags=0);

	// Read data from the socket
	// The default maxlen is large enough to hold an Ethernet frame
	int recv(std::string& buf, std::size_t maxlen=2048, int flags=0);

	// Send data to the socket
	int send(const void *buf, std::size_t len, int flags=0);

	// Send data to the socket
	int send(const std::string& buf, int flags=0);

	// Set an output formatter
	void set_output_formatter(output_formatter *outform);

	// Set an input formatter
	void set_input_formatter(input_formatter *inform);
};


// The tcp_listener class represents a socket that listens for an
// incoming connection

class tcp_listener : public tcp_base_socket {
  private:
	endpoint ep;		// IP address and port on which we are listening
	bool isbound;		// Has the endpoint been bound to the socket?
	bool islistening;	// Is the socket in the listening mode?

  public:
	// CONSTRUCTORS

	// Create an uninitialized listener
	tcp_listener();

	// Create a listener that listens for connections on the specified
	// endpoint
	explicit tcp_listener(const endpoint& n);


	// METHODS

	// Close the socket
	virtual void close();

	// Bind the previously set endpoint to the listener
	void bind();

	// Bind the specified endpoint to the listener
	void bind(const endpoint& n);

	// Start listening.
	// This method calls bind() if this has not already happened.
	void listen(int backlog=0);

	// Accept an incoming connection
	// This method calls listen() if this has not already happened.
	tcp_socket accept();
};


// The tcp_client_socket class represents a socket that connects to a
// listner

class tcp_client_socket : public tcp_socket {
  public:
	// CONSTRUCTORS

	// Create the object. Do not connect to anyone.
	tcp_client_socket();

	// Create the object and connect to the specified node.
	explicit tcp_client_socket(const endpoint& n);


	// METHODS

	// Connect to the specified node
	void connect(const endpoint& n);
};


// The socket_error class represents an exception generated by the
// dti::sockets library

class socket_error : public std::runtime_error {
  public:
	explicit socket_error(const std::string& arg);

	// GCC complains if this destructor does not exist
	virtual ~socket_error() throw() {};
};

// The socket_error_ex class represents an exception generated by the
// dti::sockets library. This exception includes addtional system
// error information

class socket_error_ex : public socket_error {
  private:
	int syserr;				// The error number in the underlying operating system
	std::string systext;	// The error text in the underlying operating system

  public:
	explicit socket_error_ex(const std::string& arg);

	// GCC complains if this destructor does not exist
	virtual ~socket_error_ex() throw() {};

	// Return the system error number
	int whaterr() const;

	// Return the system error text
	std::string whatsystext() const;
};

#endif //__SOCKET_H_INCLUDED__
