/*---------------------------------------------------------------------------------------------------------------------
- File      : untagger.h                                                                  Project ELSE, EPFL - DI/LIA -
-                                                                       Evaluation in Language and Speech Engineering -
- Author    : Seydoux Florian   Creation date : 28 June 1999                                                          -
- Eulogist  : -                 Approval date : -                  Version: 0.1                                       -
-                                                                                                                     -
- Descript. : Define and implement the untagger.                                                                      -
-             But: Extraire du corpus les donnees ne devant pas etre etiquetee                                        -
-                  Memoriser le formatage en transcodant les delimiteurs                                              -
-             In : fichier contenant le corpus 'originel'                                                             -
-                  Event.: sequence a extraire, sequence a mapper                                                     -
-             Out: corpus a etiqueter                                                                                 -
-                  texte extrait et ancrage                                                                           -
-                                                                                                                     -
-             State: X            : quelconque                    Event: (add/remove policy) -> X                     -
-                    Transparent  : copie in_char en sortie       Event: (in_char = start_balise) -> Keep,0           -
-                                                                        (in_char = map_char) -> Translate            -
-                                                                        (in_char = EOF) -> Final                     -
-                                                                        (in_char = autre) -> Transparent             -
-                    Keep,n       : memorise dans un tampons      Event: (in_char = stop_balise) -> Shrink            -
-                                                                        (in_char <> stop_balise) -> Keep,n+1         -
-                                                                        (n > max_dist) -> Dump                       -
-                    Shrink       : Vide le tampon dans ExtractFlow       Event: - -> Transparent                     -
-                    Dump         : Copie le tampon en sortie et le vide. Event: - -> Transparent                     -
-                    Translate    : copie map_char en sortie, update TranslatedTable.                                 -
-                                                                 Event: (!merge_flag) -> Transparent                 -
-                                                                        (in_char = map_char) -> Merge                -
-                                                                        (in_char <> map_char) -> Transparent         -
-                    Merge        : extrait les map_char consecutif: update TranslatedTable                           -
-                                                                 Event: (in_char = map_char) -> Merge                -  
-                                                                        (in_char <> map_char) -> Transparent         -
-                    Final        : Ferme les fichiers, dump TranslatedTable dans ExtractFlow.                        -
-                                                                                                                     -
-              Rem: in_char, map_char ... sont des sequences de caracteres.                                           -
-                   TranslatedTable memorise l'encrage des differentes occurences des sequences a translater          -
-                   L'ancrage se fait par comptage des caracteres, dans le corpus d'origine.                          -
-                                                                                                                     -
-              Chaque operateur retarde le flux de #<Tag> unité (fenetrage pour retrait du tag)                       -
-                                                                                                                     -
-              Pour etre 'reversible', chaque operateur doit effectuer son propre decompte:                           -
-              operateur forward: nb caracteres significatif en entrees avant application operateur                   -
-                                 (y compris la marque de début)                                                      -
-              operateur backward: nb caract. significatif en sortie apres application operateur,                     -
-                                  pas de delais                                                                      -
-                                                                                                                     -
-              Le fichier de 'recomposition' doit debuter par le descriptif des operateurs a instancier,              -
-              correctement ordonnes, et pour chaque ancre doit figurer l'identification de l'operateur               -
-              relatif.                                                                                               -
-                                                                                                                     -
-                                                                                                                     -
- Requested : -                                                                                                       -
-                                                                                                                     -
- Gaps      : o) Hierachie of classes, with generic search of sub-string, stream manip, specialization for tech       -
-                (replace/extract), and templatization for 'char' and 'string'                                        -
-                                                                                                                     -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Rev. date | Reviser               | Revise's description                                                            -
- - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ../../....| ........              | ...                                                                             -
---------------------------------------------------------------------------------------------------------------------*/

#ifndef UNTAGGER_H
#define UNTAGGER_H

#include <deque>
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include "globaldef.h"
#include "flow_basic_operators.h"

#ifdef _USE_NAMESPACES
    namespace Else { 
#endif // _USE_NAMESPACES

class FiltersPool: public CharFlowAgent {
    //
    // Le pool est constitué de paire (filtre*, booleen), la valeur logique indiquant
    // si l'instance pointee doit ou non etre desallouée en fin de cycle de vie.
    // [A remplacer par des auto_pointers (voir auto_deallocated, avec comptabilisation du nb de references)]
    // 
    typedef vector< pair<FilterPtr, bool> > FiltersCollection;

public:
    FiltersPool(CharFlowListener& endFlow)
    : endFlow(&endFlow),
      headFlow(&endFlow),
      pool()
    { }

    FiltersPool()
    : endFlow(0),
      headFlow(0),
      pool()
    { }    

    virtual ~FiltersPool()
    {
        flush(); 
        for (FiltersCollection::const_iterator op = pool.begin(); op != pool.end(); ++op)
            if ((*op).second) delete((*op).first);
        pool.clear();
    }

    void addFilter(Filter& filter)
    {
    	assert(endFlow);
        filter.setNextAgent(*endFlow);
        if (!pool.empty()) pool.back().first->setNextAgent(filter);
        else headFlow = &filter;
        pool.push_back(make_pair(&filter,false));
    }

    void addFilter(const FilterPtr filterPtr, const bool autoDeallocate = false)
    {
        assert(filterPtr);
        assert(endFlow);
        filterPtr->setNextAgent(*endFlow);
        if (!pool.empty()) pool.back().first->setNextAgent(*filterPtr);
        else headFlow = filterPtr;
        pool.push_back(make_pair(filterPtr, autoDeallocate));
    }

    void setEndFlow(CharFlowListener& endFlow)
    {
    	this->endFlow = &endFlow;
    	if (pool.empty()) this->headFlow = &endFlow;
    	else pool.back().first->setNextAgent(endFlow);
    }

    void flush()
    {
        for (FiltersCollection::const_iterator op = pool.begin(); op != pool.end(); ++op)
            op->first->flush();
    }
    
    void computeFlowItem(const ichar& data)
    {
        (*headFlow) <<= data;
    }

protected:
    CharFlowListenerPtr endFlow;
    CharFlowListenerPtr headFlow;
    FiltersCollection   pool;
};


class AnchoredFiltersPool : public CharFlowAgent {

    typedef vector< pair<AnchoredFilterPtr, bool> > FiltersCollection;

public:
    AnchoredFiltersPool(CharFlowListener& endFlow, ostream& anchorStream)
    : initialized(false),
      endFlow(&endFlow),
      headFlow(&endFlow),
      anchoredStream(&anchorStream),
      pool()
    { }

    ~AnchoredFiltersPool()
    {
        flush(); 
        for (FiltersCollection::const_iterator op = pool.begin(); op != pool.end(); ++op)
            if (op->second) delete(op->first);
        pool.clear();
    }

    void addFilter(AnchoredFilter& filter)
    {
        filter.setAnchoredStream(*anchoredStream);
        filter.setNextAgent(*endFlow);
        if (!pool.empty()) pool.back().first->setNextAgent(filter);
        else headFlow = &filter;
        pool.push_back(make_pair(&filter, false));
    }

    void addFilter(const AnchoredFilterPtr filterPtr, const bool autoDeallocate = false)
    {
        assert(filterPtr);
        filterPtr->setNextAgent(*endFlow);
        filterPtr->setAnchoredStream(*anchoredStream);
        if (!pool.empty()) pool.back().first->setNextAgent(*filterPtr);
        else headFlow = filterPtr;
        pool.push_back(make_pair(filterPtr, autoDeallocate));
    }
    
    void initialize()
    {
        for (FiltersCollection::const_iterator op = pool.begin(); op != pool.end(); ++op) {
            op->first->writeDefinition(*anchoredStream);
            (*anchoredStream) << '\n';
        }
        initialized = true;
    }

    void flush() 
    {
        for (FiltersCollection::const_iterator op = pool.begin(); op != pool.end(); ++op)
            op->first->flush();
    }
    
    void computeFlowItem(const ichar& data)
    {
        assert(initialized);
        (*headFlow) <<= data;
    }
    

protected:
    bool                initialized;
    CharFlowListenerPtr endFlow;
    CharFlowListenerPtr headFlow;
    ostream*            anchoredStream;
    FiltersCollection   pool;
};

#ifdef _USE_NAMESPACES
}
#endif // _USE_NAMESPACES

#endif // UNTAGG_H

