/*
 * Author:
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * $Id: id.cc,v 1.23 2006/01/05 10:24:59 kutzner Exp $
 *
 */

#include <stdlib.h>
#include <string>
using namespace std;

#include <math.h>

#include "igor.h"
#include "id.h"
#include "debug.h"
#include "utils.h"

// all ids start empty
id::id(){
	for(int i=0; i<INTS_PER_ID; i++){
		_id[i] = 0;
	}
	_idempty = true;
}

/**
 * Assign random values to all parts of an ID. The ID is valid afterwards.
 */
void
id::randomize(){
	int i;
	for(i=0; i<INTS_PER_ID; i++){
		_id[i] = (uint32)(random());
		_id[i] ^= (16*(uint32)(random())); // when rand_max is signed
	}
	_idempty = false;
}

/**
 * Create a new id with a given int as value.  This obviously can't initialize
 * all fields, it is mainly for debugging usage.
 */
id::id(int val){
	int i;
	for(i=0; i<INTS_PER_ID-1; i++){
		_id[i] = 0;
	}
	_id[INTS_PER_ID-1] = (uint32)val; // XXX cast warning
	_idempty = false;
}

/** 
 * Initialize an ID with the memory found at adress src.
 * The memory is taken literally. If the amount of memory supplied is not enough, the remaining parts of the resulting ID are filled with zeros.
 */
void
id::fromraw(const void * src, size_t len){
	id(); // initialize to zeros.
	int i;
	for(i=0; i<INTS_PER_ID; i++){
		_id[i] = 0;
	}
	int j = MIN (len, INTS_PER_ID * sizeof(int));
	memcpy(_id, src, j);
	_idempty = false;
	return;
}

/** 
 * Converts a string representation of an ID. If the parsing was successful,
 * the ID is not empty afterwards. If an error occured during parsing, The is
 * is empty.
 */
void
id::parseid(string val){
	if(val.size()!=INTS_PER_ID*8){ // rather rigid test // XXX constant warning
		_idempty = true;
		return;
	}
	string tmp;
	for (int i = 0 ; i < INTS_PER_ID ; i++){
		tmp = val.substr(8*i, 8*i+8);
		int scanret = sscanf(tmp.c_str(), "%8x", &_id[i]);
		if (scanret != 1){
			_idempty = true;
			return;
		}
	}
	_idempty = false;
	return;
}

/**
 * This is main metric function.
 * It is currently implemented is a XOR metric.
 * \param id1 the first foreign ID
 * \param id2 the second one.
 * \return
 *  - 0 not decidable (empty, both foreign IDs empty, both foreigns equal)
 *  - -1 first id is closer to this (real or id2 empty)
 *  - 1 second id is closer to this (real or id1 emtpy)
 */
int
id::closer(const id *id1, const id *id2) const {
	dbg.print(dbg.ID, 6, "%s.closer(%s,%s)\n", tostring().c_str(), id1->tostring().c_str(), id2->tostring().c_str());
	if (empty()){
		dbg.print(dbg.ID, 7, "this is empty, ret 0\n");
		return 0;
	}
	if (id1->empty() && id2->empty()){
		dbg.print(dbg.ID, 7, "both are empty, ret 0\n");
		return 0;
	}
	if (id1->empty()){
		dbg.print(dbg.ID, 7, "id1 empty, ret 1\n");
		return 1;
	}
	if (id2->empty()){
		dbg.print(dbg.ID, 7, "id2 empty, ret -1\n");
		return -1;
	}
	int i;
	uint32 t1, t2;
	for(i=0; i<INTS_PER_ID; i++){
		t1 = _id[i] ^ id1->_id[i];
		t2 = _id[i] ^ id2->_id[i];
		dbg.print(dbg.ID, 7, "round %i, t1 %08x t2 %08x\n", i, t1, t2);
		if (t1<t2){
			dbg.print(dbg.ID, 7, "t1 smaller, ret -1\n");
			return -1;
		}
		if (t1>t2){
			dbg.print(dbg.ID, 7, "t1 bigger, ret +1\n");
			return 1;
		}
	}
	dbg.print(dbg.ID, 7, "everything equal, ret 0\n");
	return 0;
}

/**
 * Convert internal representation to human readable and network transportable
 * format. This format is an hex string of 8 character with lower case letters
 * for every integer. In case the ID is empty, "--EMPTY-ID--" is returned.
 */
string
id::tostring() const {
	if (empty()){
		return "--EMPTY-ID--";
	}
	char buf[10]; // for one integer
	string s;
	for (int i = 0 ; i < INTS_PER_ID ; i ++){
		snprintf(buf, 10, "%.8x", _id[i]);
		s.append(buf);
	}
	return s;
}

/**
 * Set to a foreign value.
 */
void
id::change(const id & n){
	if (*this == n){
		return;
	}
	dbg.print(dbg.ID, 5, "id change from %s to %s\n", tostring().c_str(), n.tostring().c_str());
	*this = n;
	return;
}

/**
 * \return true if two IDs are identical.
 */
bool
id::operator==(const id&other) const {
	if (_idempty!=other._idempty){
		return false;
	}
	if (_idempty){
		return true;
	}
	for (int i = 0 ; i < INTS_PER_ID ; i++){
		if (_id[i]!=other._id[i]){
			return false;
		}
	}
	return true;
}
	
/**
 * Changes just one bit of the internal representation. This creates an ID with
 * a "logarithmic random distance" to the orginal. That procedure is needed in
 * IGOR routing tables. Which bit is flipped is selected at random.
 */
void
id::create_random_log_fingerdest(){
	int bit = random()%(INTS_PER_ID*32);
	dbg.print(dbg.ID, 6, "creating random finger; flip bit %i\n" , bit);
	create_log_fingerdest(bit);
}

/**
 * Changes just one bit of the internal representation. This creates an ID with
 * a "logarithmic random distance" to the orginal. That procedure is needed in
 * IGOR routing tables.
 * \param bit Which bit is flipped.
 */
void
id::create_log_fingerdest(int bit){
	int pos = bit / 32;
	int count = bit % 32;
	uint32 mask = 1;
	while (count > 0) {
		mask = mask << 1;
		count--;
	}
	dbg.print(dbg.ID, 7, "before flipping: %s\n", tostring().c_str());
	dbg.print(dbg.ID, 6, "creating finger; flip bit %i (mask %x) of int %i\n" , bit%32, mask, pos);
	_id[pos] = _id[pos] ^ mask;
	dbg.print(dbg.ID, 7, "after flipping: %s\n", tostring().c_str());
}

/**
 * Computes the (binary) logarithm of the distance of two IDs using the XOR metric.
 * It works by converting the IDs to double values. If INTS_PER_ID is too large, this routine will fail.
 * \return -1 if one of the IDs is empty or they are equal.
 */
double
id::lddist(id&other){
	if (_idempty)
		return -1.0;
	if (other._idempty)
		return -1.0;
	if (operator==(other))
		return -1.0;
	double ld = 0;
	for (int i = 0; i < INTS_PER_ID; i++){
		unsigned int x = _id[i] ^ other._id[i];
		ld += 1.0 * x;
		if (i<INTS_PER_ID -1){
			ld *= 4294967296.0;  ///\bug assume 32 bit integer
		}
	}
	return log(ld)/log(2);
}

