/*
 * Author:
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * $Id: node.cc,v 1.54 2006/01/09 10:27:19 kutzner Exp $
 *
 */

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

#include <time.h>

#include <unistd.h> // getdomainname

#include "node.h"
#include "callback.h"
#include "callbacklist.h"
#include "connection.h"
#include "debug.h"
#include "id.h"
#include "serviceid.h"

////////////////////////////
// node startup and shutdown

int
node::init(callbacklist * cbl, map<string,string> configvals){
	dbg.print(dbg.NODE, 5, "node_init\n");
	struct timeval t;
	gettimeofday(&t, NULL);
	srandom(t.tv_sec^t.tv_usec);
	_cbl = cbl;
	int netport = atoi(configvals["netport"].c_str());
	int clientport = atoi(configvals["clientport"].c_str());
	_cont_if_no_conns = atoi(configvals["cont_if_no_conns"].c_str());
	dbg.print(dbg.NODE, 9, "set _cont_if_no_conns to %i with configval '%s'\n", _cont_if_no_conns, configvals["quit_if_no_conns"].c_str());
	_iconn = open_incoming_connection(netport);
	_client_iconn = open_incoming_connection(clientport);
	_lcb_handle = 0;
	_client_lcb_handle = 0;
	reg_iconn_callback();
	reg_client_iconn_callback();
	string idstr = configvals["id"];
	id tmpid;
	if (idstr.size()>0){
		tmpid = id(idstr);
	} else {
		tmpid.randomize();
	}
	_myref = noderef(tmpid, transport(_iconn->transportname()));
	string boot = configvals["bootstrap"];
	if (boot.size()>0){
		do_bootstrap(boot);
	}
	stabilize_timer = atof(configvals["timer_stabilize"].c_str());
	start_stabilize();
	fixfingers_timer = atof(configvals["timer_fixfingers"].c_str());
	start_fixfingers();
	pinger_timer = atof(configvals["timer_pinger"].c_str());
	start_pinger();
	param_min_connections = 10;
	param_min_connections2 = 30;
	param_min_connection_lifetime = 30.0;
	param_logarithm = 2.0;
	dbg.print(dbg.NODE, 5, "node_init_done; ref: %s, net_listen: %s, client_listen: %s\n", 
			_myref.tostring().c_str(), 
			_iconn->transportname().c_str(), 
			_client_iconn->transportname().c_str());
	return 0;
}

void
node::shutdown(){
	dbg.print(dbg.NODE, 5, "shutting down node\n");
	// debug
	dump_conn_list();
	// prevent new connections
	_iconn->close();
	_cbl->unreg(_lcb_handle);
	delete _iconn;
	_client_iconn->close();
	_cbl->unreg(_client_lcb_handle);
	delete _client_iconn;
	// don't create new messages
	stop_stabilize();
	stop_fixfingers();
	stop_pinger();
	// close connections
	close_all_clients();
	dbg.print(dbg.NODE, 5, "node shutdown complete\n");
	dbg.print(dbg.NODE, 5, "NODE stat: Sent %li bytes in %li messages; Received %li bytes in %li messages; Used %li connections\n", 
			_bytes_out.get(), 
			_msg_out.get(), 
			_bytes_in.get(), 
			_msg_in.get(), 
			_connections_count.get());
}

void
node::do_bootstrap(string boot){
	dbg.print(dbg.NODE, 5, "bootstrap to %s in progress\n", boot.c_str());
	conn_open(boot);
	return;
}

void
node::close_all_clients(){
	connlist_iter i;
	cone * c;
	while((i=connlist.begin())!=connlist.end()){
		c = *i;
		conn_close(c);
	}
}


////////////////////////////
// regular tasks
//
// there are the following regular todos:
//  - stabilize: make sure routing is consistent by querying for ID-neighbors
//  - fix_fingers: make sure routing is fast by updating fingers
//  - pinger: send all neighbors a ping signal

void
node::start_stabilize(){
	if (0 == stabilize_timer){ // not set
		stabilize_timer = 10;
	}
	_stabilize_cb.init(this, &node::stabilize, 0, ((id)_myref).tostring() + " stabilize");
	// _stabilize_cb_handle = _cbl->reg(&_stabilize_cb, stabilize_timer);
	_stabilize_cb_handle = _cbl->reg(&_stabilize_cb, .1); // start the first stabilization very soon
}

void
node::stabilize(int cbhandle, int){
	dbg.print(dbg.NODE, 6, "starting stabilization\n");
	// count the connections in state ready. If count is zero, check whether to continue or issue error message and quit
	connlist_iter i;
	int connreadycount = 0;
	for (i = connlist.begin(); i != connlist.end(); i++){
		if ((*i)->connection->state() == conn::READY)
			connreadycount++;
		// connections which are just opening count too
		if ((*i)->connection->state() == conn::OPENING)
			connreadycount++;
	}
	if (0 == connreadycount){
		if(_cont_if_no_conns){
			dbg.print(dbg.NODE, 5, "no connection left, but that's ok\n");
		} else {
			dbg.print(dbg.NODE, 3, "no connection left, exiting now\n");
			shutdown();
			return;
		}
	}
	// send a stabilization message to our next neighbor.
	///\todo Send this stabilization message to any/all/most/some neigbors?
	msg_wnl * msg = new msg_wnl(_myref, 5);
	assert(msg);
	msg->setsource(_myref);
	route(msg);
	delete msg;
	// and restart the timer
	_cbl->rereg(cbhandle, stabilize_timer);
	dbg.print(dbg.NODE, 6, "stabilization finished\n");
}

void
node::stop_stabilize(){
	_cbl->unreg(_stabilize_cb_handle);
}


void
node::start_fixfingers(){
	if (0 == fixfingers_timer){ // not set
		fixfingers_timer = 20;
	}
	_fixfingers_cb.init(this, &node::fixfingers, 0, _myref.tostring() + " fixfingers");
	//_fixfingers_cb_handle = _cbl->reg(&_fixfingers_cb, fixfingers_timer);
	_fixfingers_cb_handle = _cbl->reg(&_fixfingers_cb, fixfingers_timer/3.0); //start the first fingerfixing early
}

void
node::fixfingers(int cbhandle, int){
	dbg.print(dbg.NODE, 6, "starting fingerfixing\n");
	// close something :)
	close_least_useful_conn();
	id target = _myref;
	target.create_random_log_fingerdest();
	msg_wnl * msg = new msg_wnl(target, 5);
	assert(msg);
	msg->setsource(_myref);
	route(msg);
	delete msg;
	_cbl->rereg(cbhandle, fixfingers_timer);
	dbg.print(dbg.NODE, 6, "fingerfixing finished\n");
}

void
node::stop_fixfingers(){
	_cbl->unreg(_fixfingers_cb_handle);
}


void
node::start_pinger(){
	if (0 == pinger_timer){ // not set
		pinger_timer = 20;
	}
	_pinger_cb.init(this, &node::pinger, 0, _myref.tostring() + " pinger");
	_pinger_cb_handle = _cbl->reg(&_pinger_cb, pinger_timer);
}

void
node::pinger(int cbhandle, int){
	dbg.print(dbg.NODE, 6, "start pinging\n");
	connlist_iter i;
	cone * c;
	for (i = connlist.begin(); i != connlist.end(); i++){
		c = *i;
		assert(c);
		if (c->connection->state()!=conn::READY){
			continue;
		}
		send_ping(c);
	}
	_cbl->rereg(cbhandle, pinger_timer);
	dbg.print(dbg.NODE, 6, "stop pinging\n");
}

void
node::stop_pinger(){
	_cbl->unreg(_pinger_cb_handle);
}

////////////////////////////
// message routing

node::cone *
node::nexthop(id *target){
	dbg.print(dbg.NODE, 6, "estimating next hop for dest %s\n", target->tostring().c_str());
	sortcones(*target);
	connlist_iter i;
	cone *cur;
	for (i = connlist.begin(); i != connlist.end(); i++){
		cur = *i;
		dbg.print(dbg.NODE, 7, "check entry %s\n", cur->otherend.tostring().c_str());
		if (cur->connection->state()!=conn::READY){
			dbg.print(dbg.NODE, 7, "not ready\n");
			continue;
		}
		// now we have a READY connection. 

		return cur;
	}
	return NULL;
}

int
node::route(msg *msg, id *target){
	dbg.print(dbg.NODE, 6, "routing msg of type %s to id %s\n", 
			msg->getname().c_str(), 
			target->tostring().c_str());
	cone * nh = nexthop(target);
	if (!nh){
		dbg.print(dbg.NODE, 6, "no target found, nothing routed\n");
		return 0;
	}
	dbg.print(dbg.NODE, 6, "sending to connection with otherend %s\n", 
			nh->otherend.tostring().c_str());
	try {
		send(msg, nh);
	}
	catch (conn::e_should_close){
		conn_close(nh);
		// lets try (recursively) again with the second best pick
		return route(msg, target);
	}
	return 1;
}

int
node::route(msg*msg){
	id dest = msg->target();
	return route(msg, &(dest));
}

int
node::route_random(msg*msg){
	id dest;
	dest.randomize();
	return route(msg, &(dest));
}

/// determine whether this is the final destination for a message with a given target

bool
node::wrfinal(id * target){
	cone * nh = nexthop(target);
	if (!nh)
		return true;
	// Check if it is closer to the target than we are
	if (target->closer(_myref, nh->otherend) == -1) { // we are closer
		dbg.print(dbg.NODE, 7, "wrfinal: return true because we (%s) are closer although nexthop %s found\n", 
				_myref.tostring().c_str(),
				nh->otherend.tostring().c_str());
		return true;
	}
	// there is a next hop, and it is closer than we
	return false;
}

////////////////////////////
// the listen sockets

conn*
node::open_incoming_connection(int port){
	dbg.print(dbg.NODE, 6, "open listen connection...\n");
	conn *c;
	c = new conn;
	c->make_listen(port);
	dbg.print(dbg.NODE, 6, "open listen connection...open, fd is %i, transport is %s\n", 
			c->getfd(), 
			c->transportname().c_str());
	return c;
}

void
node::reg_iconn_callback(){
	if (_lcb_handle){
		_cbl->rereg(_lcb_handle);
	} else {
		_lcb.init(this, &node::accept_inc, 0, "incoming connection");
		_lcb_handle = _cbl->reg_r(&_lcb, _iconn, 0);
	}
	return;
}

void
node::accept_inc(int /* cblhandle*/, int i ){
	// meaning of i:
	// 0: accept connection from peer
	// 1: accept connection from client
	dbg.print("accept new connection\n");
	conn *conn;
	if (i==0){
		conn = _iconn->do_accept();
	} else {
		conn = _client_iconn->do_accept();
	}
	if (!conn){
		dbg.print(dbg.NODE, 5, "new connection failed\n");
		if (i==0){
			reg_iconn_callback();
		} else {
			reg_client_iconn_callback();
		}
		return;
	}
	if (i==1){
		conn->setclient();
	}
	cone * c = new cone;
	c->connection = conn;
	transport t(conn->transportname()); // somehow stupid, since (transport)->(string)->(transport) conversion
	c->otherend.sethost(t.host()); // keeps node-id and port number unset
	c->readcallback.init(this, &node::conn_readable, conn->getfd(), "connection readable");
	c->readcallbackhandle = _cbl->reg_r(&c->readcallback, c->connection, 0);
	c->writecallback.init(this, &node::conn_writable, conn->getfd(), "connection writable");
	c->writecallbackhandle = 0;
	c->opensince = dbltime();
	connlist.push_back(c);
	// reg_iconn_callback();
	if (i==0)
		send_ping(c);
	return;
}

void
node::reg_client_iconn_callback(){
	if (_client_lcb_handle){
		_cbl->rereg(_client_lcb_handle);
	} else {
		_client_lcb.init(this, &node::accept_inc, 1, "incoming client connection callback");
		_client_lcb_handle = _cbl->reg_r(&_client_lcb, _client_iconn, 0);
	}
	return;
}

/////////////////////////
// connection handling

void
node::conn_open(noderef ref){
	cone * c;
	c = conn_open(((transport)ref).tostring()); // this is stupid, because tranport is converted (parsed)->(string)->(parsed)
}

node::cone *
node::conn_open(string tp){
	dbg.print(dbg.NODE, 5, "open new connection to %s\n", tp.c_str());
	cone * c = new cone;
	assert(c);
	c->connection = new conn;
	assert(c->connection);
	if (0 > (c->connection->open(tp))){
		dbg.print(dbg.NODE, 6, "open connection failed\n");
		delete c;
		return NULL;
	}
	c->readcallback.init(this, &node::conn_readable, c->connection->getfd(), "connection ready");
	c->readcallbackhandle = _cbl->reg_r(&c->readcallback, c->connection, 0);
	c->writecallback.init(this, &node::conn_writable, c->connection->getfd(), "connection writable");
	c->writecallbackhandle = 0;
	c->otherend.update(tp);
	connlist.push_back(c);
	send_ping(c); // does this work?
	return c;
}

void
node::conn_readable(int , int fd){
	cone * c;
	msg * m = NULL;
	bool callagain;
	c = getconebyfd(fd);
	assert(c);
	dbg.print(dbg.NODE, 7, "connection with fd %i readable\n", c->connection->getfd());
	do {
		try {
			if (c->connection->state()==conn::OPENING){
				// setting the connection to ready is now done in conn::recv() // c->connection->setready();
				dbg.print(dbg.NODE, 8, "sending ping\n");
				bool protect = false;
				send_ping(c, protect);
			}
			dbg.print(dbg.NODE, 8, "receiving\n");
			m = c->connection->recv(&callagain);
		} catch (conn::e_should_close x){
			dbg.print(dbg.NODE, 6, "exception catched: connection not readable (or ping failure). Reason given: '%s'\n", x.reason().c_str());
			conn_close(c);
			return;
		}
		dbg.print(dbg.NODE, 7, "recv returned (msg %p, callagain %i)\n", (void*)m, callagain);
		if(m){
			dbg.print(dbg.NODE, 6, "incoming message of type %s\n", m->getname().c_str());
			incoming_message(m, c);
		}
	} while (callagain);
}

void
node::conn_writable(int, int fd){
	cone *c = getconebyfd(fd);
	assert(c);
	dbg.print(dbg.NODE, 5, "connection with fd %i writable\n", c->connection->getfd());
	int sres;
	try {
		sres = c->connection->send(NULL);
	} catch (conn::e_should_close){
		conn_close(c);
		sres = 0;
	}
	if (sres == 1){ // still more data to send
		dbg.print(dbg.NODE, 5, "still more data to send\n");
		_cbl->rereg(c->writecallbackhandle);
	} else {
		_cbl->unreg(c->writecallbackhandle);
		c->writecallbackhandle = 0;
	}
}


void
node::conn_close(cone *c){
	dbg.print(dbg.NODE, 5, "closing connection with cbhandle %i and wcbhandle %i\n", c->readcallbackhandle, c->writecallbackhandle);
	_cbl->unreg(c->readcallbackhandle);
	if (c->writecallbackhandle){
		_cbl->unreg(c->writecallbackhandle);
		c->writecallbackhandle = 0;
	}
	if(c->connection){
		dbg.print(dbg.NODE, 6, "delete connection with fd %i\n", c->connection->getfd());
		// copy out the statistics
		c->connection->copy_counters( & _msg_in, & _msg_out, & _bytes_in, & _bytes_out);
		_connections_count.inc();
		delete c->connection;
	}
	// remove all services
	for (service_t::iterator i = services.begin(); i!=services.end(); i++){
		if (i->second == c) {
			services.erase(i);
		}
	}
	connlist.remove(c);
	delete c;
	return;
}

////////////////////////
// message handling




// outgoing message

void
node::send(msg *m, cone *c){
	int sr = c->connection->send(m);
	if (sr == 0) // everything ok
		return;
	// we are here because not everything could be sent out at once
	if (!(c->writecallbackhandle)){
		c->writecallbackhandle = _cbl->reg_w(&c->writecallback, c->connection, 0);
	}
}

void
node::send_protected(msg *m, cone *c){
	try{
		send(m, c);
	} catch (conn::e_should_close) {
		conn_close(c);
	}
}


// the dispatcher

void
node::incoming_message(msg * m, cone * c){
	dbg.print(dbg.NODE, 5, "got a message of type %s on fd %i\n", m->getname().c_str(), c->connection->getfd());
	switch(m->type()){
		case msg::WNL:
			handlemsg_wnl(m, c);
			break;
		case msg::RL:
			handlemsg_rl(m, c);
			break;
		case msg::PING:
			handlemsg_ping(m, c);
			break;
		case msg::REG:
			handlemsg_register(m, c);
			break;
		case msg::REGREP:
			handlemsg_register_reply(m, c);
			break;
		case msg::UNREG:
			handlemsg_unregister(m, c);
			break;
		case msg::DATA:
			handlemsg_data(m, c);
			break;
		case msg::GETINFO:
			handlemsg_getinfo(m, c);
			break;
		case msg::INFO:
			handlemsg_info(m, c);
			break;
		default:
			dbg.print(dbg.NODE, 5, "don't know how to handle that msg\n");
			delete m;
			break;
	}
}

// node-node messages

void
node::handlemsg_wnl(msg *mi, cone * cn){
	msg_wnl *m = dynamic_cast<msg_wnl*>(mi);
	dbg.print(dbg.NODE, 6, "handlemsg_wnl %p\n", (void*)m);
	// let's answer
	sortcones(m->target());
	msg_rl * answer = new msg_rl(m);
	answer->setsource(_myref);
	int numprovided = MIN(m->numbers_wanted(), connlist.size());
	connlist_iter i;
	for (i = connlist.begin(); numprovided && i != connlist.end(); i++){
		cone * c = *i;
		if (c->connection->state()==conn::READY){
			answer->add(c->otherend);
			numprovided--;
		}
	}
	//route(answer);
	send_protected(answer, cn);
	delete(answer);
	delete m;
}

void
node::send_ping(cone * c, bool protect){
	msg_ping * pm = new msg_ping;
	assert(pm);
	pm->setsource(_myref);
	dbg.print(dbg.NODE, 7, "sending ping to %s fd %i\n", 
			c->otherend.tostring().c_str(), 
			c->connection->getfd());
	if (protect)
		send_protected(pm, c);
	else
		send(pm, c);
	delete pm;
	return;
}

void
node::handlemsg_ping(msg * mi, cone * c){
	msg_ping *m = dynamic_cast<msg_ping*>(mi);
	dbg.print(dbg.NODE, 6, "in handlemsg_ping\n");
	// see what we can learn
	dbg.print(dbg.NODE, 7, "msg source %s, otherend %s\n", 
			m->source().tostring().c_str(), 
			c->otherend.tostring().c_str());
	// we believe port number and node id, but not host name ///\todo this is not good
	c->otherend.setport(m->source().port()); // set the port number to what the other side said
	c->otherend.change(m->source()); // set the node id to what the other side said
	// we don't believe the host name. Because of NAT and wrongly set hostnames it is bogus anyway.
	dbg.print(dbg.NODE, 7, "new otherend %s\n", c->otherend.tostring().c_str());
	delete m;
}

void
node::handlemsg_rl(msg * mi, cone * ){
	msg_rl *m = dynamic_cast<msg_rl*>(mi);
	dbg.print(dbg.NODE, 6, "in handlemsg_rl\n");
	noderef ref;
	while (m->numprovided()>0){
		ref=m->remove();
		dbg.print(dbg.NODE, 7, "message contained the reference %s\n", 
				ref.tostring().c_str());
		if (useful_ref(ref)){
			dbg.print(dbg.NODE, 7, "open new connection to %s\n", 
					ref.tostring().c_str());
			conn_open(ref);
		}
	}
	delete m;
}

// client-node messages

void
node::handlemsg_register(msg * mi, cone *c){
	msg_register *m = dynamic_cast<msg_register*>(mi);
	dbg.print(dbg.NODE, 6, "in handlemsg_register\n");
	if (c->connection->state()!=conn::CLIENT){
		dbg.print(dbg.NODE, 4, "ignoring received msg of type register in state %s\n", 
				c->connection->state_s().c_str());
		delete m;
		return;
	}
	bool alreadyregd = (services.find(m->getserviceid())) != services.end();
	msg_register_reply * answer = new msg_register_reply;
	assert(answer);
	if (alreadyregd) { // there is already a registration for this service (doesn't matter if it is the same connection)
		answer->setsuccess(false);
	} else {
		pair<serviceid,cone*> thepair = pair<serviceid,cone*>(m->getserviceid(), c);
		services.insert(thepair);
		answer->setserviceid(m->getserviceid());
		answer->setsuccess(true);
	}
	send_protected(answer, c);
	delete answer;
	delete m;
}

void
node::handlemsg_register_reply(msg * m, cone *){
	dbg.print(dbg.NODE, 4, "received msg of type register_reply; that's bull\n");
	delete m;
}

void
node::handlemsg_unregister(msg * mi, cone *c1){
	msg_unregister *m = dynamic_cast<msg_unregister*>(mi);
	dbg.print(dbg.NODE, 6, "in handlemsg_unregister\n");
	cone * c2 = NULL;
	service_t::const_iterator i = services.find(m->getserviceid());
	if (i!=services.end()){
		c2 = i->second;	
	}
	bool bogus = false;
	if (c1!=c2){
		bogus = true;
	}
	if (c1->connection->state()!=conn::CLIENT){
		bogus = true;
	}
	if (c2 == NULL){
		bogus = true;
	}
	if (bogus){
		serviceid sid(m->getserviceid());
		dbg.print(dbg.NODE, 4, "ignoring bogus unregistration for service %s (c1 %s state %s fd %i, c2 %s state %s fd %i)\n", 
			sid.tostring().c_str(), 
			c1->connection->transportname().c_str(), 
			c1->connection->state_s().c_str(), 
			c1->connection->getfd(), 
			c2->connection->transportname().c_str(), 
			c2->connection->state_s().c_str(), 
			c2->connection->getfd());
		return;
	}
	services.erase(m->getserviceid());
	delete m;
}

/// messages which can appear on both node-node and node-client

void
node::handlemsg_data(msg * mi, cone * c){
	dbg.print(dbg.NODE, 6, "in handlemsg_data\n");
	msg_data *m = dynamic_cast<msg_data*>(mi);
	switch (c->connection->state()){
		case conn::CLIENT:
			handlemsg_data_client(m, c);
			break;
		case conn::READY:
			handlemsg_data_net(m, c);
			break;
		default:
			dbg.print(dbg.NODE, 4, "received bogus data message in state %s\n", 
					c->connection->state_s().c_str());
			delete m;
			break;
	}
}

void
node::handlemsg_data_net(msg_data *m, cone *){
	dbg.print(dbg.NODE, 6, "in handlemsg_data_net\n");
	if (m->source().empty()){
		m->setsource(_myref);
	}
	id tid = m->target();
	bool wrfinalx = wrfinal(&tid);
	bool upcall = m->upcall();
	cone * c = NULL;
	service_t::const_iterator i = services.find(m->getserviceid());
	if (i!=services.end()){
		c = i->second;
	}
	if (c && upcall){
		assert(c->connection->state()==conn::CLIENT);
		send_protected(m, c);
		delete m;
		return;
	}
	if (c && wrfinalx){
		assert(c->connection->state()==conn::CLIENT);
		m->setupcall(false);
		send_protected(m, c);
		delete m;
		return;
	}
	if (!wrfinalx){
		route(m);
		delete m;
		return;
	}
	serviceid sid = m->getserviceid();
	dbg.print(dbg.NODE, 4, "could not handle data message (dest %s, upcall %i, wrfinal %i, cone %p, service %s)\n", 
		m->target().tostring().c_str(), 
		upcall, 
		wrfinalx, 
		(void*)c, 
		sid.tostring().c_str());
	delete m;
}

void
node::handlemsg_data_client(msg_data *m, cone * c){
	dbg.print(dbg.NODE, 6, "in handlemsg_data_client\n");
	///\todo do some checking
	// set source
	if (m->source().empty())
		m->setsource(_myref);
	// send the message
	int routeret = route(m);
	if (routeret == 0){ //failure
		m->setupcall(false);
		send_protected(m, c); // just return it
	}
	delete(m);
}


void
node::handlemsg_getinfo(msg *m, cone *c){
	dbg.print(dbg.NODE, 6, "in handlemsg_getinfo\n");
	msg_info * mi = new msg_info;
	mi->setsource(_myref);
	buf b;
	char * str;
	int len;
	connlist_iter i;
	sortcones(_myref);
	for (i = connlist.begin(); i != connlist.end(); i++){
		len = asprintf(&str, "conntable: otherend %s, connstate %s, fd %i\n", 
				(*i)->otherend.tostring().c_str(), 
				(*i)->connection->state_s().c_str(), 
				(*i)->connection->getfd());
		if (len != -1) {
			b.put((uint8*)str, len);
			free(str);
		} else {
			b.put((uint8*)"OOM", 4 /*length of OOM*/); // don't bother when we out of memory
		}
	}
	mi->setdata(&b);
	send_protected(mi, c);
	delete mi;
	delete m;
}

void
node::handlemsg_info(msg *m, cone *){
	dbg.print(dbg.NODE, 5, "received a msg_info; how nice\n");
	delete m;
}


/////////////////////
// helper functions

// linear search for a connection entry, given a callback handler id

node::cone*
node::getconebycblh(int cblh){
	connlist_iter i;
	cone * c;
	for (i = connlist.begin(); i != connlist.end(); i++){
		c = *i;
		if (c->readcallbackhandle == cblh){
			return c;
		}
	}
	return NULL;
}


// linear search for a connection entry, given socket file descriptor

node::cone*
node::getconebyfd(int fd){
	connlist_iter i;
	cone * c;
	for (i = connlist.begin(); i != connlist.end(); i++){
		c = *i;
		if (c->connection->getfd() == fd){
			return c;
		}
	}
	return NULL;
}


// helper class for sorting connection entries

class conecmp {
	public:
	conecmp(id i) { _id = i; };
	bool operator()(node::cone*& c1, node::cone*& c2){
		int cmp = _id.closer(c1->otherend, c2->otherend);
		if (cmp == +1){return false;}
		return true;
	};
	private:
	id _id;
};

void
node::sortcones(id ref){
	connlist.sort(conecmp(ref));
	return;
}

bool
node::useful_ref(const noderef &ref){
	dbg.print(dbg.NODE, 7, "checking usefulness of %s\n", ref.tostring().c_str());
	if (ref.empty()){
		dbg.print(dbg.NODE, 7, "empty ref not useful\n");
		return false;
	}
	if (ref.sameid(_myref)){
		dbg.print(dbg.NODE, 7, "ref to own id not useful\n");
		return false;
	}
	if (ref.sametransport(_myref)){
		dbg.print(dbg.NODE, 7, "ref to own transport not useful\n");
		return false;
	}
	connlist_iter i;
	cone *cur;
	int connreadycount = 0;
	for (i = connlist.begin(); i != connlist.end(); i++){
		cur = *i;
		dbg.print(dbg.NODE, 7, "check entry %s\n", cur->otherend.tostring().c_str());
		if (ref.sameid(cur->otherend)){
			dbg.print(dbg.NODE, 7, "id already there: %s\n", 
					cur->otherend.tostring().c_str());
			return false;
		}
		if (ref.sametransport(cur->otherend)){
			dbg.print(dbg.NODE, 7, "transport already there: %s\n", 
					cur->otherend.tostring().c_str());
			return false;
		}
		if (cur->connection->state() == conn::READY)
			connreadycount++;
	}
	// do some checking about the number and distribtion of connections
	if (connreadycount < param_min_connections){
		// if we have only few connections, we accept anything
		dbg.print(dbg.NODE, 7, "only %i connections -> useful\n", connreadycount);
		return true;
	}
	if (!useful_ref_distribution(ref)){ // this also iterates through all connections. can this be avoided?
		dbg.print(dbg.NODE, 7, "reference distribution says not useful\n");
		return false;
	}
	dbg.print(dbg.NODE, 7, "nothing found -> useful\n");
	return true;
}

/**
Determine whether a given ref is useful for us, given the current list of connections.
A connection is deemed useful if the ID space arround it is relatively empty.
A ref is relatively useless if we have already a lot of connections in the neighborhood of it.
 - sort by our own id
 - walk the list until we find the gap where this ref should belong
 - now we have pred and succ of this ref (from our point of view)
 - compute logdist(we, succ) - logdist(we, pred)
 - compare this value with target value (eg 2 for ordinary nodes, a value between 1 and 2 for power nodes)
 - if larger (there is a gap) return true
 - if smaller (already crowded here) return false
*/

bool
node::useful_ref_distribution(const noderef &ref){
	sortcones(_myref);
	// both are relative to ref
	cone * pred = NULL;
	cone * succ = NULL;
	dbg.print(dbg.NODE, 6, "checking usefulness of %s\n", ref.tostring().c_str());
	for (connlist_iter i = connlist.begin(); i!=connlist.end(); i++){
		cone *c = *i;
		if (c->connection->state() != conn::READY){
			continue;
		}
		if (pred == NULL){
			pred = c;
		}
		if (succ == NULL){
			succ = c;
		}
		if (pred->otherend.closer(c->otherend, ref) == -1){ /// XXX sequence?
			pred = c;
			dbg.print(dbg.NODE, 7, "updated pred to %s\n", c->otherend.tostring().c_str());
		}
		if (succ->otherend.closer(ref, c->otherend) == 1){ /// XXX ?????
			succ = c;
			dbg.print(dbg.NODE, 7, "updated succ to %s\n", c->otherend.tostring().c_str());
		}
	}
	double preddist = _myref.lddist(pred->otherend);
	double succdist = _myref.lddist(succ->otherend);
	double distdiff = preddist - succdist;
	dbg.print(dbg.NODE, 7, "preddist %f succdist %f distdiff %f\n", preddist, succdist, distdiff);
	if (distdiff > param_logarithm)
		return true;
	return false;
}

/**
check for a minimun number of connections (think bootstrapping)
return if limit not reached
sort by our own id
for each conn
	compute logdist(we, they)
for each consecutive triplet (a,b,c) of conns in this sorted list
	compute c-a
	assign this value as usefulness to b
find the connection with the minimal usefulness
check if this connection is alive for a minimum amount of time
kill it if not
*/
/*
comments: this algorithm tries to find the crowdiest point in the distribution of connections
since it computes usefulness values only for triplets of nodes, the first and the last connection (given by the metric) are automatically preseved from killing
connections are held open for a minimum amount of time to prevent churn in the routing table
*/

void
node::close_least_useful_conn(){
	// count READY connections
	int readycounter = 0;
	sortcones(_myref);
	cone *a, *b, *c;
	a=b=c=NULL;
	double minusefulness = 99999999.0;
	cone * leastuseful = 0;
	for (connlist_t::iterator i = connlist.begin(); i!=connlist.end(); i++){
		cone *cc = *i;
		if (cc->connection->state() == conn::READY){
			readycounter++;
			// shift it in
			a=b;
			b=c;
			c=cc;
			// nothing empty
			if ((a&&b&&c)){
				double usefulness = _myref.lddist(c->otherend) - _myref.lddist(a->otherend);
				if (minusefulness > usefulness){
					minusefulness = usefulness;
					leastuseful = b;
				}
			}
		}
	}
	if (!leastuseful)
		return;
	if (readycounter < param_min_connections2){
		return;
	}
	if (dbltime() - leastuseful->opensince > param_min_connection_lifetime){
		conn_close(leastuseful);
	}
}
	

///////////////////
// debug

void
node::dump_conn_list(){
	connlist_iter i;
	cone * c;
	dbg.print(dbg.NODE, 5, "dumping connection table\n");
	sortcones(_myref);
	for (i = connlist.begin(); i != connlist.end(); i++){
		c = *i;
		dbg.print(dbg.NODE, 5, "conntable: otherend %s, connstate %s, fd %i\n", 
				c->otherend.tostring().c_str(), 
				c->connection->state_s().c_str(), 
				c->connection->getfd());
	}
	dbg.print(dbg.NODE, 5, "dumping connection table finished\n");
}


