/*
 * Author:
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * $Id: node.h,v 1.28 2005/11/29 16:14:48 kutzner Exp $
 *
 */

#ifndef _NODE_H_
#define _NODE_H_

#include <map>
#include <list>
#include <string>
using namespace std;

#include "connection.h"
#include "callback.h"
#include "callbacklist.h"
#include "id.h"
#include "message.h"
#include "noderef.h"
#include "serviceid.h"
#include "statistic.h"

class node{
	public:
		struct cone { // short for connection entry
			conn * connection;
			callback<node> readcallback;
			callback<node> writecallback;
			int readcallbackhandle;
			int writecallbackhandle;
			noderef otherend;
			list<serviceid> services;
			double opensince;
		};

		conn * open_incoming_connection(int port);
		//conn * open_client_iconn(int port);
		int init(callbacklist * cbl, map<string,string>);
		void shutdown();
		void accept_inc(int,int);
		void do_bootstrap(string transport);
		void incoming_message(msg *m, cone*);
		void send_ping(node::cone*, bool protect = true);

		void conn_close(cone*);
		void conn_open(noderef);
		cone* conn_open(string);

	private:
		// message routing
		cone * nexthop(id *target);
		int route(msg * msg);
		int route(msg * msg, id * id);
		int route_random(msg*msg);
		bool wrfinal(id *target);

		// message handling
		void send(msg *m, cone *c);
		/// same thing, but catches the e_should_close exception
		void send_protected(msg *m, cone *c);
		void handlemsg_rl(msg *m, cone*);
		void handlemsg_wnl(msg *m, cone*);
		void handlemsg_ping(msg *m, cone*);
		void handlemsg_data(msg *m, cone*);
		void handlemsg_data_net(msg_data *m, cone*);
		void handlemsg_data_client(msg_data *m, cone*);
		void handlemsg_register(msg *m, cone*);
		void handlemsg_register_reply(msg *m, cone*);
		void handlemsg_unregister(msg *m, cone*);
		void handlemsg_getinfo(msg *, cone *);
		void handlemsg_info(msg *, cone *);
		
		conn * _iconn;
		conn * _client_iconn;
		callback<node> _lcb;
		callback<node> _client_lcb;
		int _lcb_handle;
		int _client_lcb_handle;
		void reg_iconn_callback();
		void reg_client_iconn_callback();
		// id _id;
		callbacklist * _cbl;

		typedef list<cone*> connlist_t;
		typedef connlist_t::const_iterator connlist_iter;
		// typedef std::pair<int, cone*> connlist_et;
		connlist_t connlist;
		cone * getconebycblh(int);
		cone * getconebyfd(int);
		void sortcones(id);

		void conn_readable(int handle, int i);
		void conn_writable(int handle, int i);

		void close_all_clients();

		noderef _myref;

		/// Check the routing table if this reference would be useful at all.
		bool useful_ref(const noderef &);
		/// Check routing table if this reference would nicely fit into the distribution of node IDs
		bool useful_ref_distribution(const noderef &ref);
		void close_least_useful_conn();

	// stabilization:
	public:
		void start_stabilize();
		void stabilize(int,int);
		void stop_stabilize();
	private:
		callback<node> _stabilize_cb;
		int _stabilize_cb_handle;
		float stabilize_timer;
		bool _cont_if_no_conns;

	// finger fixing:
	public:
		void start_fixfingers();
		void fixfingers(int,int);
		void stop_fixfingers();
	private:
		callback<node> _fixfingers_cb;
		int _fixfingers_cb_handle;
		float fixfingers_timer;

	// connection pinging
	public:
		void start_pinger();
		void pinger(int,int);
		void stop_pinger();
	private:
		callback<node> _pinger_cb;
		int _pinger_cb_handle;
		float pinger_timer;

	// clients
	private:
		typedef map<serviceid, cone*> service_t;
		service_t services;
	
	// (variable) constants
	private:
		int param_min_connections; ///< Minimum number of connections. If we have less than this, all new connections are accepted.
		int param_min_connections2; ///< Minimum number of connections. If we have less than this, existing connections are not closed.
		float param_logarithm; ///< Use a logarithm to that base. Should be arround 2.0. Use larger values for weak nodes, use lower values (but always above 1.0) for high power nodes.
		float param_min_connection_lifetime; ///< Minimum connection lifetime. Connections younger than this value are not closed.


	// debug
	private:
		void dump_conn_list();

	// statistics
	private:
		stat_counter _msg_in;
		stat_counter _msg_out;
		stat_counter _bytes_in;
		stat_counter _bytes_out;
		stat_counter _connections_count;
};

#endif

