#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>;

    struct LopData;
    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});

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

        FPattern(States &states,                     // 3: lop fpattern,
                size_t tailLength,                  // having 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
        size_t end() const;                         // fpattern's last state
        Pair const &pair() const;                   // {begin, end}
        Pair rhsPair() const;                       // LOP's rhs's states
        FPattern const &lhs() const;                 // LOP's lhs duplicate

        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;
        bool fixedLength() const;
        size_t length() const;          // if fixedLength() the length of the
                                        // text matched by this fpattern

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

    private:
        FPattern duplicate(States &states) const;    // return FPattern which
                                                    // duplicates (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, PairVector &pv);
        static void copyFPattern(States &states, size_t lower, 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);
};

struct FPattern::LopData
{
    size_t scOrLength;              // Start condition index, or tail length
                                    // for fixed-sized tail LOP rules
    size_t 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 lhs;                    // duplicate of the LHS fpattern or an
                                    // empty fpattern with fixed-sized LOP
                                    // rules 
};

inline FPattern const &FPattern::lhs() const
{
    return d_lopData->lhs;
}

inline size_t FPattern::scIndex() const
{
    return d_lopData->scOrLength;
}

inline size_t FPattern::lopTailLength() const
{
    return d_lopData->scOrLength;
}

inline size_t FPattern::begin() const
{
    return d_pair.first;
}

inline size_t FPattern::end() const
{
    return d_pair.second;
}

inline FPattern::Pair const &FPattern::pair() const
{
    return d_pair;
}

inline FPattern::Pair FPattern::rhsPair() const
{
    return Pair(d_lopData->mid, d_pair.second);
}

inline FPattern::RuleType FPattern::type() const
{
    return d_lopData == 0 ?         RuleType::NORMAL :
           d_lopData->mid == 0 ?    RuleType::LOP_FIXED :
                                    RuleType::LOP_1;
}

inline bool FPattern::fixedLength() const
{
    return d_length != s_max;
}

inline size_t FPattern::length() const
{
    return d_length;
}

#endif

