#ifndef CONNECTIONWRAP_H
#define CONNECTIONWRAP_H
#include <string>
#include "Connection.h"
#include "FilterConnection.h"
#include "ConfigConnection.h"
/**
 * ConnectionU is a union for easy casting
 */
union ConnectionU {	//Makes for quick and dirty casting with no up or down casting
	Connection<char> * charConn;
	Connection<double> * doubleConn;
	Connection<float> * floatConn;
	Connection<short> * shortConn;
	FilterConnection<char>  * charFConn;
	FilterConnection<double>* doubleFConn;
	FilterConnection<float> * floatFConn;
	FilterConnection<short> * shortFConn;
	ConfigConnection        * configConn;
	void * voidStar;
} typedef ConnectionU;
/**
 * Explicitly defined enumeration
 */
#define SHORTCONN 0
#define CHARCONN  1
#define BYTECONN  1
#define FLOATCONN 2
#define DOUBLECONN 3
#define CONFIGCONN 4
/**
 * ConnectionWrap is a 5 byte wrapper for a connection. It is used to wrap
 * the type of the connection as well as functions.
 * This is so we can have a vector of one type containing many.
 */
class ConnectionWrap { //5 bytes Not too bad
	public:
	/**
	 * The connection union containing the connection being wrapped
	 * */
	ConnectionU conn;
	/**
	 * the type
	 * */
	char type;
	/**
	 * Default Constructor
	 * */
	ConnectionWrap(): conn() { type = (char)0; conn.voidStar = NULL; }
	/**
	 * The constructor, connection is a Connection and type is the type like (SHORTCONN,BYTECONN,..)
	 * */
	ConnectionWrap(void * connection,int type): conn() {
		this->conn.voidStar = connection;
		this->type = (char)type;
	}
	/**
	 * Returns the current connection's FileHandle
	 * */
	int getFileHandle() {
		if (type==SHORTCONN) {
			return conn.shortConn->getFileHandle();
		} else if (type==CHARCONN) {
			return conn.charConn->getFileHandle();
		} else if (type==FLOATCONN) {
			return conn.floatConn->getFileHandle();
		} else if (type==DOUBLECONN) {
			return conn.doubleConn->getFileHandle();
		}
		return 0;
	}
	/**
	 * is it readable?
	 * */
	bool readable() {
		if (type==SHORTCONN) {
			return conn.shortConn->readable();
		} else if (type==CHARCONN) {
			return conn.charConn->readable();
		} else if (type==FLOATCONN) {
			return conn.floatConn->readable();
		} else if (type==DOUBLECONN) {
			return conn.doubleConn->readable();
		}
		return 0;
	}
	/**
	 * gets the Connection XML connection
	 * */
	string * getXML() {
		if (type==SHORTCONN) {
			return conn.shortFConn->getXML();
		} else if (type==CHARCONN) {
			return conn.charFConn->getXML();
		} else if (type==FLOATCONN) {
			return conn.floatFConn->getXML();
		} else if (type==DOUBLECONN) {
			return conn.doubleFConn->getXML();
		}
		return 0;
	}
	/**
	 * Disconnect Connection
	 * */
	void disconnect() {
		if (type==SHORTCONN) {
			return conn.shortConn->disconnect();
		} else if (type==CHARCONN) {
			return conn.charConn->disconnect();
		} else if (type==FLOATCONN) {
			return conn.floatConn->disconnect();
		} else if (type==DOUBLECONN) {
			return conn.doubleConn->disconnect();
		} else if (type==CONFIGCONN) {
			return conn.configConn->disconnect();
		}
	}
	/**
	 * Process the connection (read or write)
	 * */
	void process() {
		if (type==SHORTCONN) {
			conn.shortFConn->process();
		} else if (type==CHARCONN) {
			conn.charFConn->process();
		} else if (type==FLOATCONN) {
			conn.floatFConn->process();
		} else if (type==DOUBLECONN) {
			conn.doubleFConn->process();
		}
	}
	/**
	 * Destructor
	 * */
	~ConnectionWrap() {
		//delete conn.voidStar;
		if (conn.voidStar!=NULL) {
			if (type==SHORTCONN) {
				delete conn.shortFConn;
			} else if (type==CHARCONN) {
				delete conn.charFConn;
			} else if (type==FLOATCONN) {
				delete conn.floatFConn;
			} else if (type==DOUBLECONN) {
				delete conn.doubleFConn;
			} else if (type==CONFIGCONN) {
				delete conn.configConn;
			}
		}
		conn.voidStar = NULL;
	}
	/**
	 * Write to connection
	 * size is the number of samples not the number of bytes
	 * */
	void write(char * buffer,int size) {
		if (type==SHORTCONN ) {
			conn.shortConn->write((short*)buffer,size);
		} else if (type==CHARCONN || type==CONFIGCONN) {
			conn.charConn->write((char*)buffer,size);
		} else if (type==FLOATCONN ) {
			conn.floatConn->write((float*)buffer,size);
		} else if (type==DOUBLECONN ) {
			conn.doubleConn->write((double*)buffer,size);
		} else if (type==CONFIGCONN ) {
			conn.configConn->write((char*)buffer,size);
		}
	}
	/**
	 * read from connection
	 * size is the number of samples not the number of bytes
	 * */
	int read(char * buffer,int size) {
		if (type==SHORTCONN ) {
			return conn.shortConn->write((short*)buffer,size);
		} else if (type==CHARCONN || type==CONFIGCONN) {
			return conn.charConn->write((char*)buffer,size);
		} else if (type==FLOATCONN ) {
			return conn.floatConn->write((float*)buffer,size);
		} else if (type==DOUBLECONN ) {
			return conn.doubleConn->write((double*)buffer,size);
		} else if (type==CONFIGCONN ) {
			return conn.configConn->write((char*)buffer,size);
		}
		return 0;
	}
	/**
	 * W/o blocking is the connection readable.
	 * */
	bool readReady() {
		if (type==SHORTCONN ) {
			return conn.shortConn->readReady();
		} else if (type==CHARCONN || type==CONFIGCONN) {
			return conn.charConn->readReady();
		} else if (type==FLOATCONN ) {
			return conn.floatConn->readReady();
		} else if (type==DOUBLECONN ) {
			return conn.doubleConn->readReady();
		}
		return false;
	}
	/**
	 * W/o blocking is the connection writable.
	 * */
	bool writeReady() {
		if (type==SHORTCONN ) {
			return conn.shortConn->writeReady();
		} else if (type==CHARCONN || type==CONFIGCONN) {
			return conn.charConn->writeReady();
		} else if (type==FLOATCONN ) {
			return conn.floatConn->writeReady();
		} else if (type==DOUBLECONN ) {
			return conn.doubleConn->writeReady();
		}
		return false;
	}
	/**
	 * Get the data size of the current connection
	 * */
	int getDataSize() {
		if (type==SHORTCONN ) {
			return conn.shortConn->getDataSize();
		} else if (type==CHARCONN || type==CONFIGCONN) {
			return conn.charConn->getDataSize();
		} else if (type==FLOATCONN ) {
			return conn.floatConn->getDataSize();
		} else if (type==DOUBLECONN ) {
			return conn.doubleConn->getDataSize();
		}
		return 0;
	}
	/**
	 * Disconnect the connection FROM another connection (incoming patch) (Wrap)
	 * */
	void disconnectFrom(ConnectionWrap * w) {
		if (type==SHORTCONN && w->type ==SHORTCONN) {
			((FilterConnection<short>*)(conn.shortConn))->disconnectFrom((Connection<short>*)w->conn.shortConn);
		} else if (type==CHARCONN && w->type==CHARCONN) {
			((FilterConnection<char>*)(conn.charConn))->disconnectFrom((Connection<char>*)w->conn.charConn);
		} else if (type==FLOATCONN && w->type==FLOATCONN) {
			((FilterConnection<float>*)(conn.floatConn))->disconnectFrom((Connection<float>*)w->conn.floatConn);
		} else if (type==DOUBLECONN && w->type==DOUBLECONN) {
			((FilterConnection<double>*)(conn.doubleConn))->disconnectFrom((Connection<double>*)w->conn.doubleConn);
		} else {
			throw new string("Connections are of different data types!");
		}
	}
	/**
	 * Disconnect the connection TO another connection (outgoing patch) (Wrap)
	 */
	void disconnectTo(ConnectionWrap * w) {
		if (type==SHORTCONN && w->type ==SHORTCONN) {
			((FilterConnection<short>*)(conn.shortConn))->disconnectTo((FilterConnection<short>*)w->conn.shortFConn);
		} else if (type==CHARCONN && w->type==CHARCONN) {
			((FilterConnection<char>*)(conn.charConn))->disconnectTo((FilterConnection<char>*)w->conn.charFConn);
		} else if (type==FLOATCONN && w->type==FLOATCONN) {
			((FilterConnection<float>*)(conn.floatConn))->disconnectTo((FilterConnection<float>*)w->conn.floatFConn);
		} else if (type==DOUBLECONN && w->type==DOUBLECONN) {
			((FilterConnection<double>*)(conn.doubleConn))->disconnectTo((FilterConnection<double>*)w->conn.doubleFConn);
		} else {
			throw new string("Connections are of different data types!");
		}
	}
	/**
    	 * connectFrom Accepts a connection FROM conn connection (incoming connection). 
	 * */
	void connectFrom(ConnectionWrap * w) {
		if (type==SHORTCONN && w->type ==SHORTCONN) {
			((FilterConnection<short>*)(conn.shortConn))->connectFrom(*(Connection<short>*)w->conn.shortConn);
		} else if (type==CHARCONN && w->type==CHARCONN) {
			return ((FilterConnection<char>*)(conn.charConn))->connectFrom(*(Connection<char>*)w->conn.charConn);
		} else if (type==FLOATCONN && w->type==FLOATCONN) {
			return ((FilterConnection<float>*)(conn.floatConn))->connectFrom(*(Connection<float>*)w->conn.floatConn);
		} else if (type==DOUBLECONN && w->type==DOUBLECONN) {
			return ((FilterConnection<double>*)(conn.doubleConn))->connectFrom(*(Connection<double>*)w->conn.doubleConn);
		} else {
			throw new string("Connections are of different data types!");
		}
	}
	/**
	 * Is the unlying file closed?
	 * */
	bool isClosed() {
		if (type==SHORTCONN ) {
			return conn.shortConn->isClosed();
		} else if (type==CHARCONN || type==CONFIGCONN) {
			return conn.charConn->isClosed();
		} else if (type==FLOATCONN ) {
			return conn.floatConn->isClosed();
		} else if (type==DOUBLECONN ) {
			return conn.doubleConn->isClosed();
		}
		return true;
	}
};
#endif
