#include "BufferedFile.h"
#ifndef BUFFEREDFILEFACTORY_H
#include "BufferedFileFactory.h"
#endif
/**
 * default time to wait
 * */
#define TIMETOWAIT 1000

extern int write(int fd,void * data, int size);
BufferedFile::BufferedFile(int fdesc)  {
	pushBackBlocks = list<CharAndSize *>();
	this->fd = fdesc;	
	parent = NULL;
	preadReady = false;
	pwriteReady = false;
	hasRead = false;
	hasWritten  = false;
	reading = writing = true;
	timeToWait = TIMETOWAIT;
}
BufferedFile::BufferedFile()  {
	parent = NULL;
	pushBackBlocks = list<CharAndSize *>();
	preadReady = false;
	pwriteReady = false;
	reading = writing = true;
	timeToWait = TIMETOWAIT;
	fd = -1;
}
void BufferedFile::setBufferedFileFactory(void * p) {
	parent = p;
}

int BufferedFile::Write(const char * data,int size) {
	hasWritten = true;
	return write(fd,(const void *)data, size);
	//i = write(this->fd,(void *)data, sizeof(char)*size);
	//return (int)i;
}
BufferedFile::~BufferedFile() {
	//cerr << "~BufferedFile\n";
	list<CharAndSize *>::iterator iter = pushBackBlocks.begin();
	while (iter != pushBackBlocks.end()) {
	//for (iter = pushBackBlocks.begin(); iter < pushBackBlocks.end(); iter++) {
		delete [] (*iter)->data;
		delete (*iter);
		iter++;
	}
	if (fd) {
		close(fd);
	}
}
void BufferedFile::pushBack(char * data,int size) {
	struct CharAndSize * c = new CharAndSize();
	c->data = data;
	c->size = size;
	c->alreadyRead = 0;
	pushBackBlocks.push_front(c);
}
int BufferedFile::Read(char * data, int size) {
	hasRead = true; //Read was called.
	int index = 0;
	if (!pushBackBlocks.empty()) {
		int si = 0;
		int sar = 0;
		int min = 0;
		int alr = 0;
		//list<CharAndSize *>::iterator iter;
		while (!pushBackBlocks.empty()) {
			CharAndSize * block = *(pushBackBlocks.begin());
			pushBackBlocks.pop_front();
			si = size-index; //bytes left to read
			min = si; 
			alr = block->alreadyRead; //already read bytes
			sar = (block->size) - alr; //bytes left in this block
			//if (sar < min) { sar = min; }
			if (sar < min) { min = sar; }
			for (int i = 0; i < min; i++) {
				data[index+i] =	block->data[alr+i];
			}
			//if (sar > si) { //We haven't read enough of a block to count!
			if (sar > si) { //We haven't read enough of a block to count!
					//Case 1 we aren't done with this
					//block
				block->alreadyRead+=min;
				pushBackBlocks.push_front(block);
				return size;
			} else if ( sar == si  ) { //We have read all we need to read!
						   //Case2 The block is all we
						   //needed!
				delete [] block->data;
				block->data = NULL;
				delete block;
				return size;
			} else if (sar < si) { //We need more blocks
				delete [] block->data;
				block->data = NULL;
				delete block;
				index+=sar;
			}
		}
	}
	if (index >= size) {
		return size;
	}
	int total = read(fd,(void *)(data+index), sizeof(char)*(size-index));
	if (total <= 0) {
		Close();
		cerr << "Closing BufferedFile " << fd << "\n";
		return -1;
	}
	if (index) {
		return total + index;
	} else {
		return total;
	}
}
//bool BufferedFile::readReady() {return ready(0);}
//bool BufferedFile::writeReady() {return ready(1);}
bool BufferedFile::readReady() {return preadReady;}
bool BufferedFile::writeReady() {return pwriteReady;}

void BufferedFile::Close() {
	if (parent!=NULL) {
		((BufferedFileFactory*)parent)->closeFile(this);
	}
	parent = NULL;
	reading = false;
	writing = false;
	if (fd > 0) {
		close(fd);
		fd = 0;
	}
}

bool BufferedFile::ready(int rw) {
	if (pushBackBlocks.size() > 0) {
		return true;
	}
	fd_set tmp_set;
	FD_ZERO (&tmp_set);
	FD_SET (fd, &tmp_set);
	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = timeToWait; 
	int currCount;
	if (!rw) {
		currCount = select(fd+1, &tmp_set, NULL, NULL, &tv);
	} else {
		currCount = select(fd+1 ,NULL, &tmp_set,  NULL, &tv);
	}
	return (currCount > 0);
}
//bool BufferedFile::ready(int rw) {
//	if (pushBackBlocks.size() > 0) {
//		return true;
//	}
//	struct pollfd polls;
//	polls.fd = fd;
//	if (rw==BUFFER_READ) {
//		polls.events = POLLIN;
//	} else if (rw == BUFFER_WRITE) {
//		polls.events = POLLOUT;
//	}
//	poll(&polls,1,timeToWait/1000);
//	if (rw==BUFFER_READ) {
//		return (polls.revents & POLLIN);
//	} else if (rw == BUFFER_WRITE) {
//		return (polls.revents & POLLOUT);
//	}
//	return false;
//}
void BufferedFile::setWait(int w) {
	timeToWait = w;
}
int BufferedFile::getWait() {
	return timeToWait;
}
int BufferedFile::getFileHandle() {
	return fd;
}
void BufferedFile::setWriteReady(bool b) {
	pwriteReady = b;
	hasWritten = false;
}
void BufferedFile::setReadReady(bool b) {
	preadReady = b;
	hasRead = false;
}
void BufferedFile::setWriteOnly() {
	reading = false;
	writing = true;
	setParentRW();
}
void BufferedFile::setParentRW() {
	if (parent!=NULL) {
		((BufferedFileFactory*)parent)->setBufferedFileWrite(this,writing);
		((BufferedFileFactory*)parent)->setBufferedFileRead(this,reading);
	}
}
void BufferedFile::setReadOnly() {
	reading = true;
	writing = false;
	setParentRW();
}
void BufferedFile::setReadWrite() {
	reading = true;
	writing = true;
	setParentRW();
}
bool BufferedFile::doesRead() {
	return reading;
}
bool BufferedFile::isClosed() {
	return (!reading && !writing);
}
bool BufferedFile::doesWrite() {
	return writing;
}
bool BufferedFile::hasBeenWritten() {
	return hasWritten;
}
bool BufferedFile::hasBeenRead() {
	return hasRead;
}
