#include "BufferedFileFactory.h"
#include "Clock.h"
#include <iostream>
BufferedFileFactory::BufferedFileFactory(): descriptorToFile(),tv() {
	//descriptorToFile = new BuffVec();
	//
	//descriptorToFile = new BuffVec();
	FD_ZERO(&readSet);
	FD_ZERO(&writeSet);
	maxFd = -1;
}
BufferedFile * BufferedFileFactory::getNewBufferedFile(int fdesc) {
	BufferedFile * bf = new BufferedFile(fdesc);
	bf->setBufferedFileFactory(this);
	assert(bf!=NULL);
	descriptorToFile.push_back(bf);
	FD_SET(fdesc,&writeSet);
	FD_SET(fdesc,&readSet);
	if (fdesc+1 > maxFd) {
		maxFd = fdesc+1;
	}
	return bf;
}
//If readReady is true and !hasBeenRead DONT
int BufferedFileFactory::process(int microTime) {
	BuffVec::iterator iter;
	fd_set readingSet;
	fd_set readingSet2;
	fd_set writingSet;
	fd_set writingSet2;
	fd_set exceptions;
	fd_set waitset;
	FD_ZERO(&readingSet);
	FD_ZERO(&writingSet);
	FD_ZERO(&readingSet2);
	FD_ZERO(&writingSet2);
	FD_ZERO(&exceptions);
	FD_ZERO(&waitset);
	FD_SET(STDIN_FILENO,&waitset);
	for (iter = descriptorToFile.begin(); iter !=  descriptorToFile.end(); iter++) {
		BufferedFile * file = *iter;
		int fd = file->getFileHandle();
		FD_SET(fd,&exceptions);
		if (file->doesRead()) {
			//if (!file->readReady() || (file->readReady() && file->hasBeenRead())) {
			if (file->readReady() && file->hasBeenRead()) {
			      FD_SET(fd,&readingSet2);
			} else {
				//cerr << "Has Been Read or is not readready: " << fd << " " << Clock::clock << "\n";
			      FD_SET(fd,&readingSet);
			}
		}
		if (file->doesWrite()) {
			//if (!file->writeReady() || (file->writeReady() && file->hasBeenWritten())) {
			if (file->writeReady() && file->hasBeenWritten()) {
			      FD_SET(fd,&writingSet2);
			} else {
				//cerr << "Has Been Written or is not writeready: " << fd << " " << Clock::clock << "\n";
			
			      FD_SET(fd,&writingSet);
			}
		}
	}
	//fd_set readingSet = readSet;
	//fd_set writingSet = writeSet;
	//tv.tv_usec = microTime/2; 
	//microTime/=2;
	tv.tv_sec = 0;
	tv.tv_usec = microTime;
	//tv.tv_usec = 0;
	//gettimeofday(&tv,NULL);
	//long milli =  tv.tv_usec+microTime;
	int read = select(maxFd+1,&readingSet,&writingSet, NULL, &tv);
	tv.tv_sec = 0;
	tv.tv_usec = microTime;
	read += select(maxFd+1,&readingSet2,&writingSet2, &exceptions, &tv);
	tv.tv_sec = 0;
	tv.tv_usec = microTime;
	select(maxFd+1,NULL,&waitset,NULL,&tv);
	
	//tv.tv_sec = 0;
	//tv.tv_usec = microTime/3; 
	//int excepts = 0;
	//int excepts = select(maxFd+1,NULL,NULL, &exceptions, &tv);
	//int read = select(maxFd+1,&readingSet,NULL, NULL, &tv);
	//tv.tv_sec = 0;
	//tv.tv_usec = microTime/2; 
	//int write = select(maxFd+1,NULL,&writingSet, NULL, &tv);
	int write = 0;
	for (iter = descriptorToFile.begin(); iter !=  descriptorToFile.end(); iter++) {
		BufferedFile * file = *iter;
		int fd = file->getFileHandle();
		if (file->doesRead()) {
			if (FD_ISSET(fd, &readingSet) || FD_ISSET(fd,&readingSet2)) {
				//cerr << "ReadFD: "<<fd << " " << (int)(file) << "\n";
				file->setReadReady(true);
			} else {
				file->setReadReady(false);
			}
		}
		if (file->doesWrite()) {
			if (FD_ISSET(fd, &writingSet) || (FD_ISSET(fd, &writingSet2))) {
				file->setWriteReady(true);
			} else {
				file->setWriteReady(false);
			}
		}	
		if (FD_ISSET(fd,&exceptions)) {
			file->setBufferedFileFactory(this);
			cerr << "Closing Connection " << fd << "\n";
			file->Close();
		}
	}
	return (read+write);
}
void BufferedFileFactory::closeFile(BufferedFile * f) {
	int fdesc = f->getFileHandle();
	descriptorToFile.remove(f);
	FD_CLR(fdesc,&readSet);
	FD_CLR(fdesc,&writeSet);
	maxFd = 0;
	BuffVec::iterator iter;
	//for (iter = descriptorToFile.begin(); iter !=  descriptorToFile.end(); iter++) {
	//	BufferedFile * r = (*iter);
	//	if (f==r) {
	//		descriptorToFile.erase(iter);
	//	}
	//}
	for (iter = descriptorToFile.begin(); iter !=  descriptorToFile.end(); iter++) {
		int fd = (*iter)->getFileHandle();
		//if (fd == fdesc) {
		//	descriptorToFile.erase(iter);
		//} else if (fd+1 > maxFd) { 
		if (fd+1 > maxFd) { 
			maxFd = fd+1;
		}
	}
}
BufferedFileFactory::~BufferedFileFactory() {
	BuffVec::iterator iter;
	for (iter = descriptorToFile.begin(); iter !=  descriptorToFile.end(); iter++) {
		(*iter)->setBufferedFileFactory(NULL);
		(*iter)->Close();
		delete *iter;
	}
	descriptorToFile.clear();
	//delete descriptorToFile;
}
void BufferedFileFactory::setBufferedFileRead(BufferedFile * bf,bool mode) {
	int fd = bf->getFileHandle();
	if (mode) { //READ
		if (FD_ISSET(fd,&readSet)) {

		} else {
			FD_SET(fd,&readSet);
		}
	} else { //NOT READ
		if (FD_ISSET(fd,&readSet)) {
			FD_CLR(fd,&readSet);
		} else {
		}
	}
}
void BufferedFileFactory::setBufferedFileWrite(BufferedFile * bf,bool mode) {
	int fd = bf->getFileHandle();
	if (mode) { //WRITE
		if (FD_ISSET(fd,&writeSet)) {

		} else {
			FD_SET(fd,&writeSet);
		}
	} else { //NOT WRITE
		if (FD_ISSET(fd,&writeSet)) {
			FD_CLR(fd,&writeSet);
		} else {
		}
	}
}
