// yfs remove server

#include "yfs_server.h"
#include "extent_client.h"
#include <sstream>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>


//TODO TO REPAIR:
// double locking where is needed and cache flushing accordingly
// extent should not know structure
// improve efficiency of locking system as well as the small artifices in the lock system

/* STRING LAYOUT OF FILES AND DIRECTORIES
The structure of the file is. The structure is HARD, space means one space.
 [ space, size of name, space, name, space, mode, space, device, space, contents ]
Directory structure:
  [space, size of name of dir, space, name of dir, space, mode, space,{here the files start} size, space, name, space, inode number -- same thing can repeat]
 we also put the size of the name on the string because we want the program to still work for filenames with
 spaces in them
 If any entry is empty then it is one space.
 */

const std::string GLOBAL_LOCK = "globallock"; //NOT used 

extent_protocol::extentid_t string_to_eid(std::string lock) {
	extent_protocol::extentid_t eid = 0;
	for (int i = 0; i < lock.size(); i++) {
		eid = eid * 10 + lock.at(i) - '0';
	}
	return eid;
}

void yfs_lock_release_user::dorelease(std::string lock) {
	
	if (ec->flush(string_to_eid(lock))  < 0 ) {
		printf("ERROR with flush in do release of lock %s \n", lock.c_str());
	}
}

yfs_lock_release_user::yfs_lock_release_user(extent_client * ec) : lock_release_user() {
	this->ec = ec;
}



std::string to_string(yfs_protocol::inum inum) {
	char c[200];
	sprintf(c,"%llu",inum);
	std::string s(c);
	return s;
}

//starts reading from the number that indicates the length of the string and after has the index of the next such number
yfs_protocol::inum read_nr(unsigned int start, std::string str, unsigned int & after)
{
	unsigned int i = start;
	yfs_protocol::inum 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;
}


yfs_server::yfs_server(struct sockaddr_in extent_dst, 
		       struct sockaddr_in lock_dst,
		       int rlock_port)
{
		
  ec = new extent_client(extent_dst);
  lock_release_user * lu =  new yfs_lock_release_user(ec);
  el = new lock_client_cache(lock_dst, lu);
  last_inum = 1;
  
}

yfs_protocol::inum
yfs_server::inum(std::string n)
{
  std::istringstream ist(n);
  unsigned long long finum;
  ist >> finum;
  return finum;
}

std::string
yfs_server::filename(yfs_protocol::inum inum)
{
  std::ostringstream ost;
  ost << inum;
  return ost.str(); 
}

bool
yfs_server::isfile(yfs_protocol::inum inum)
{
  if(inum & 0x80000000)
    return true;
  return false;
}

bool 
yfs_server::isdir(yfs_protocol::inum inum)
{
  return ! isfile(inum);
}

int yfs_server::mygetfile(yfs_protocol::inum inum, yfs_protocol::fileinfo &fin)
{
	
	
	
  int r = yfs_protocol::OK;
  std::string f = filename(inum);


  //printf("getfile %016llx\n", inum);
  extent_protocol::attr a;
  if (ec->getattr(inum, a) != extent_protocol::OK) {
//    printf("leaving yfs server get file with IOERR \n");
    r = yfs_protocol::IOERR;
    return r;
  }

  fin.atime = a.atime;
  fin.mtime = a.mtime;
  fin.ctime = a.ctime;
  fin.size = a.size;
  
  //printf("leaving yfsserver getfile with OK returns inum %016llx -> size %llu\n", inum, fin.size);
  
  	
  	return r;
}
int yfs_server::getfile(yfs_protocol::inum inum, yfs_protocol::fileinfo &fin) {
	std::string inum_string = to_string(inum);
	el->acquire(inum_string);
	//printf( "acquired: entering  yfs_server get file, called for inum = %llu \n", inum);
	yfs_protocol::status ret = mygetfile(inum, fin);
	//printf("yfs_server: getfile for inum %llu returns modified time %ld \n", inum, fin.mtime);
	el->release(inum_string);
	return ret;
}


int yfs_server::mygetdir( yfs_protocol::inum inum, yfs_protocol::dirinfo &din)
{
  
	
  int r = yfs_protocol::OK;
  std::string d = filename(inum);


  //printf("inode in hex is %016llx\n", inum);
  extent_protocol::attr a;
  if (ec->getattr(inum, a) != extent_protocol::OK) {
    r = yfs_protocol::IOERR;
    //printf("returning with IO error from yfs_server get dir\n");
    return r;
  }
  din.atime = a.atime;
  din.mtime = a.mtime;
  din.ctime = a.ctime;

 
//  printf("returning with OK from yfs_server get dir; \n");
  	
  return r; 	
}

int yfs_server::getdir(yfs_protocol::inum inum, yfs_protocol::dirinfo &din) {
	std::string inum_string = to_string(inum);
	el->acquire(inum_string);
	//printf("acquired: yfs_server get dir for inode %llu \n", inum);
	int ret = mygetdir( inum, din);
	el->release(inum_string);
	//printf("yfs_server: getdir %llu returns modified time %ld \n", inum, din.mtime);
	return ret;
	
}


std::string read_name(std::string buf, unsigned int & end) {
	while ((end < buf.size()) && (buf.at(end) == ' ')) {
		end++;
	}
	return read_name(end, buf, end);
}

yfs_protocol::inum read_nr(std::string buf, unsigned int &end) {
	while ((end < buf.size()) && (buf.at(end) == ' ')) {
		end++;
	}
	return read_nr(end, buf, end);
}


//appends [space, size of name, space, name]
void write_name(std::string & value, std::string name) {
	value.append(" ");
	char aux[100];
	sprintf(aux, "%d", name.size());
	value.append(aux);
	value.append(" ");
	value.append(name);
}

//appends [space, nr]
void write_nr(std::string & value, yfs_protocol::inum inum) {
	value.append(" ");
	char aux1[100];
	sprintf(aux1, "%llu", inum);
	value.append(aux1);
}

void write_nr(std::string & value, long long nr) {
	value.append(" ");
	char aux1[100];
	sprintf(aux1, "%lld", nr);
	value.append(aux1);
}

void write_nr(std::string & value, unsigned int nr) {
	value.append(" ");
	char aux1[100];
	sprintf(aux1, "%u", nr);
	value.append(aux1);
}




int yfs_server::readdir(yfs_protocol::inum inum, std::list<yfs_protocol::dirent> &lst) {
	
	std::string inum_lock = to_string(inum);
	el->acquire(inum_lock);
	int r = yfs_protocol::OK;
	std::string d = filename(inum);
	unsigned int size, i;
	
	//printf("entering yfs server read dir for inum %llu\n", inum);
	std::string buf;
	if (ec->get(inum, buf) != extent_protocol::OK) {
		printf("leaving yfs server read dir with error 1\n");
		r = yfs_protocol::IOERR;
		goto readdir_end;
		//return r;
	}
	size = buf.size();

	lst.clear();	
	
	i = 1;
	read_name(buf, i);//go past name
	read_nr(buf,i);//go past mode
	while (i < size) {
		
		while ((i < size) && (buf.at(i) == ' ') ) {
			i++;
		}
		if (i >= size) break;
		//reads the name
		std::string name  = read_name(i, buf, i); 
		
		//i points to the beginning of the inode number
		while ((i < size) && (buf.at(i) == ' ') ) {
			i++;
		}
		if (i >= size) break;
		//now i points to the string
		yfs_protocol::inum inum = read_nr(i, buf, i);
		
		yfs_protocol::dirent e;
		e.name = name;
		e.inum = inum;
		lst.push_back(e);
		
	}
	
	//printf("leaving yfs server  read dir with OK nr of entries is %d\n", lst.size());
	
	readdir_end:
		el->release(inum_lock);
		return r;

}

int yfs_server::mknod(yfs_protocol::inum parent, std::string name, mode_t mode, dev_t dev, yfs_protocol::inum & inum) {
	//if (DEBUG) printf("in mknod, waiting to acquire locks: %llu %s \n", parent, name.c_str());
	
	//acquire locks in alphabetical order to prevent deadlocks
	std::string inum_string = to_string(parent);
	std::string old_name = name;
	el->acquire(inum_string);
	/*if (inum_string.compare(old_name) < 0) {
		el->acquire(inum_string);
		printf("mknod acquired %s \n", inum_string.c_str());
		el->acquire(old_name);
		printf("mknod acquired %s \n", old_name.c_str());
	} else {		
		el->acquire(old_name);
		printf("mknod fd %s \n", old_name.c_str());
		el->acquire(inum_string);
		printf("mknod acquired %s \n", inum_string.c_str());
		
}*/
	
	//if (DEBUG) printf("mknod acquired! entering yfs_server mknod; inum-parent= %llu name=%s  \n",parent, name.c_str());
	
	std::string parent_value, value, buf;
	
	
	//first check if the file already exists
	yfs_protocol::status ret = mylookup(parent, name, inum);
	if (ret == yfs_protocol::OK) {
	//	printf("mknod: inode exists so we do not create it \n");
		goto mknod_end;
		//return ret;
	}
	
	if (ret != yfs_protocol::NOENT) {
		goto mknod_end;
		//return ret;
	} 	
	;
	//find an inode that was not used before
	
	last_inum++;
	inum = (50*(getpid()-1) + last_inum)  | 0x080000000;
	//if (DEBUG) printf("mknod before the while of determining id\n");
	while ((ret = ec->get(inum, buf)) != extent_protocol::NOENT) {
		if (ret != extent_protocol::OK) {
			goto mknod_end;
			//return ret;
		}
		last_inum++;
		inum = (50*getpid() + last_inum)  | 0x080000000;
	}
	//if (DEBUG) printf("mknod after the while of determining id\n");
	ret = yfs_protocol::OK;
	//printf("in mknod of yfs_server inum becomes %llu\n", inum);
	value = std::string("");
	write_name(value, name);		
	write_nr(value, mode);
	write_nr(value, dev);
	value.append(" ");
	//printf("yfs server: mknod: value we put is %s \n", value.c_str());
	ret = ec->put(inum, value);
	if (ret != yfs_protocol::OK) {		
	//	printf("mknod of yfs_server returns with IOERR 1\n");
		ret = yfs_protocol::IOERR;
		goto mknod_end;
		//return ret;
	}
	if (DEBUG) printf("cm3\n");
	
	ret = ec->get(parent, parent_value);
	if (ret != yfs_protocol::OK) {
	//	printf("mknod of yfs_server returns with IOERR 2\n");
		ret = yfs_protocol::IOERR;
		goto mknod_end;
		//return ret;
	}
	if (DEBUG) printf("cm4\n");
	//printf("parent value contained: %s and now is ", parent_value.c_str());
	write_name(parent_value, name);
	write_nr(parent_value, inum);
	
	//printf(" %s\n",parent_value.c_str() );
	
	//put overwrites according to specification
	ret = ec->put(parent, parent_value);
	if (ret != yfs_protocol::OK) {
		ret = yfs_protocol::IOERR;
	//	printf("mknod of yfs_server returns with IOERR 3\n");
		goto mknod_end;
		//return ret;
	}
	
	
	
	mknod_end: 
//	if (DEBUG) printf("mknod RELEASES %s \n", inum_string.c_str());
	
	
	el->release(inum_string);
	/*	el->release(inum_string);
			el->release(old_name);*/
		//printf("released: mknod: %s %s, return with OK\n", inum_string.c_str(), old_name.c_str());
		return ret;
	
}

int yfs_server::mkdir(yfs_protocol::inum parent, std::string name, mode_t mode, yfs_protocol::inum & inum) {
	
//	if (DEBUG) 
//	printf("entering yfs_server mkdir; inum-parent= %llu name=%s  \n",parent, name.c_str());
	//acquire locks in alphabetical order to prevent deadlocks
	std::string inum_string = to_string(parent);
	
	el->acquire(inum_string);
	/*if (inum_string.compare(name) < 0) {
		el->acquire(inum_string);
		el->acquire(name);
	} else {		
		el->acquire(name);
		el->acquire(inum_string);
}*/
//	if (DEBUG) printf("mkdir ACQUIRED! %s \n", inum_string.c_str());
	std::string parent_value;
	std::string value("");//empty it has no values
	std::string buf;
	
	//check if already created	
	yfs_protocol::status ret = mylookup(parent, name, inum);
	if (ret == yfs_protocol::OK) {
		
		goto mkdir_end;
	}
	if (ret != yfs_protocol::NOENT) {
		goto mkdir_end;
	}
	
	
	
	ret = yfs_protocol::OK;
	
	//find an inode that was not used before
	
	last_inum++;
	inum = (50*(getpid()-1) + last_inum);
//	if (DEBUG) printf("mkdir before the while of determining id\n");
	while ((ret = ec->get(inum, buf)) != extent_protocol::NOENT) {
		if (ret != extent_protocol::OK) {
			goto mkdir_end;
			//return ret;
		}
		last_inum++;
		inum = (50*getpid() + last_inum);
	}
//	if (DEBUG) printf("mkdir after the while of determining id\n");			
	
	write_name(value, name);
	write_nr(value, mode);
	ret = ec->put(inum, value);
	if (ret != yfs_protocol::OK) {
	//	printf("mkdir of yfs_server returns with IOERR 1\n");
		ret = yfs_protocol::IOERR;
		goto mkdir_end;
		//return ret;
	}
	
	
	ret = ec->get(parent, parent_value);
	if (ret != yfs_protocol::OK) {
	//	printf("mkdir of yfs_server returns with IOERR 2\n");
		ret = yfs_protocol::IOERR;
		goto mkdir_end;
		//return ret;
	}
	
//	printf("parent value contains %s ", parent_value.c_str());
	//now we need to update the parent
	write_name(parent_value, name);
	write_nr(parent_value, inum);
	
//	printf(" and now contains %s\n",parent_value.c_str() );
	
	//put overwrites according to specification
	ret = ec->put(parent, parent_value);
	if (ret != yfs_protocol::OK) {
		ret = yfs_protocol::IOERR;
//		printf("mkdir of yfs_server returns with IOERR 3\n");
		goto mkdir_end;
		//return ret;
	}
	
	
//	printf("mkdir of yfs_server returns with OK\n");
	mkdir_end:
//			if (DEBUG) printf("mkdir RELEASES %s \n", inum_string.c_str());
			el->release(inum_string);
		
		//el->release(name);
		return ret;
	
}



		
//if entry not found then inum becomes 0		
int yfs_server::mylookup(yfs_protocol::inum parent, std::string name, yfs_protocol::inum & inum){
	
	
	int ret = yfs_protocol::OK;
	std::string parent_value;
	
	int r = ec->get(parent, parent_value);
	if (r != extent_protocol::OK) {
		ret = yfs_protocol::IOERR;
		return ret;
	}
	
	unsigned int size = parent_value.size();
	bool found = false;
	unsigned int i = 1;
	read_name(parent_value, i);
	read_nr(parent_value, i);
	
	//start from 1 because the first character is a space
	for (; i < size; ) {
		while ((i < size) && (parent_value.at(i) == ' ') ) {
			i++;
		}
		if (i >= size) break;
		std::string a_name = read_name(i, parent_value, i); //second i is passed by reference
		while ((i < size) && (parent_value.at(i) == ' ') ) {
			i++;
		}
		if (i >= size) break;
		yfs_protocol::inum nr = read_nr(i, parent_value, i); 
		//printf("in while: name read is %s inode %llu loop  index %d total size %d\n", a_name.c_str(), nr, i, size);
		if (name.compare(a_name) == 0) {
			inum = nr;
			found = true;
			break;
		}  
		
	}
	
	if (!found) {
		ret = yfs_protocol::NOENT; //no entry found
//		printf("leaving lookup with NOENT \n");
		return ret;
	}
	
//	printf("leaving yfs server lookup, with OK, inum found is %llu \n", inum);
	return ret;
}

int yfs_server::lookup(yfs_protocol::inum parent, std::string name, yfs_protocol::inum & inum) {
//	printf("waiting to acquire yfs server lookup, parent inode: %llu, name of file to lookup: %s\n", parent, name.c_str());
	
	//acquire locks in alphabetical order to prevent deadlocks
	std::string inum_string = to_string(parent);
	el->acquire(inum_string);
	/*if (inum_string.compare(name) < 0) {
		el->acquire(inum_string);
		printf("lookup acquired %s \n", inum_string.c_str());
		el->acquire(name);
		printf("lookup acquired %s \n", name.c_str());
	} else {		
		el->acquire(name);
		printf("lookup acquired %s \n", inum_string.c_str());
		el->acquire(inum_string);
		printf("lookup acquired %s \n", name.c_str());
	}
	printf("acquired: yfs server lookup parent inode: %llu, name of file to lookup: %s\n", parent, name.c_str()); */
	yfs_protocol::status ret = mylookup( parent, name, inum);
		
	/*el->release(name);
	el->release(inum_string); */
	//printf("released: yfs server lookup %s %s\n", inum_string.c_str(), name.c_str());
	el->release(inum_string);
	return ret;
}

int yfs_server::setattr(yfs_protocol::inum inode, int  to_set, yfs_protocol::fileinfo  attr, int & r) {
	std::string inode_lock = to_string(inode);	
//	if (DEBUG) printf("setattr  for inum %llu wants to acquire %s \n", inode, inode_lock.c_str());
	el->acquire(inode_lock);
	yfs_protocol::status ret = yfs_protocol::OK; 
//	if (DEBUG) printf("setattr ACQUIRED %s \n", inode_lock.c_str());
	if (to_set & SIZE) {
//		printf("entering yfs_server:set attr: needs to set SIZE\n");
		
		if (isdir(inode)) {
			printf("leaving setattr in yfs server with IOERR 2: trying to resize directory \n");
			ret = yfs_protocol::IOERR;
			goto setattr_end;
			//return ret;
		}
		//it is a file
		
		yfs_protocol::status ret;
		extent_protocol::status r;
		
		yfs_protocol::fileinfo fi;
		r = mygetfile(inode, fi);
		if (r != extent_protocol::OK) {
//			printf("leaving setattr in yfs server with IOERR 1 \n");
			ret = yfs_protocol::IOERR;
			goto setattr_end;
			//return ret;
		}
			
		unsigned long long new_size = attr.size;
		unsigned long long old_size = fi.size;
		fi.size = new_size;
		
		if (new_size != old_size) {
			printf("need to resize \n");
			std::string value;
			r = ec->get(inode, value);
			if (r != extent_protocol::OK) {
//				printf("leaving set attr returning with IOERR3 \n");
				ret = yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
				
			}
//			printf("yfs server old size %lld new size %lld \n", old_size, new_size);
			if (new_size > old_size) {
				//need to padd
//				printf("yfs server padding\n");
				for (unsigned int i = old_size; i < new_size; i++) {
					value.append("0");
				}
//				printf("done padding\n");
			} else {
				//need to truncate
//				printf("yfs server truncating\n");
				std::string new_value ="";
//				printf("check 1\n");
				int old_total_size = value.size();
//				printf("check 2\n");
//				printf("old value before truncating is %s \n", value.c_str());
				for (int i = 0; i < old_total_size-old_size+new_size; i++ ) {
					new_value.push_back(value.at(i));
				}
//				printf("check 3\n");
				value = new_value;	
//				printf("new value after truncating is %s \n", value.c_str());			
			}			
			r = ec->put(inode, value);
			if (r != extent_protocol::OK) {
//				printf("leaving set attr with IOERR 4\n");
				ret =  yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}
			
			
			
			
				
		}
				
		
		struct timeval timeval1;
		struct timezone timezone1;
		gettimeofday(&timeval1, &timezone1 );
			
		extent_protocol::attr new_attr;
		new_attr.atime = fi.atime;
		new_attr.mtime = timeval1.tv_usec;
		new_attr.ctime = fi.ctime;
		new_attr.size  = new_size;
//			printf("new size is %d \n", new_size);
		r = ec->setattr(inode, new_attr);
		
		if (r != extent_protocol::OK) {
			ret = yfs_protocol::IOERR;
			goto setattr_end;
			//return ret;
		}
		
		
		
		
	}
	if (to_set & ATIME) {
//		printf("entering yfs_server:set attr: needs to set ATIME\n");
		if (isfile(inode)) {
			yfs_protocol::fileinfo fi;
			extent_protocol::status r = mygetfile(inode, fi);
			if (r != extent_protocol::OK) {
				ret = yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}
			extent_protocol::attr new_attr;
			new_attr.atime = attr.atime;
			new_attr.mtime = fi.mtime;
			new_attr.ctime = fi.ctime;
			new_attr.size  = fi.size;
			
			r = ec->setattr(inode, new_attr);
		
			if (r != extent_protocol::OK) {
				ret = yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}
			
			
		} else {
		
			yfs_protocol::dirinfo di;
			extent_protocol::status r = mygetdir(inode, di);
			if (r != extent_protocol::OK) {
				ret = yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}
			extent_protocol::attr new_attr;
			new_attr.atime = attr.atime;
			new_attr.mtime = di.mtime;
			new_attr.ctime = di.ctime;
						
			r = ec->setattr(inode, new_attr);
		
			if (r != extent_protocol::OK) {
				ret = yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}	
		
		}
			
		
	}
	if (to_set & MTIME) {
//		printf("entering yfs_server:set attr: needs to set MTIME\n");
		
		if (isfile(inode)) {
			yfs_protocol::fileinfo fi;
			extent_protocol::status r = mygetfile(inode, fi);
			if (r != extent_protocol::OK) {
				ret = yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}
			extent_protocol::attr new_attr;
			new_attr.atime = fi.atime;
			new_attr.mtime = attr.mtime;
			new_attr.ctime = fi.ctime;
			new_attr.size  = fi.size;
			
			r = ec->setattr(inode, new_attr);
		
			if (r != extent_protocol::OK) {
				ret = yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}
			
			
		} else {
		
			yfs_protocol::dirinfo di;
			extent_protocol::status r = mygetdir(inode, di);
			if (r != extent_protocol::OK) {
				ret =  yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}
			extent_protocol::attr new_attr;
			new_attr.atime = di.atime;
			new_attr.mtime = attr.mtime;
			new_attr.ctime = di.ctime;
						
			r = ec->setattr(inode, new_attr);
		
			if (r != extent_protocol::OK) {
				ret = yfs_protocol::IOERR;
				goto setattr_end;
				//return ret;
			}	
		
		}
			
			
		
	}
	if ((to_set & GID) != 0 ) {
//		printf("entering yfs_server:set attr: needs to set GID\n");
		
//		printf("UNIMPLEMENTED! nothing to be done here \n");
	}
	if ((to_set & UID) != 0 ) {
//		printf("entering yfs_server:set attr: needs to set UID\n");
		
//		printf("UNIMPLEMENTED! nothing to be done here \n");
	}
	if ((to_set & MODE) != 0 ) {
//		printf("entering yfs_server:set attr: needs to set MODE\n");
		
//		printf("UNIMPLEMENTED! nothing to be done here \n");
	}
	
//	printf("leaving yfs_server put attribute with OK\n");
	
	setattr_end:
			printf("leaving setattr for inum %llu \n", inode);
		el->release(inode_lock);
		return ret;
}


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 "";
	}
	
	
	
}
int yfs_server::read(yfs_protocol::inum inum, std::string &value) {
		
	
	std::string inum_lock = to_string(inum);
//	if (DEBUG) printf("waiting to acquire in  read in yfs server %s\n", inum_lock.c_str());
	el->acquire(inum_lock);
//	if (DEBUG) printf("read: ACQUIRED  %s\n", inum_lock.c_str());
	
	yfs_protocol::status ret = yfs_protocol::OK;
	std::string full_value;
	extent_protocol::status r;
	
	if (isdir(inum)) {
//		if (DEBUG) printf("read in yfs server is given a directory \n");
		ret = yfs_protocol::IOERR;
		goto read_end;
		//return ret;
	}
	//we deal with a file: space, size of name, space, name, space, mode, space, device, space, contents
	
	
	r = ec->get(inum, full_value);
	if (r != extent_protocol::OK) {
		ret = yfs_protocol::IOERR;
//		printf("returning from yfs server read with error\n");
		goto read_end;
		//return ret;
		
	}
	//printf("in yfs server full value is %s \n", full_value.c_str());
	value = get_contents(full_value);
	//printf("leaving read in yfs server with ok, value is %s \n", value.c_str());
	
	read_end:
//			if (DEBUG) printf("read RELEASED %s \n", inum_lock.c_str());
			el->release(inum_lock);
	
		return ret;
	
	
}

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;
}




std::string change_contents(std::string value, std::string buf, off_t off ) {
	
	
	//printf("in change_contents: value %s buf %s \n", value.c_str(), buf.c_str());
	//printf("off %lld  \n", off);
	std::string content = get_contents(value);
	//printf("contents are %s\n", content.c_str());
	
	//determine position of content
	unsigned int pos = get_content_position(value);
	//printf("pos of content is %u\n", pos);
	
	//modify the contents
	if (off < content.size()) {
		content.replace(off, buf.size(), buf);
	} else {
		//create the new content
		int content_size = content.size();
		for (int i = content_size; i < off; i++) {
			content.push_back('\0');
		}
		for (int i = 0; i < buf.size(); i++) {
			content.push_back(buf.at(i));
		}
	}
	
	//add the new content
	value.replace(pos, content.size(), content);
	//printf("in change_contents: new value is %s\n", value.c_str());
	return value;
		
	

}


int yfs_server::write(yfs_protocol::inum inum, std::string buf, unsigned long long off,  int & res) {
	
	yfs_protocol::status ret = yfs_protocol::OK;
//	if (DEBUG) printf("entering write in yfs_server with inum %llu and buf %s\n", inum, buf.c_str());
	std::string inum_lock = to_string(inum);
	el->acquire(inum_lock);
//	printf("acquired!\n");
//	if (DEBUG) printf("write: ACQUIREd lock %s!\n", inum_lock.c_str());
	std::string new_value, value;
	extent_protocol::status r;
	
//	if (DEBUG) printf("cw1\n");
	if (isdir(inum)) {
//		printf("write in yfs server is given a directory \n");
		ret =  yfs_protocol::IOERR;
		goto write_end;
		//return ret;
	}
	if (DEBUG) printf("cw2\n");
	r = ec->get(inum, value);
	if (r != extent_protocol::OK) {
//		printf("leaving write in yfs server with IOEERR 1 \n");
		ret = yfs_protocol::IOERR;
		goto write_end;
		//return ret;
	}
//	if (DEBUG) printf("cw3\n");
	new_value = change_contents(value, buf, off);
//	printf("cw3.5\n");
	
	r = ec->put(inum, new_value);
	if (r != extent_protocol::OK) {
		printf("leaving write in yfs server with IOERR 2\n");
		ret = yfs_protocol::IOERR;
		goto write_end;
		//return ret;
	}
//	if (DEBUG) printf("cw4\n");
	//printf("leaving write in yfs server with ok \n");
	
	write_end:
		el->release(inum_lock);
	
	
//	printf("write released for inum %llu\n", inum);
		return ret;
}

bool remove_from_directory(std::string  & buf , yfs_protocol::inum inum) {
	//printf("in remove directory initial string is buf %s and inum to remove %llu \n", buf.c_str(), inum);
	std::string new_buf("");
	unsigned int i = 0;
	unsigned int size = buf.size();
	write_name(new_buf, read_name(buf, i));
	write_nr(new_buf, read_nr(buf, i));
	bool found = false; //indicates if the 
	
	while (i < size ) {
		std::string name = read_name(buf, i);
		yfs_protocol::inum aux_inum = read_nr(buf, i);
		if (aux_inum != inum) {
			write_name(new_buf, name);
			write_nr(new_buf, aux_inum);
		} else {
			//we do not add it to the list, and we set the found flag
			found = true;
		}
		
	}
	//printf("in remove directory final string is buf %s \n", new_buf.c_str());
	buf = new_buf;
	return found;
}
int yfs_server::unlink(yfs_protocol::inum inum_parent, yfs_protocol::inum inum, int & ) {
	
	
//	printf("entering yfs unlink with inum is %llu \n", inum);
	extent_protocol::status r;
	std::string buf;
	bool found;	
	yfs_protocol::status ret = yfs_protocol::OK;
	
	//acquire locks in alphabetical order to prevent deadlocks
	std::string inum_string = to_string(inum_parent);
	
	/*std::string inum_string2 = to_string(inum);
	if (inum_string.compare(inum_string2) < 0) {
		el->acquire(inum_string);
		el->acquire(inum_string2);
	} else {		
		el->acquire(inum_string2);
		el->acquire(inum_string);
}(*/
	el->acquire(inum_string);
//	if (DEBUG) printf("unlink ACQUIRED: %s  for inum : %llu \n", inum_string.c_str(), inum);
	
	if (isdir(inum)) {
		printf("attempting to unlink a directory: IOERR \n");
		ret = yfs_protocol::IOERR;
		goto unlink_end;
		//return ret;
	}
	
	r = ec->remove(inum);
	if (r != extent_protocol::OK) {
		printf("in yfs server remove returned no entry from extent server \n");
		ret = yfs_protocol::NOENT;
		goto unlink_end;
		//return ret;
	}
	
	//now remove from parent's listing
	r = ec->get(inum_parent, buf);
	
	if (r!=extent_protocol::OK) {
		printf("returning with IOERR from yfs server \n");
		ret = yfs_protocol::IOERR;
		goto unlink_end;
		//return ret;
	}
	
	//printf("here is the directory before ");
	found = remove_from_directory(buf, inum);
	if (!found) {
		printf("yfs server unlink: the file entry was not found in the directory\n");
		ret = yfs_protocol::IOERR;
		goto unlink_end;
		//return ret;
	}
	
	r = ec->put(inum_parent, buf);
	if (r != extent_protocol::OK) {
		printf("returning with IOERR2 from yfs server \n");
		ret = yfs_protocol::IOERR;
		goto unlink_end;
		//return ret;
	}
	
	
//	if (DEBUG) printf("leaving with OK from yfs server unlink\n");
	
	unlink_end:
//			printf("unlink RELEASES %s for inum: %llu \n", inum_string.c_str(), inum);
		el->release(inum_string);
		//el->release(inum_string2);
		return ret;
	
}


