/*---------------------------------------------------------------------------------------------------------------------
- File      : dataflow.h                                                                  Project ELSE, EPFL - DI/LIA -
-                                                                       Evaluation in Language and Speech Engineering -
- Author    : Seydoux Florian   Creation date : 29 June 1999                                                          -
- Eulogist  : -                 Approval date : -                  Version: 0.1                                       -
-                                                                                                                     -
- Descript. : Define an unsynchronized DataFlow, and it's specialization for char flow and strings flow               -
-                                                                                                                     -
- Requested : -                                                                                                       -
-                                                                                                                     -
- Gaps      : o) La modélistion du flot est très (trop) simpliste... De plus, il n'y a pas de synchronisation entre   -
-                les différents acteurs (il faudrait enregistrer tous les 'FlowListener' dans une collection statique -
-                et fournire une 'horloge' qui cadence le flot (donc ajouter une 'sortie' à tous les producteurs, ou  -
-                une 'entrée', à prendre en compte de manière pseudo-simultanée). Avantage: limiter la profondeur des -
-                appels (actuellement, le caract. passe par tout les acteurs: appels imbriques de tous les acteurs)   -
-                en passant le controle de maniere distribuee. Probleme: les 'vitesse' des flots ne sont pas toutes   -
-                les memes: un agent peut stopper le flux, pour le reprendre plus tard, avec n chr en sorties...      -
-                -> la meth. doit retourner le nb de chr en sorties... Attention, le flux n'est pas necessairement    -
-                'lineaire': plusieurs agent doivent pouvoir se brancher sur la sortie d'un autre.                    -
-                                                                                                                     -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Rev. date | Reviser               | Revise's description                                                            -
- - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ../../....| ........              | ...                                                                             -
---------------------------------------------------------------------------------------------------------------------*/

#ifndef DATAFLOW_H
#define DATAFLOW_H    

#include <cassert>
#include <iterator>
#include "globaldef.h"
#include "constrained_circular_buffer.h"

#ifdef _USE_NAMESPACES
    namespace Else { 
#endif // _USE_NAMESPACES


class IndexedChar {
public:
    
    IndexedChar(const char chr)
    : c(chr),
      i(0)
    { }

    IndexedChar(const char chr, const VeryLongNatural index)
    : c(chr),
      i(index)
    { }
    
   operator const char&() const
   {
    return c;
   }

public:
    char c;
    VeryLongNatural i;
};

typedef IndexedChar ichar;
typedef vector<ichar> UnconstrainedLinearCharBuffer;
typedef deque<ichar>  UnconstrainedCircularCharBuffer;
typedef ConstrainedCircularBuffer<ichar, ShortNatural> ConstrainedCircularCharBuffer;

template <class FlowType>
class FlowListener {
public:
    virtual void computeFlowItem(const FlowType& data) = 0;
     void operator<<=(const FlowType& data) {computeFlowItem(data); }
};

template <class FlowType>
class NullFlowListener : public FlowListener<FlowType> {
public:
    void computeFlowItem(const FlowType& data) { }
};

template <class FlowType>
class FlowToStream : public FlowListener<FlowType> {
public:
    FlowToStream(ostream& os): osPtr(&os)      { }
    void setOutputStream(ostream& os)          { osPtr = &os; }
    void computeFlowItem(const FlowType& data)  { (*osPtr) << data; }
private:
    ostream* osPtr;
};


/* L'agent ou écouteur censé réagir à l'acquisistion d'une donnée en retournant une
   donnée ne s'intègre pas bien de la sorte...
   Le technique de la fct d'interrogation (2 temps avec variable interne) me parait
   meilleure...
*/
template <class FlowType, class ReturnType>
class ComplexFlowListener {
public:
    virtual ReturnType computeFlowItem(const FlowType data) = 0;
     ReturnType operator<<=(const FlowType& data) {return computeFlowItem(data); }
};


template <class FlowType>
class FlowAgent: public FlowListener<FlowType> {
public:
    // Doit etre invoquée en fin de flux, pour 'vider' les tampons.
    // Attention: la méthode doit (re)mettre l'instance dans l'état initial
    // -> il doit etre possible de 'redémarrer' le flux.
    virtual void flush() = 0;
};


template <class FlowType>
class NullFlowAgent : public FlowAgent<FlowType> {
public:
    void flush() { }
    void computeFlowItem(const FlowType& data) { }
};


template <class FlowType>
class Pipe : public virtual FlowAgent<FlowType> {
    //
    // Tampon remplit avec les elements du flot, 'débordant' sur le prochain agent.
    //
public:            
    Pipe(const ShortNatural delay, FlowListener<FlowType>& nextAgent)
    : delay(delay), pipe(delay), nextAgent(&nextAgent)
    { }

    void setNextAgent(FlowListener<FlowType>& nextAgent) { this->nextAgent = &nextAgent; }
    void computeFlowItem(const FlowType& data)
    {
        if (delay == 0) (*nextAgent) <<= data;
        else {
            if (pipe.full()) (*nextAgent) <<= pipe.pop();
            pipe.push(data);
        }
    }
    
    void flush()
    {
        while (! pipe.empty() )
            (*nextAgent) <<= pipe.pop();
    }
    
#ifdef _MEMBER_TEMPLATES
    template <class InputIterator>
    void load(InputIterator start, const InputIterator stop)
#else
   void load(const_iterator start, const_iterator stop)  
#endif
    {
        while (start != stop) {
            computeFlowItem(*start);
            ++start;
        }
    }

    void clear()              { pipe.clear(); }
    bool full() const         { return pipe.full(); }
    bool empty() const        { return pipe.empty(); }
    ShortNatural size() const { return pipe.size(); }

private:
    ConstrainedCircularCharBuffer   pipe;
    ShortNatural                    delay;
    FlowListener<FlowType>*         nextAgent;
};


//
// Specilization(s) for a flow of characters (char) ................................................
//

typedef FlowListener<ichar>              CharFlowListener;
typedef CharFlowListener*                CharFlowListenerPtr;

typedef ComplexFlowListener<ichar, bool> PredicateCharFlowListener;
typedef PredicateCharFlowListener*       PredicateCharFlowListenerPtr;

typedef FlowAgent<ichar>                 CharFlowAgent;
typedef CharFlowAgent*                   CharFlowAgentPtr;

typedef Pipe<ichar>                      CharPipe;
typedef CharPipe*                        CharPipePtr;

typedef FlowToStream<ichar>              CharFlowToStream;
typedef CharFlowToStream*                CharFlowToStreamPtr;

extern NullFlowAgent<ichar>              nullCharFlowAgent;
extern NullFlowListener<ichar>           nullCharFlowListener;


//
// Specialization(s) for a flow of strings (string) .................................................
// 

typedef FlowListener<string>    StringFlowListener;
typedef StringFlowListener*     StringFlowListenerPtr;

typedef FlowAgent<string>       StringFlowAgent;
typedef StringFlowAgent*        StringFlowAgentPtr;

typedef FlowToStream<string>    StringFlowToStream;
typedef StringFlowToStream*     StringFlowToStreamPtr;

extern NullFlowAgent<string>    nullStringFlowAgent;
extern NullFlowListener<string> nullStringFlowListener;

#ifdef _USE_NAMESPACES
    }
#endif // _USE_NAMESPACES

#endif

