// the lock server implementation

#include "lock_server.h"
#include <sstream>
#include <stdio.h>
//#include <unistd.h>
#include <arpa/inet.h>

// assumption: no failures

const bool DEBUG = true;


lock_server::lock_server():
  nacquire (0)
{
	
	assert(pthread_mutex_init(&locks_mutex, NULL) == 0);
	locks_list = new list<std::string>;
	acquire_list = new list<lock_info >;
	release_list = new list<lock_info >;
}

lock_protocol::status
lock_server::stat(std::string name, int &r)
{
  lock_protocol::status ret = lock_protocol::OK;
  printf("stat request\n");
  r = nacquire;
  return ret;
}

bool lock_taken(list<std::string> * locks_list, std::string name) {

	list<std::string>::iterator it = locks_list->begin();
	while (it != locks_list->end()) {
		if ((name.compare(*it)) == 0) {			
			return true;
		}
		it++;
	}	
	return false;
}

void take_lock(list<std::string> * locks_list, std::string name) {
	locks_list->push_back(name);
}

//effects: removes the lock called name if it is in the list
void remove_lock(list<std::string> * locks_list, std::string name) {

        list<std::string>::iterator it = locks_list->begin();
	while (it != locks_list->end()) {
		if (name.compare(*it) == 0) {
			it = locks_list->erase(it);
		} else {
		it++;
		}
	}
}

bool request_satisfied(list<lock_info > * lst, std::string name, int nonce, int pid) {
	list<lock_info >::iterator it = lst->begin();

	while (it != lst->end()) {
		if ((it->name.compare(name)==0) && (it->nonce == nonce) && (it->pid == pid)) {
			return true;
		}
		it++;
	}
	return false;
}

void add_request_satisfied(list<lock_info > * lst, std::string name, int nonce, int pid) {
	lock_info * li = new lock_info;
	li->name = name;
	li->nonce = nonce;
	li->pid = pid;
	lst->push_front(*li);
}

lock_protocol::status 
lock_server::acquire(std::string name, int nonce, int pid, int &r)
{
	//printf("request for acquire: %s \n", name.c_str());
	while (true) {
		pthread_mutex_lock(&locks_mutex);
		if (request_satisfied(acquire_list, name, pid, nonce)) {
			pthread_mutex_unlock(&locks_mutex);
			return 0;
		}
		if (!lock_taken(locks_list, name)) {
			take_lock(locks_list, name);
			add_request_satisfied(acquire_list, name, pid, nonce);
			pthread_mutex_unlock(&locks_mutex);
			printf("ACQUIRED  %s \n", name.c_str());
			printf("size of acquired list is %u and of released %u\n", acquire_list->size(), release_list->size());
			return lock_protocol::OK;
		}
		pthread_mutex_unlock(&locks_mutex);
	}
	
}


lock_protocol::status 
lock_server::release(std::string name, int nonce, int pid, int & r)
{
	//printf("request for release %s \n", name.c_str());
	pthread_mutex_lock(&locks_mutex);        
	if (request_satisfied(release_list, name, pid, nonce)) {
		pthread_mutex_unlock(&locks_mutex);
		return 0;
	}	
	if (!lock_taken(locks_list, name)) {
		pthread_mutex_unlock(&locks_mutex);
		return 1;
	} else {
		printf("RELEASED  %s \n", name.c_str());
		remove_lock(locks_list, name);
	}	 	
	add_request_satisfied(release_list, name, pid, nonce);
	pthread_mutex_unlock(&locks_mutex);
	return 0;
}

