#ifndef FILTERCONNECTION_H
#define FILTERCONNECTION_H
#include <stdio.h>
#include <string>
#include <vector>
#include "Clock.h"
#include "Connection.h"
/**
 * The buffersize to read per process
 */
#define BUFFSIZE 512

/**
 *  FilterConnection Interface (5.1.1.2)
 * Inherits from Connection.
 * connectFrom(conn:Connection) Accepts a connection FROM conn connection. 
 * The current filterConnection accepts the connection as an input to the connection.
 * process() A step taken which will read in data and output data, fills and dumps 
 * one buffer. Must be called to move audio.
 * disconnectFrom(conn:Connection) Disconnects an incoming patch between the current 
 * connection and "conn" connection.
 * disconnectTo(conn:Connection)Disconnects an outgoing patch between current
 * connection and "conn" connection. 
 */
template<class SAMPLE> class FilterConnection : private Connection<SAMPLE> {
	public:	
		/**
		 * connectFrom Accepts a connection FROM conn connection. 
		 * Essentially a compatibility wrapper
		 * */
		void connectFrom(Connection<SAMPLE> &conn) {
			connectFrom(&conn);
		}
		/**
		 * connectFrom Accepts a connection FROM conn connection. 
		 * */
		void connectFrom(Connection<SAMPLE> *conn) {
			if (conn->readable()) {
				inConnections.push_back(conn);
			} else {
				throw new string("Not A Input Connection");
			}
		}
		/**
		 * Process, reads and write data from other connections.
		 * */
		virtual void process() { // A step taken which will read in data and output data, 
			//if (!writeReady() && !file->ready(BUFFER_WRITE)) {
			if (!writeReady()) {
				return;
			}
			int i =0;		// fills and dumps one buffer. Must be called to move audio.
			int c = 0;
			int size = inConnections.size();
			SAMPLE * b2 = buffer2;
			SAMPLE * b1 = buffer;
			//bzero(b1,sizeof(SAMPLE)*BUFFSIZE);
			vector<Connection<SAMPLE>*>::iterator iter;
			for (i = 0; i < BUFFSIZE; i++ ) {
				b1[i] = 0;
			}
			for (iter = inConnections.begin(); iter < inConnections.end(); iter++) {
				if ((*iter)->readReady()) {
					//int r = (*iter)->read(b2,BUFFSIZE);
					int r = (*iter)->read(b2,BUFFSIZE);
					if (r == -1) {
						disconnectFrom(*iter);
					} else {
						c++;
						for (i = 0; i < BUFFSIZE; i++ ) {
						//for (i = 0; i < r; i++ ) {
							b1[i]+=(b2[i])/size;
							//b1[i]=(b2[i]);
						}
					}
				}
			}
			if (c > 0) {
				write(b1,BUFFSIZE);
			}
		}
				
		/**
		 * disconnectFrom Disconnects an incoming patch between the current 
		 * connection and "conn" connection.
		 * */
    		void disconnectFrom(Connection<SAMPLE> *conn) {
			vector<Connection<SAMPLE>*>::iterator iter;
			for (iter = inConnections.begin(); iter < inConnections.end(); iter++) {
				if (*iter == conn) {
					inConnections.erase(iter);
					break;
				}
			}
		}
		/**
		 * disconnectTo Disconnects an outgoing patch between current and conn
		 * */
		void disconnectTo(FilterConnection<SAMPLE> *conn){//
			conn->disconnectFrom(this);
		}
		/**
		 * used for XML output gets the type of the FilterConnection
		 * */
		virtual string xmlType()  { //FILTER/INPUT/OUTPUT
			return "FILTER";
		}
		/**
		 * gets an XML representation of the current instance.
		 * */
		string * getXML() {
			char number[12];
			char number2[12];
			snprintf(number,12,"%d",this->getFileHandle());
			string * out = new string("<connection><type>");
			(*out)+=xmlType();
			(*out)+="</type><name>";
			(*out)+=getName();
			(*out)+="</name><id>";
			(*out)+=string(number);
			(*out)+="</id></connection>\n";
			vector<Connection<SAMPLE>*>::iterator iter;
			for (iter = inConnections.begin(); iter < inConnections.end(); iter++) {
				snprintf(number2,12,"%d",(*iter)->getFileHandle());
				(*out)+="<patch><from>";
				(*out)+=string(number2);
				(*out)+="</from><to>";
				(*out)+=string(number);
				(*out)+="</to></patch>";
			}
			return out;
		}
		/**
		 * the lastClock value remembered.
		 * */
		int lastClock;
		virtual int read(SAMPLE * data, int size) {
			if (lastClock!=Clock::clock) {
				Connection<SAMPLE>::read(buffer3,size);
				lastClock=Clock::clock;
			}
			memcpy((void*)data,buffer3,sizeof(SAMPLE)*size);
			return size;
		}
		/**
		 * Constructor using a bufferedfile
		 * */
		FilterConnection(BufferedFile * fd): inConnections() {
			init(fd,"Unnamed");
			initBuffer();
		}
		/**
		 * Constructor using a bufferedfile and name
		 * */
		FilterConnection(BufferedFile * fd,string name): inConnections() {
			init(fd,name);
			initBuffer();
		}
		/**
		 * Destructor
		 * */
		virtual ~FilterConnection() {
			if (!inConnections.empty()) {
				disconnect();
			}
			delete [] buffer;
		}
	protected: 
		vector<Connection<SAMPLE>*>  inConnections;
		SAMPLE  * buffer;
		SAMPLE  * buffer2;
		SAMPLE  * buffer3;
		void initBuffer() {
			//Malloc bad but we have to
			buffer =  new SAMPLE[3*BUFFSIZE];
			buffer2 = buffer+BUFFSIZE; 
			buffer3=  buffer2+BUFFSIZE;
			memset(buffer,0,BUFFSIZE);
			memset(buffer2,0,BUFFSIZE);
			memset(buffer3,0,BUFFSIZE);
			lastClock = 0;
		}
		virtual void   disconnect() {
			inConnections.clear();
			this->Connection<SAMPLE>::disconnect();
		}
	private:
};
#endif
