/*
 * Author:
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * $Id: connection.h,v 1.23 2005/12/29 16:21:58 kutzner Exp $
 *
 */

#ifndef _CONNECTION_H_
#define _CONNECTION_H_

/** \file
 * Declares class \link conn conn \endlink
 */

#include <string>
using namespace std;

#include <netinet/in.h>

#include "message.h"
#include "igor.h"
#include "buf.h"
#include "statistic.h"

/** Handles connection related stuff.  This class implements a tcp-like
 * connection. It should not be dependend on IPv4/v6. The communication is done
 * non-blocking if not noted otherwise. It does (blocking) name resolutions
 * where neccessary. The basic operations for connections are open and close,
 * as well as sending and receiving of messages. This class doesn't handle
 * single bytes, but objects of type \link msg msg \endlink. Further a
 * connection can be made listening for incoming connections.
 */

class conn {

	public:
		/// throwable exception to tell somebody this connections has ended its existence
		class e_should_close{
			public:
				/// Initialize the exception with an explanation.
				e_should_close(string reason){_why = reason;};
				/// Fetch the reason why this exception occured.
				string reason(){return _why;};
			private:
				/// Remember the reason for this exception.
				string _why;
		};

		/** The possible states for a connection.
		 * Every connection is always in one of these states.
		 */
		enum conn_state { 
			EMPTY, ///< just created object
			OPENING, ///< waiting for syn from the other side
			LISTEN, ///< waiting for incoming connections
			READY, ///< ready for read/write operations
			CLIENT, ///< similar to ready, but client side \todo Move the distinction between READY and CLIENT to class node.
			CLOSED ///< just before destruction
		};
		conn();
		/// Open a transport connection.
		int open(string transport);
		/// Just a wrapper arround the above.
		int open(const char * transport);
		/// Destroy a connection
		~conn();
		/// Wait for incoming connections.
		void make_listen(int port = 0); // when no port supplied, let the OS select
		/// Switch to blocking communication.
		void make_blocking(); 
		/// Accept a incoming connection.
		conn* do_accept();
		/** Return the socket file descriptor. This access to internal
		 * structures is neccessary, because we want to pass the fd to
		 * select(). 
		 * \todo Find another way to call select(). 
		 * \return * The socket file descriptor of the connection.
		 */
		int getfd(){return _sock;};
		/// Send a message (not a rawmessage, not a buf).
		int send(msg*) throw (e_should_close);
		/// Receive a message (not a rawmessage, not a buf).
		msg* recv(bool *) throw (e_should_close);
		/// Return the state a connection is in.
		enum conn_state state(){return _state;};
		/// Return the state a connection is in for humans.
		string state_s(){return statename(_state);};
		/// Connection shutdown.
		void close();
		/// Give the name of the connection.
		string transportname();
		/// Special state transitions not reachable otherwise.
		void setclient();
		/// Special state transitions not reachable otherwise.
		void setready();
		// Statistics: add the internal counters to the ones provided.
		void copy_counters(stat_counter * m_in, stat_counter * m_out, stat_counter * b_in, stat_counter * b_out);
	private:
		/// The state the connection is in.
		enum conn_state _state;
		/** The socket file desriptor the connection is about. 
		 * This variable is -1 when the socket descriptor is not valid.
		 */
		int _sock;
		struct sockaddr_storage _sa;
		/// Change from one state into another.
		void state_change(conn_state);
		char * statename(conn_state s){
			switch (s){
				case EMPTY: return("EMPTY");
				case OPENING: return("OPENING");
				case LISTEN: return("LISTEN");
				case READY: return("READY");
				case CLOSED: return("CLOSED");
				case CLIENT: return("CLIENT");
				default : return("--error in file connection.h --");
			}
		}
		buf _receive_buf;
		buf _send_buf;
		stat_counter _msg_in, _msg_out, _bytes_in, _bytes_out;
		/// Parse the receive buffer
		msg * recv_parsemsg();
};


#endif

