// the extent server implementation
//TODO: the extent server does know a bit about the layout of a file (knows which is content and which are attributes in the string --- correct this by making the additional attributes of a file objects)

#include "extent_server.h"
#include <sstream>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>


extent_server::extent_server():
  nput (0), nget (0), ngetattr (0), nremove (0)
{
	data_map = new std::map<extent_protocol::extentid_t, object *>;
	std::string strg(" 1 p 0");
	int res;
	put(1,strg, res);
//	assert(pthread_mutex_init(&mutex, NULL) == 0);
	
	printf("\n Extent Server \n");
	printf("==============\n \n");
}

//starts reading from the number that indicates the length of the string and after has the index of the next such number
unsigned long long read_nr(unsigned int start, std::string str, unsigned int & after)
{
	unsigned int i = start;
	unsigned long long nr = 0;
	char c;
	
	while  ((i < str.size()) && (c = str.at(i)) != ' ') {
		char  aux[100];
		sprintf(aux, "%c", c);
		nr = nr * 10 + atoi(aux);
		i++;
	}
	//printf("in read_nr we read the number %llu ", nr);
	i++;
	after = i;
	return nr;
}




//precondition: it points to the first digit of the size number
//starts reading from the number that indicates the length of the string and after has the index of the next such number
std::string read_name(unsigned int start, std::string str, unsigned int & after) 
{
	unsigned long long len = read_nr(start, str, after);
	while ((after < str.size())&&(str.at(after)==' ')){
		after++;
	}
	if (after >= str.size()) {
		return "";
	}
	std::string name = "";
	for (unsigned int j = 0; j < len; j++) {
		char aux[100];
		sprintf(aux, "%c", str.at(after+j) );
		name.append(aux);
		
	}
	//printf("in read_name, we read the name: %s\n", name.c_str());
	after = after + len + 1;
	
	return name;
}
int get_content_position(std::string full_value) {
	unsigned int i = 0;
	
	while ((i < full_value.size()) && (full_value.at(i) == ' ')) {
		i++;
	}
	if (i < full_value.size()) {
		read_name(i, full_value, i);
	} else {
		return -1;
	}
	while ((i < full_value.size()) && (full_value.at(i) == ' ' )) {
		i++;
	}
	
	if (i < full_value.size()) {
		read_nr(i, full_value, i);
	} else {
		return -1;
	}
	while ((i < full_value.size()) && (full_value.at(i) == ' ')) {
		i++;
	}
	if (i < full_value.size()) {
		read_nr(i, full_value, i);
	} else {
		return -1;
	}
	
	

	return i;
}

void
extent_server::print_stats()
{
  if ((nput + nget + ngetattr + nremove) % 100 == 0)
    printf ("nput %d nget %d ngetattr %d nremove %d\n", nput, nget, 
	    ngetattr, nremove);
}

std::string
extent_server::filename(extent_protocol::extentid_t id)
{
  char buf[32];
  sprintf(buf, "./ID/%016llx", id);
  return std::string(buf, strlen(buf));
}

std::string get_contents(std::string full_value) {
	
	//printf("reading elements in get contents full value is %s\n", full_value.c_str());
	
	unsigned int i = 0;
	
	while ((i < full_value.size()) && (full_value.at(i) == ' ')) {
		i++;
	}
	if (i < full_value.size()) {
		std::string aux = read_name(i, full_value, i);
		//printf("get contents: an element %s \n", aux.c_str());
	} else {
		return "";
	}
	while ((i < full_value.size()) && (full_value.at(i) == ' ')) {
		i++;
	}
	
	if (i < full_value.size()) {
		read_nr(i, full_value, i);
		//printf("get contents: an element %llu \n", aux); 
	} else {
		return "";
	}
	while ((i < full_value.size()) && (full_value.at(i) == ' ')) {
		i++;
	}
	if (i < full_value.size()) {
		read_nr(i, full_value, i);
		//printf("get contents: an element %llu \n", aux); 
	} else {
		return "";
	}
	
	while ((i < full_value.size()) && (full_value.at(i) == ' ')) {
		i++;
	}
	if (i < full_value.size()) {
		std::string aux = full_value.substr(i);
		//printf("get contents: an element %s \n", aux.c_str());
		return aux;
	} else {
		return "";
	}
	
	
	
}

void print_m(std::map<extent_protocol::extentid_t, object *> * map){
	
	/*printf("---- the full extent server map is: \n");
	std::map<extent_protocol::extentid_t,object * >::iterator it = map->begin();
	while (it != map->end()) {
		printf("id: %llu buf: %s \n", it->first, it->second->value.c_str() );
		it++;
	}
	printf("--------\n");
	*/
}
int extent_server::print_map(int, int & r) {
	print_m(data_map);

	return 0;
}


//overwrites if neccessary
int extent_server::put(extent_protocol::extentid_t id, std::string buf, int &)
{
	
	
 printf("Put id %llu \n", id);
 //pthread_mutex_lock(&mutex);
  object * obj = new object;
  obj->value = buf;
  if (id & 0x80000000) {
  	obj->fi.size = buf.size()- get_content_position(buf);
  } else {
	  obj->fi.size = 0;
  }

  struct timeval timeval1;
  struct timezone timezone1;
  gettimeofday(&timeval1, &timezone1 );
  
  obj->fi.ctime =timeval1.tv_usec;
  obj->fi.atime = timeval1.tv_usec; //time(&dummy);
  std::map<extent_protocol::extentid_t, object *>::iterator it;
  if ((it = data_map->find(id)) != data_map->end()) {
	  std::string previous_content = get_contents(it->second->value);
	  if (previous_content.compare(get_contents(buf)) != 0) {
		  //change modified only if time has changed
		 
		  obj->fi.mtime = timeval1.tv_usec; //time(&dummy);
		//  printf("changing mtime, old: %d new %d \n", it->second->fi.mtime, obj->fi.mtime);
	  } else {
		  
		  //keep the old modified time setting
		 // printf("we are not changing mtime \n");
		  obj->fi.mtime = it->second->fi.mtime;
		  //obj->fi.mtime = time(&dummy);
	  }
	  data_map->erase(id);
  }   else {
	 
	  obj->fi.mtime = timeval1.tv_usec; //time(&dummy); 
	 // printf("changing a new mtime %d \n", obj->fi.mtime);
	  
  }
  data_map->insert(std::make_pair(id, obj));
  //printf("leaving put for extent server\n");
  //pthread_mutex_unlock(&mutex);
  //write(1,"back from extent server put with OK",50);
  return extent_protocol::OK;
	
	
}


extent_protocol::attr * extent_server::copy(extent_protocol::attr a)
{
	extent_protocol::attr * new_a = new extent_protocol::attr;
	new_a->ctime = a.ctime;
	new_a->mtime = a.mtime;
	new_a->atime = a.atime;
	new_a->size = a.size;
	return new_a;
}




int extent_server::remove(extent_protocol::extentid_t id, int &)
{
  printf("we are in extent server remove for id %llu; \n", id);
  //pthread_mutex_lock(&mutex);
  if (data_map->find(id) != data_map->end()) {
  
	  data_map->erase(id);
  //pthread_mutex_unlock(&mutex);
  } else 
  {//	 printf("leaving extent server remove \n");
	  return extent_protocol::NOENT;
  }
 //printf("leaving extent server remove \n");
  return extent_protocol::OK;
	
}

int extent_server::get(extent_protocol::extentid_t id, std::string &buf)
{
	   printf("Get eid %llu \n", id);	
       
	//pthread_mutex_lock(&mutex);
	std::map<extent_protocol::extentid_t, object *>::iterator it = data_map->find(id);
	if (it != data_map->end()) {
		object * obj = it->second;
		buf = obj->value;
//		printf("returning from extent server get with OK, value of buf is of %s\n",buf.c_str());
		//pthread_mutex_unlock(&mutex);
		return extent_protocol::OK;
	}
	else {
//		printf("returning from extent server get with NOENT\n");
		//pthread_mutex_unlock(&mutex);
		return extent_protocol::NOENT;
	}
}


int extent_server::getattr(extent_protocol::extentid_t id, extent_protocol::attr &a)
{

            printf("Get attr eid %llu \n", id);	
       
	//pthread_mutex_lock(&mutex);	
	std::map<extent_protocol::extentid_t, object *>::iterator it = data_map->find(id);
	if (it != data_map->end()) {
		object * obj = (data_map->find(id))->second;
		a = *copy(obj->fi);
		//printf("extent_server get attr returning ok\n");
		//pthread_mutex_unlock(&mutex);
		return extent_protocol::OK;
	} else {
		//printf("returning from extent server get attr with NOENT\n");
		//pthread_mutex_unlock(&mutex);
		return extent_protocol::NOENT;
	}
	

}

//overwrites the existing attribute; if such an attribute does not exist then it returns NOENT
int extent_server::setattr(extent_protocol::extentid_t id, extent_protocol::attr a, int & )
{	
	printf("in extent_server set attr\n");	
	//pthread_mutex_lock(&mutex);	
	std::map<extent_protocol::extentid_t, object *>::iterator it = data_map->find(id);
	
	if (it != data_map->end()) {
		object * old_obj = it->second;
		object * new_obj = new object;
		
		new_obj->value = old_obj->value;
		new_obj->fi = a;
//		printf("size to put is %d \n", a.size);
		data_map->erase(id);
				
		/* TO DO THIS OR NOT?
		time_t dummy;
		//modified time does not change
		obj->fi.ctime = time(&dummy);
		obj->fi.atime = time(&dummy);
		*/
		
		data_map->insert(std::make_pair(id, new_obj));
//		printf("returning from extent server PUT attr with OK\n");
		//pthread_mutex_unlock(&mutex);
		return extent_protocol::OK;
	} else {
//		printf("returning from extent server put attr with NOENT\n");
		//pthread_mutex_unlock(&mutex);
		return extent_protocol::NOENT;
	}
	
//	printf("ERROR in setattr should not get here\n \n");
	return extent_protocol::OK;
	
}



