#ifndef __repl__
#define __repl__

#include <iterator>
#include <string>

#include "filter.hh"
#include "repl_util.hh"

#include "try_hard.hh"

namespace autil {
  class ConfigData;
}

namespace afilter {

  class ReplOpts;

  typedef int unichar;

  struct ReplSingleOut {};
  struct ReplMultiOut {};

  class CharReplInfo {
  public:
    virtual std::string name() const = 0;
    virtual double order_num() const = 0;

    virtual int max_in() const = 0;
    virtual int max_out() const = 0;
  };

  template <typename T>
  class CharReplBase : virtual public CharReplInfo {
  public:
    typedef typename T::Base      Base;
    typedef typename T::Itr       Itr;
    typedef typename T::InValue   InValue;
    typedef typename T::Value     Value;
    typedef typename T::ItrRoot   ItrRoot;
    typedef typename T::Buffer    Buffer;
    typedef typename T::SingleMulti SingleMulti;

    virtual CharReplBase * clone() const = 0;
    virtual void assign(const CharReplBase * other) = 0;

    virtual ~CharReplBase() {}

    virtual int scan_needed() const {return 0;}
  
    virtual void reset() {}
    virtual void scan(Itr & begin, const ItrRoot & stop) {}
    virtual bool fill(InValue c, Itr *, Buffer *) = 0;
    //: Itr is expected to be AT the last charter read in.  Not one past.
    // if it is unable to translate the character it should return 0
  protected:
    CharReplBase & operator=(const CharReplBase &) {return *this;}
  };

  struct char2char {
    typedef FilterItrPart      Itr;
    typedef FilterItrRoot      ItrRoot;

    typedef char               InValue;
    typedef char               Value;

    typedef SimpleBuffer<char> Buffer;

    typedef ReplMultiOut       SingleMulti;

    typedef CharReplBase<char2char> Base;
  };

  struct char2single : public char2char {
    typedef CharReplBase<char2single> Base;
    typedef ReplSingleOut             SingleMulti;
  };

  struct char2uni  {
    typedef FilterItrPart      Itr;
    typedef FilterItrRoot      ItrRoot;

    typedef char               InValue;
    typedef unichar            Value;

    typedef DummyBufferItr<unichar>   Buffer;

    typedef ReplSingleOut             SingleMulti;

    typedef CharReplBase<char2uni>    Base;
  };

  struct uni2char {
    struct Void {};
    typedef Void               Itr;
    typedef Void               ItrRoot;

    typedef unichar            InValue;
    typedef char               Value;

    typedef SimpleBuffer<char> Buffer;

    typedef ReplMultiOut       SingleMulti;

    typedef CharReplBase<uni2char>   Base;
  };

  struct uni2single : public uni2char {
    typedef CharReplBase<uni2single>   Base;
    typedef ReplSingleOut       SingleMulti;
  };  

  template <typename T = char2char>
  class straight_through : public T::Base
  {
  public:
    typedef typename T::Base        Base;
    typedef typename T::Itr         Itr;
    typedef typename T::InValue     InValue;
    typedef typename T::Value       Value;
    typedef typename T::ItrRoot     ItrRoot;
    typedef typename T::Buffer      Buffer;
    typedef typename T::SingleMulti SingleMulti;

    straight_through * clone() const {return new straight_through(*this);}
    void assign(const Base * other) {}

    string name()      const {return "char/char";}
    double order_num() const {return 0.10;}
    int    max_in()    const {return 1;}
    int    max_out()   const {return 1;}

    bool fill(InValue c, Itr *, Buffer * out) {*out += c; return true;}
  };

  template <typename CharRepl, int MaxIn=-1, int MaxOut=-1>
  class ReplItr : public FilterItrPart
  {
  private:
    typedef FilterItrPart             Itr;
    typedef FilterItrRoot             ItrRoot;     
    typedef typename CharRepl::Buffer Buffer;
    typedef typename CharRepl::Base   CharReplBase;

  public:
    ReplItr();
    ReplItr(ConfigData *, const ReplOpts *);
    ReplItr(const CharReplBase & cr);

    ReplItr * clone() const;
    void assign(const FilterItrPart * other);
  
    string name() const;
    double order_num() const;

    void reset() {repl.reset();}

    void scan(const ItrRoot & stop) {repl.scan(*itr, stop);}
    char first();
    char next();

    CharRepl & char_repl() {return repl;}
  
  private:
    CharRepl       repl;
    Buffer         buffer;
    ClonePtr<Itr>  last;
  };

  //
  // dual_repl combines two repl and combines them to one
  //   A -> B & B -> C  becomes A -> C
  //
  // CharReplFrom must write a single charter to a dummy_buffer_iterator 
  // and CharReplTo must have the the dummy_buffer_iterator as the Itr
  // The EndItr of CharReplTo must be a never_end_iterator.  CharReplTo must
  // not have any state information.
  //
  template <typename From, typename To> 
  struct DualReplTypes
  {
    typedef typename From::Itr     Itr;
    typedef typename From::ItrRoot ItrRoot;
    typedef typename From::InValue InValue;
    typedef typename To::Value     Value;
    typedef typename To::Buffer    Buffer;
    typedef typename To::SingleMulti SingleMulti;
    typedef CharReplBase<DualReplTypes> Base;
  };

  template <typename From, typename To>
  class DualRepl : public CharReplBase<DualReplTypes<From,To> >
  {
  public:
    typedef DualReplTypes<From,To>      T;
    typedef typename T::Base        Base;
    typedef typename T::Itr         Itr;
    typedef typename T::InValue     InValue;
    typedef typename T::Value       Value;
    typedef typename T::ItrRoot     ItrRoot;
    typedef typename T::Buffer      Buffer;
    typedef typename T::SingleMulti SingleMulti;

    DualRepl();
    DualRepl(From * f, To * t, ConfigData & o) 
      : repl_from(f), repl_to(t), try_hard(o, 0) {}

    Base * clone() const {return new DualRepl(*this);}
    void assign(const Base * other) {*this = *(const DualRepl *)(other);}
  
    string name() const;
    double order_num() const;
  
    int max_in() const;
    int max_out() const;
  
    int scan_needed() const;

    void reset();

    void scan(Itr & begin, const ItrRoot & stop);

    bool fill(InValue c, Itr * i, Buffer * b);
  
  private:
    ClonePtr<From> repl_from;
    ClonePtr<To  >   repl_to;

    TryHard try_hard;

    DummyBufferItr<unichar> dummy_buf;
  };

  template <typename T> 
  class PairRepl : public T::Base
  {
  private:
    int max(int x, int y) const {return x > y ? x : y;}
  public:
    typedef typename T::Base        Base;
    typedef typename T::Itr         Itr;
    typedef typename T::InValue     InValue;
    typedef typename T::Value       Value;
    typedef typename T::ItrRoot     ItrRoot;
    typedef typename T::Buffer      Buffer;
    typedef typename T::SingleMulti SingleMulti;
    typedef typename T::First       First;
    typedef typename T::Second      Second;

    PairRepl(First * f, Second * s, ConfigData &)
      : first(f), second(s) {}

    void assign(const Base * other) {*this = *(const PairRepl *)(other);}

    string name() const {return first->name() + "&" +  second->name();}
    double order_num() const {return first->order_num();}
    int max_in()  const {return max(first->max_in(),  second->max_in()); }
    int max_out() const {return max(first->max_out(), second->max_out());}
  
    void reset() {} // expected to be stateless
    void scan(Itr & begin, const ItrRoot & stop) {}
  protected:
    ClonePtr<First>  first;
    ClonePtr<Second> second;
  };

  template <typename T>
  class PairToUniRepl : public PairRepl<T>
  {
  public:
    typedef typename T::Base        Base;
    typedef typename T::Itr         Itr;
    typedef typename T::InValue     InValue;
    typedef typename T::Value       Value;
    typedef typename T::ItrRoot     ItrRoot;
    typedef typename T::Buffer      Buffer;
    typedef typename T::SingleMulti SingleMulti;
    typedef typename T::First       First;
    typedef typename T::Second      Second;
    PairToUniRepl(First * f, Second * s, ConfigData & o)
      : PairRepl<T>(f,s,o) {}
    Base * clone() const {return new PairToUniRepl(*this);}

    bool fill(InValue c, Itr * i, Buffer * b) {
      if (first->fill(c,i,b))
	return true;
      else if (second->fill(c,i,b))
	return true;
      else
	return false;
    }
  };

  template <typename T>
  class PairFromUniRepl : public PairRepl<T>
  {
  public:
    typedef typename T::Base        Base;
    typedef typename T::Itr         Itr;
    typedef typename T::InValue     InValue;
    typedef typename T::Value       Value;
    typedef typename T::ItrRoot     ItrRoot;
    typedef typename T::Buffer      Buffer;
    typedef typename T::SingleMulti SingleMulti;
    typedef typename T::First       First;
    typedef typename T::Second      Second;

    PairFromUniRepl(First * f, Second * s, ConfigData & o)
      : PairRepl<T>(f,s,o) {}

    Base * clone() const {return new PairFromUniRepl(*this);}

    bool fill(InValue c, Itr * i, Buffer * b) {
      if (second->fill(c,i,b))
	return true;
      else if (first->fill(c,i,b))
	return true;
      else
	return false;
    }
  };

  template <typename T>
  struct PairReplVirtual : public T
  {
    typedef CharReplBase<T> First;
    typedef CharReplBase<T> Second;
  };

  template <typename Buffer, typename String>
  void add_to_buffer(Buffer & b, const String & s, ReplSingleOut) {
    b += *s.begin();
  }

  template <typename Buffer, typename String>
  void add_to_buffer(Buffer & b, const String & s, ReplMultiOut) {
    b = s;
  }

  struct ReplOpts {
    virtual ~ReplOpts() {}
  };

  template <class Filter, class Type>
  FilterItrPart * get_filter(ConfigData * opts, const ReplOpts * lopts);

  template <class Filter, class Type>
  FilterItrPart * get_filter(const CharReplBase<Type> & r);
  
  template <class Filter, class Type>
  CharReplBase<Type> *get_repl(ConfigData * opts, const ReplOpts * lopts);

  FilterItrPart * get_repl_itr(const string & in, const string & out, 
			       ConfigData & opts);
}

#endif
