#ifndef FORKJOIN_H
#define FORKJOIN_H

#include "cryptlib.h"
#include "filters.h"
#include "queue.h"

class Fork : public BufferedTransformation
{
public:
    Fork(int number_of_outports, BufferedTransformation *const *outports = NULL);

    void SelectOutPort(int portNumber);

    bool Attachable() {return true;}
    void Detach(BufferedTransformation *newOut = NULL);
    void Attach(BufferedTransformation *newOut);
    virtual void Close();

    unsigned long MaxRetrieveable()
        {return outPorts[currentPort]->MaxRetrieveable();}
//    virtual void InputFinished()
//        {outPorts[currentPort]->InputFinished();}

    unsigned int Get(byte &outByte)
        {return outPorts[currentPort]->Get(outByte);}
    unsigned int Get(byte *outString, unsigned int getMax)
        {return outPorts[currentPort]->Get(outString, getMax);}
    unsigned int Peek(byte &outByte) const
        {return outPorts[currentPort]->Peek(outByte);}

    virtual void Put(byte inByte);
    virtual void Put(const byte *inString, unsigned int length);

protected:
    unsigned int NumberOfPorts() const {return numberOfPorts;}
    BufferedTransformation& AccessPort(unsigned int i) {return *outPorts[i];}

private:
    Fork(const Fork &); // no copying allowed

    unsigned int numberOfPorts, currentPort;
    vector_member_ptrs<BufferedTransformation> outPorts;
};

class Join;

class Interface : public BufferedTransformation
{
public:
    Interface(Join &p, ByteQueue &b, int i)
        : parent(p), bq(b), id(i) {}

    unsigned long MaxRetrieveable();
    void Close();
    bool Attachable() {return true;}
    void Detach(BufferedTransformation *bt);
    void Attach(BufferedTransformation *bt);

    void Put(byte inByte);
    void Put(const byte *inString, unsigned int length);
    unsigned int Get(byte &outByte);
    unsigned int Get(byte *outString, unsigned int getMax);
    unsigned int Peek(byte &outByte) const;

private:
    Join &parent;
    ByteQueue &bq;
    const int id;
};

class Join : public Filter
{
public:
    Join(unsigned int number_of_inports, BufferedTransformation *outQ = NULL);

    // Note that ReleaseInterface is similar but not completely compatible 
    // with SelectInterface of version 2.0.  ReleaseInterface can be called
    // only once for each interface, and if an interface is released,
    // the caller will be responsible for deleting it.
    Interface *ReleaseInterface(unsigned int i);

    virtual void NotifyInput(unsigned int interfaceId, unsigned int length);
    virtual void NotifyClose(unsigned int interfaceId);

    void Put(byte inByte) {outQueue->Put(inByte);}
    void Put(const byte *inString, unsigned int length)
        {outQueue->Put(inString, length);}

protected:
    unsigned int NumberOfPorts() const {return numberOfPorts;}
    ByteQueue& AccessPort(unsigned int i) {return *inPorts[i];}
    unsigned int InterfacesOpen() const {return interfacesOpen;}

private:
    Join(const Join &); // no copying allowed

    unsigned int numberOfPorts;
    vector_member_ptrs<ByteQueue> inPorts;
    unsigned int interfacesOpen;
    vector_member_ptrs<Interface> interfaces;
};

#endif
