#include <stdio.h>
#include <sched.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <list>
#include "Connector.h"
#include "ConnectionWrap.h"
#include "BufferedFile.h"
#include "BufferedFileFactory.h"
#include "ConnectionFactory.h"
#include "Connection.h"
#include "FilterConnection.h"
#include "OutputConnection.h"
#include "Clock.h"
#include "connect.h"
/**
 * The Buffersize
 */
#define BUFFSIZE 512
/**
 * Connect to a host using the port and the hostentry
 */
int connect(hostent * he, int port);
/**
 * is the current string a number?
 */
bool isNum(char *arg);
/**
 * Get the IP address of an int as a string
 */
string getIP(int ip);
/**
 * accepts hostname pairs
 */
int main(int argc, char *argv[]){
	try {
		string * name = NULL;
		list<hostent> hosts;
		list<int> ports;
		try {
			if (argc >= 3) {
				int index = 2;
				int offset = 0;
				if ((argc+1)%2 == 1) { //name included  app10 name21 host32 port43
					cout << "NAME\n";
					name = new string(argv[1]);
					index = 2;
					offset = 0;
				} else if ((argc+1)%2 == 0) { //noname app10 host21 port3
					cout << "NONAME\n";
					name = NULL;
					index = 1;
					offset = 1;
				}
				for (; index < argc; index++) {
					if ((index+offset)%2==1) {
					 	if (!isNum(argv[index])) {
							string error = string("Arguement Invalid: ");
							error += string(argv[index]);
							throw error.c_str();
						}
						int port = atoi(argv[index]);
						ports.push_back(port);
					} else {
						struct hostent *he = gethostbyname(argv[index]);
						if (he == NULL) {
							string errstr = "Could not resolve host ";
							errstr += string(argv[index]);
							throw errstr.c_str();
						}
						hosts.push_back(*he);
					}
				}
			} else {
				throw (char*)NULL;
			}
		} catch (char * err) {
			if (err != NULL) {
				cerr << "ERROR: " << err << "\n";
			}
			cout << argv[0] << " [name] [host] [port] [host] [port] ...\n";
			return 0;
		}
		list<ConnectionWrap *> outgoing;
		list<hostent>::iterator hostIter = hosts.begin();
		list<int>::iterator portIter = ports.begin();
		BufferedFileFactory bf;
		BufferedFile * inputBf = bf.getNewBufferedFile(STDIN_FILENO);
		inputBf->setReadOnly();
		//InputConnection<short> * ic = new InputConnection<short>(inputBf);
		//ConnectionWrap * icw = new ConnectionWrap(ic,SHORTCONN);
		char * buffer = new char[BUFFSIZE * 2];//(icw->getDataSize())];
		int framesize = 2;
		while (hostIter != hosts.end() && portIter != ports.end()) {
			int port = *portIter;
			hostent host = *hostIter;
			int fd =  connect(&host,port);
			cout << host.h_name << " " << port << "\n";
			if (fd!=-1) {
				cout << "FD: " << fd <<"\n";
				BufferedFile * outputBf = bf.getNewBufferedFile(fd);
				outputBf->setWriteOnly();
				OutputConnection<short> * oc = new OutputConnection<short>(outputBf);
				outgoing.push_back(new ConnectionWrap(oc,SHORTCONN));
				//oc->connectFrom((Connection<short>*)ic);
			}
			hostIter++;
			portIter++;
		}
		if (name!=NULL) {
			string headStart("<header><name>");
			string nameEnd("</name>");
			string headEnd("</header>");
			int length = headStart.length() + headEnd.length() + nameEnd.length() + name->length();
			if (length%framesize!=0) {
				int count = framesize - length%framesize - 1;
				for (int i = 0 ; i < count; i++) {
					nameEnd += " ";
				}
				length = headStart.length() + headEnd.length() + nameEnd.length() + name->length();
			}
			const char * header = (headStart+(*name)+nameEnd+headEnd).c_str();
			
			bf.process(100);
			list<ConnectionWrap *>::iterator iter;
			for (iter = outgoing.begin(); iter != outgoing.end(); iter++) {
				ConnectionWrap * cw = (*iter);
				//if (cw->writeReady()) {
					cw->write((char*)header,length);
				//}
			}
			
		}
		for (;;) {
			int rw = bf.process(100);
			Clock::clock++;
			if (rw >= 2) {
					//int r = icw->read((char*)buffer,BUFFSIZE);
					//int r = ic->read((short*)buffer,BUFFSIZE);
					read(STDIN_FILENO,buffer,BUFFSIZE*2);
					list<ConnectionWrap *>::iterator iter;
					for (iter = outgoing.begin(); iter != outgoing.end(); iter++) {
						ConnectionWrap * cw = (*iter);
						if (cw->writeReady()) {
							cw->write((char*)buffer,BUFFSIZE);
						}
					}
			}
		}
	} catch (char * str) {
		cerr<< "G EXCEPTION: " <<str<<"\n";
	} catch (string * str) {
		cerr<< "G EXCEPTION: " <<*str<<"\n";
	}
	return 0;
}
bool isNum(char *arg) {
	for (int i = 0; arg[i]!='\0' ; i++) {
		if (!isdigit(arg[i])) {
			return false;
		}
	}
	return true;
}
string getIP(int ip) {
	int i1 = ip >> 24;
	int i2 = (ip << 8 )>> 24;
	int i3 = (ip << 16)>> 24;
	int i4 = (ip << 24)>> 24;
	char data[12];
	string out = "";
	out+=string((snprintf(data,12,"%d",i1),data));//Bad coding style uses , operator
	out+=string((snprintf(data,12,"%d",i2),data));
	out+=string((snprintf(data,12,"%d",i3),data));
	out+=string((snprintf(data,12,"%d",i4),data));
	return out;
}
