#include "consistent_hash.h"

#include <assert.h>

consistent_hash::id_type 
consistent_hash::string_to_id(std::string lock) {
	id_type id  = 0;
	for (unsigned int i = 0; i < lock.size(); i++) {
		id = id * 10 + lock.at(i) - '0';
	}
	return id;
}

consistent_hash::consistent_hash(std::list<member> lst) {

	mems = lst;
	assert(pthread_mutex_init(&ch_lock, NULL)==0);
	
}

consistent_hash::consistent_hash() {
	assert(pthread_mutex_init(&ch_lock, NULL)==0);
	
}

//==========================================================================================


//LOOKUP

// hash of object after the one that is responsible for it
member consistent_hash::lookup_hash(double h) //returns the member to whom id maps to
{
		
	member ret;
	
	assert(pthread_mutex_lock(&ch_lock) == 0);
	

	std::list<member>::iterator it = mems.begin();
	std::list<member>::iterator oldit = mems.begin();

	while ((it != mems.end()) && (h >= hash(*it)) ) {
		oldit = it;
		it++;
	}
	if ((it == mems.begin()) || (it == mems.end())) {
		ret = mems.back();
		
	} else {
	
		ret = *oldit;
	}
	
	assert(pthread_mutex_unlock(&ch_lock) == 0);

	return ret;
}



member consistent_hash::lookup(std::string id) 
{
	return lookup_hash(hash(string_to_id(id)));
}
member consistent_hash::lookup(id_type id) //returns the member to whom id maps to
{
	return lookup_hash(hash(id));
}

member consistent_hash::lookup_previous(member m) //looks up the member before m in the ring
{	
	member ret;

	
	assert(pthread_mutex_lock(&ch_lock) == 0);
	
	std::list<member>::iterator it = mems.begin();
	std::list<member>::iterator oldit = mems.begin();

	while ((it != mems.end()) && (!it->equals(m))) {
		oldit = it;		
		it++;
	}
	if (it == mems.end()) {
		ret = 0;
	} else {
		if (it->equals(mems.front())) {
			ret = mems.back();
		} else {
			ret = *oldit;	
		}
	}
	
	
	assert(pthread_mutex_unlock(&ch_lock) == 0);
	
	return ret;
}

member consistent_hash::lookup_next(member m) //looks up the member before m in the ring
{
	
	member ret;
	
	assert(pthread_mutex_lock(&ch_lock) == 0);
	
	std::list<member>::iterator it = mems.begin();

	while ((it != mems.end()) && (!it->equals(m))) {		
		it++;
	}
	if (it == mems.end()) {
		ret = 0;
	} else {
		if (it->equals(mems.back())) {
			ret = mems.front();
		} else {
			ret = *(++it);	
		}
	}
	
	
	assert(pthread_mutex_unlock(&ch_lock) == 0);

	return ret;
}
		

//===========================================================================================


// MEMBERSHIP 
void consistent_hash::set_membership(std::list<member> lst){
	//printf("before locking in set membership\n");
	assert(pthread_mutex_lock(&ch_lock) ==0);
	//printf("after locking in set membership\n");
	mems.clear();
	std::list<member>::iterator it = lst.begin();
	while(it != lst.end()) {
		add_my_member(*it);
		it++;
	}
	
	assert(pthread_mutex_unlock(&ch_lock) ==0);
}


//prencondition: ch_lock held
bool consistent_hash::add_my_member(member m) {

	bool ret;
	
	
	
	std::list<member>::iterator it = mems.begin();
	while (it != mems.end()) {
		if (it->equals(m)) {	
			ret = false;
			goto end_add;
		}
		if (hash(*it) > hash(m)) {
			mems.insert(it, m);
			ret = true;
			goto end_add;
		}
		it++;
	}
	mems.push_back(m);
	ret = true;

end_add:
	
	
	return ret;
}

bool consistent_hash::add_member(member m) {

	assert(pthread_mutex_lock(&ch_lock) ==0);
	
	bool ret = add_my_member(m);
	assert(pthread_mutex_unlock(&ch_lock) ==0);
	return ret;
}




bool consistent_hash::contains_member(member m) {

	bool ret = false;

	assert(pthread_mutex_lock(&ch_lock) == 0);
	
	std::list<member>::iterator it = mems.begin();
	while (it != mems.end()) {
		if (it->equals(m)) {
			
			ret = true;
			break;
		}
		it++;
	}	
	
	assert(pthread_mutex_unlock(&ch_lock) == 0);
	return ret;
}


bool consistent_hash::remove_member(member m) {

	bool ret = false;

	assert(pthread_mutex_lock(&ch_lock) == 0);
	
	
	std::list<member>::iterator it = mems.begin();
	while (it != mems.end()) {
		if (it->equals(m)) {
			mems.erase(it);
			ret = true;
			break;
		}
		it++;
	}	
	
	assert(pthread_mutex_unlock(&ch_lock) == 0);
	return ret;
}


//============================================================================================

//HASHING



double consistent_hash::hash(member m) {
	return (((m.get_client_id() ) % 35000) * 1.0) /  35000; //((m.get_client_id() * prime1 + prime2) % prime3) * 1.0 / prime3;	
}
double consistent_hash::hash(id_type id) {
	return 	((id * prime1) % prime3) * 1.0 / prime3;	
}



//=============================================================================================

void consistent_hash::print_mems() {

	printf("members are:");
	std::list<member>::iterator it = mems.begin();
	while (it != mems.end()) {
		printf("%d ", it->get_client_id());
		it++;
	}	
}
