#ifndef INCLUDED_BOBCAT_PATTERN_
#define INCLUDED_BOBCAT_PATTERN_

#include <regex.h>

#include <string>
#include <utility>              // for pair<>
#include <memory>

#include <bobcat/exception>

//  match() throws an Errno when either the construction (i.e.,
//  compilation of the pattern) or the match failed.
//
//  The 0-index for position() or operator[] indicates the matched text,
//  other indices indicate subsequent subexpressions
//
//  Patterns may use:
//      \b - indicating a word-boundary
//      \d - indicating a digit
//      \D - indicating no digit
//      \s - indicating a white-space ([:space:]) char
//      \S - indicating no white-space ([:space:]) char
//
//  Pattern strings:
//
//      ------------------------------------------------------------
//      Required pattern        Provide Pattern()   Use as argument:
//                              internally with:
//      ------------------------------------------------------------
//      \\                      \\\\                \\\\\\\\       |
//      \d                      \d                  \\d            |
//      ------------------------------------------------------------
//
// When using Raw String Literals the required patterns can be used as-is

//  define a pattern using a case-flag and number of
//  ()-subexpressions. Compilation flags:
//
//  REG_EXTENDED
//      Use POSIX Extended Regular Expression syntax when interpreting regex.
//      If not set, POSIX Basic Regu- lar Expression syntax is used.
//
//  REG_NOSUB
//      Support for substring addressing of matches is not required.  The
//      nmatch and pmatch parameters to regexec are ignored if the pattern
//      buffer supplied was compiled with this flag set.
//
//  REG_NEWLINE
//      Match-any-character  operators  don't  match a newline.
//
//      A non-matching list ([^...])  not containing a newline does not match
//      a newline.
//
//      Match-beginning-of-line operator (^) matches the empty string
//      immediately after a newline, regardless of whether eflags, the
//      execution flags of regexec, contains REG_NOTBOL.

//
//      Match-end-of-line operator ($) matches the empty string immediately
//      before a newline, regardless of whether eflags contains REG_NOTEOL.

namespace FBB
{

class Pattern
{
    struct Regex
    {
        regex_t  d_regex;
        std::string  d_converted;

        Regex(std::string pattern, int options);
        ~Regex();                                   // destructor.f

        private:
            Regex(Regex const &other) = delete;
    };

    std::shared_ptr<Regex>          d_regex;
    std::shared_ptr<regmatch_t[]>   d_subExpression;
        // by using a shared_ptr<string> the size of the new Pattern class
        // is equal to the size of Bobcat's version 6.12.01 Pattern class
    std::shared_ptr<std::string>    d_text;         // text passed to match()

    size_t                          d_nSub;
    size_t                          d_beyondLast;
    int                             d_matchOptions;

    public: 
        using Position = std::pair<size_t, size_t>;

        Pattern();                                      // 1

        explicit Pattern(std::string const &pattern,    // 2
                        bool caseSensitive = true,
                        size_t nSub = 10,
                        int options = REG_EXTENDED | REG_NEWLINE);

                                                        // set match options
        Pattern &operator<<(int matchOption);           // oplshift1.cc

        bool operator<<(std::string const &text);       // oplshift2.cc

                                                        //FBB -> setpattern.f
        void setPattern(std::string const &pattern,     // calls the move
                        bool caseSensitive = true,      // assignment op.
                        size_t nSub = 10,
                        int options = REG_EXTENDED | REG_NEWLINE);

        //  match a string with a pattern. true: string matched
        //  options could be:
        //
        //  REG_NOTBOL
        //      The  match-beginning-of-line  operator always fails
        //      to match (but see the compilation flag  REG_NEWLINE
        //      above)  This  flag  may be used when different portions
        //      of a string are passed  to  regexec  and  the
        //      beginning  of  the string should not be interpreted
        //      as the beginning of the line.
        //
        //  REG_NOTEOL
        //      The  match-end-of-line  operator  always  fails  to
        //      match  (but  see  the  compilation flag REG_NEWLINE)

        void match(std::string const &text, int options = 0);

                                        //FBB -> .f
        std::string before() const;  // text before the matched text
        std::string matched() const;   // the matched text              .f
        std::string beyond() const;  // text beyond the matched text    .cc

            // (0) is complete matching part. Remaining are subexpressions.
            // (npos, npos) is returned if index exceeds available indices
            // (which may be 0)

            // Position info of subexpression
        Position position(size_t index) const;

            // subexpression text itself
        std::string operator[](size_t index) const;
        size_t end() const;           // index beyond the last available    .f

        std::string const &pattern() const; // compiled pattern             .f

        void swap(Pattern &other);

    private:
        void newRegex(std::string const &pattern, int options);         // .f
        void destroy();
};

inline std::string Pattern::matched() const
{
    return (*this)[0];
}
inline std::string const &Pattern::pattern() const
{
    return d_regex->d_converted;
}
inline size_t Pattern::end() const
{
    return d_beyondLast;
}

} // FBB

#endif
