#ifndef INCLUDED_BOBCAT_CONFIGFILE_
#define INCLUDED_BOBCAT_CONFIGFILE_

//    Lines are stored with initial WS removed.
//    If a line ends in \, then the next line (initial WS removed)
//    is appended to the current line.
//    Information at and beyond the first # on individual lines is removed
//    if the rmComment flag is set to true
//    Then, lines containing only blanks and tabs are not stored

#include <vector>
#include <string>
#include <iterator>

#include <bobcat/exception>

namespace FBB
{

struct RE_iterator
{
    friend class ConfigFile_;

    friend int operator-(RE_iterator const &lhs,        // opneg.f
                         RE_iterator const &rhs);

    friend bool operator==(RE_iterator const &lhs, RE_iterator const &rhs);

    using iterator_category = std::input_iterator_tag;
    using difference_type   = std::ptrdiff_t;
    using value_type        = std::string const;
    using pointer           = value_type *;
    using reference         = value_type &;

    private:
                                // contains iterators to lines matching REs
        using VsIterator = std::vector<std::string>::const_iterator;
        using VsIterVector = std::vector<VsIterator>;

        VsIterVector const &d_vsIter;
        size_t d_idx;

    public:
        RE_iterator &operator++();

        std::string const &operator*() const;           // opstar.f
        std::string const *operator->() const;          // oparrow.f

    private:
        RE_iterator(VsIterVector const &vsIter, size_t idx);
};

struct CFEnums_
{
        using const_RE_iterator = RE_iterator;
        using const_iterator = std::vector<std::string>::const_iterator;
        using RE_iteratorPair = 
                        std::pair<const_RE_iterator, const_RE_iterator>;
                                                        

        enum Comment
        {
            KeepComment,
            RemoveComment
        };
        enum SearchCasing
        {
            SearchCaseSensitive,
            SearchCaseInsensitive
        };

        enum Indices
        {
            IgnoreIndices,
            StoreIndices
        };
};

class ConfigFile_;
class ConfigFile: public CFEnums_
{
    ConfigFile_ *d_ptr;

    public:
        explicit ConfigFile(Comment cType = KeepComment,        // 1
                   SearchCasing sType = SearchCaseSensitive,
                   Indices iType = IgnoreIndices);
                                                        // 2
        explicit ConfigFile(std::string const &fname,   // config file name
                    Comment cType = KeepComment,
                    SearchCasing sType = SearchCaseSensitive,
                    Indices iType = IgnoreIndices);

        ConfigFile(ConfigFile &&tmp);                   // 3
        ConfigFile(ConfigFile const &rhs);              // 4

        ~ConfigFile();

        ConfigFile &operator=(ConfigFile &&tmp);
        ConfigFile &operator=(ConfigFile const &rhs);   // 2

        void open(std::string const &fname);

        void setCommentHandling(Comment type);
        void setSearchCasing(SearchCasing type);

        const_iterator begin() const;
        const_iterator end() const;

        RE_iteratorPair beginEndRE(std::string const &re) const;    // 1
        RE_iteratorPair beginEndRE() const;                         // 2

        const_iterator find(std::string const &target) const;
        const_iterator findRE(std::string const &re) const;

        std::string findKey(std::string const &key, size_t count = 1) const;

        std::string findKeyTail(std::string const &key,
                                size_t count = 1) const;

        size_t index(size_t lineNr);                        // 1
        size_t index(const_iterator const &iterator);       // 2

        std::string const &operator[](size_t idx) const;

        size_t size() const;
};

inline std::string const &RE_iterator::operator*() const
{
    return *d_vsIter[d_idx];
}
inline std::string const *RE_iterator::operator->() const
{
    return &*d_vsIter[d_idx];
}

    // Free members:

bool operator==(RE_iterator const &lhs, RE_iterator const &rhs);

inline bool operator!=(RE_iterator const &lhs, RE_iterator const &rhs)
{
    return not (lhs == rhs);
}
inline int operator-(RE_iterator const &lhs, RE_iterator const &rhs)
{
    return lhs.d_idx - rhs.d_idx;
}


} // FBB

#endif
