#ifndef INCLUDED_FPATTERN_
#define INCLUDED_FPATTERN_

#include <memory>
#include <iosfwd>
#include <vector>
#include <set>

#include <bobcat/linearmap>

#include "../flextypes/flextypes.h"

class CharClass;
class Interval;
class States;

class FPattern: private FlexTypes
{
    using Map = FBB::LinearMap<size_t, size_t>;
    using Pair = std::pair<size_t, size_t>;
    using PairVector = std::vector<Pair>;

    class LopData
    {
        size_t d_scOrLength;        // Start condition index, or tail length
                                    // for fixed-sized tail LOP rules
        size_t d_mid;               // begin of the RHS fpattern in d_pair
                                    // or 0, in which case scOrLength
                                    // is the length of the fixed-sized 
                                    // LOP rhs fpattern's tail
        FPattern *d_lhs;            // duplicate of the LHS fpattern or an
                                    // empty fpattern with fixed-sized LOP
                                    // rules 
        public:
            LopData();                                                  // .f
            LopData(size_t startCond, size_t mid,                       // 1
                    FPattern *lhs = 0); 
            ~LopData();                                                 // .f

            size_t scOrLength() const;                                  // .f
            size_t mid() const;                                         // .f
            FPattern const &lhs() const;                                // .f
    };

    std::shared_ptr<LopData> d_lopData;

    Pair d_pair;

    static size_t const s_max = -1;
    size_t d_length = s_max;        // if fixed: the length of the fpattern 
                                    // otherwise s_max

    public:
        FPattern(Pair const &pair = Pair{0, 0});                        //  1

        FPattern(States &states,                        // lop fpattern     2
                FPattern const &lhs, FPattern const &rhs,     
                                    size_t lopStartCondition);    

        FPattern(States &states,                        // lop fpattern,    3
                size_t tailLength,                  // with fixed sized tail.
                FPattern const &lhs, FPattern const &rhs);    

        FPattern &operator=(FPattern const &other) = default;
        FPattern &operator=(Pair const &pair);

        size_t begin() const;                   // fpattern's first state   .f
        size_t end() const;                     // fpattern's last state    .f
        Pair const &pair() const;               // {begin, end}             .f
        Pair rhsPair() const;                   // LOP's rhs's states       .f
        FPattern const &lhs() const;            // LOP's lhs duplicate      .f

        bool canBeEmpty(States const &states) const;// true if there's an
                                                    // empty transition from
                                                    // begin -> end or if the
                                                    // FPattern has a fixed
                                                    // length of 0.

        static FPattern eof(States &states);
        static FPattern escape(States &states, std::string const &ch);
        static FPattern rawText(States &states, std::string const &str);
        static FPattern dot(States &states);
        static FPattern concatenate(States &states, FPattern const &lhs,
                                                      FPattern const &rhs);
        static FPattern alternatives(States &states, FPattern const &lhs, 
                                                       FPattern const &rhs);
        static FPattern quantifier(States &states, FPattern const &pat, 
                                     size_t type);
        static FPattern interval(States &states, FPattern &regex, 
                                                   Interval const &interval);
        static FPattern characterClass(States &states, CharClass &charClass);

        RuleType type() const;                                          //  .f
        bool fixedLength() const;                                       //  .f
        size_t length() const;          // if fixedLength() the length of   .f
                                        // the text matched by this fpattern

        size_t scIndex() const;         // 1st SC index of a LOP rule   //  .f
                                        // (undefined behavior if called 
                                        //  for non-LOP rules)
        size_t lopTailLength() const;   // length of a fixed tail of a LOP  .f
                                        // rule as retrieved by
                                        // Generator::actions 

    private:
        FPattern *duplicate(States &states) const;  // return FPattern
                                                    // duplicating (new copy)
                                                    // the states of `this'
                                                    // fpattern.

        static bool empty(std::set<size_t> &indices, 
                          States const &states, size_t idx);

        static FPattern star(States &states, FPattern const &fpattern);
        static FPattern plus(States &states, FPattern const &fpattern);
        static FPattern questionMark(States &states, 
                                       FPattern const &fpattern);

        // lower and upper are the lower and upper limits of an Interval
        static FPattern copy(States &states, 
                                FPattern &fpattern, 
                                size_t lower, size_t upper);

        static void copyFPattern(States &states, size_t count,          //  1
                                                 PairVector &pv);
        static void copyFPattern(States &states, size_t lower,          //  2
                                 size_t upper, Map &eject, PairVector &pv);

            // used by copyFPattern
        static size_t dupFPattern(Map &map, States &states, size_t idx);

        static FPattern optRepeatLastFPattern(States &states, 
                                               FPattern &regex,
                                               size_t lower, 
                                               PairVector &beginEnd);
        static FPattern optionalFPatterns(States &states, 
                            FPattern &fpattern, 
                            size_t lower, size_t upper, 
                            PairVector &beginEnd);
        static void jumpToEnd(States &states, PairVector &beginEnd, 
                              size_t lower, size_t upper, Map &eject);
        static void join(States &states, FPattern &fpattern, size_t upper,
                                         PairVector const &beginEnd);
};

#include "lopdata.f"
#include "fpattern.f"

#endif

