#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/pattern>

namespace FBB
{

class RE_iterator: public std::iterator<std::input_iterator_tag, std::string>
{
    friend class ConfigFile;

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

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

    VsIterVector const &d_vsIter;
    size_t d_idx;

    public:
        RE_iterator &operator++();

        std::string const &operator*() const;
        std::string const *operator->() const;

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


class ConfigFile
{
    std::vector<std::string> d_line;

    bool d_rmComment;
    bool d_caseSensitive;
    bool d_indices;
    size_t d_rawIndex;        
    size_t d_nextIndex;        
    std::vector<size_t> d_index;

                            // contains iterators to lines matching REs
    typedef std::vector<std::string>::const_iterator VsIterator;
    typedef std::vector<VsIterator> VsIterVector;
    VsIterVector d_vsIter;
    std::string d_re;

    mutable Pattern d_pattern;

    public:
        typedef RE_iterator const_RE_iterator;
        typedef std::vector<std::string>::const_iterator const_iterator;

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

        enum Indices
        {
            IgnoreIndices,
            StoreIndices
        };

        explicit ConfigFile(Comment cType = KeepComment, 
                   SearchCasing sType = SearchCaseSensitive,
                   Indices iType = IgnoreIndices);

        explicit ConfigFile(std::string const &fname,// Name of the config file
                    Comment cType = KeepComment, 
                    SearchCasing sType = SearchCaseSensitive,
                    Indices iType = IgnoreIndices);

        ConfigFile(ConfigFile &&tmp);

        ConfigFile &operator=(ConfigFile const &rhs) = default;
        ConfigFile &operator=(ConfigFile &&tmp);

        void setCommentHandling(Comment type);
        void setSearchCasing(SearchCasing type);
        void open(std::string const &fname);

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

        const_RE_iterator beginRE(std::string const &re);
        const_RE_iterator endRE() const;

        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);
        std::string findKeyTail(std::string const &key, size_t count = 1);

        size_t index(size_t lineNr);
        size_t index(const_iterator const &iterator);

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

        size_t size() const;

        ConfigFile(ConfigFile const &&tmp);             // Deprecated
        ConfigFile &operator=(ConfigFile const &&tmp);  // Deprecated

    private:
        size_t append_next(std::istream &istr, std::string &line);
        bool hasContent(std::string const &line);
        bool nextLine(std::istream &istr, std::string &line);
        void removeComment(std::string &line);
        void removeTrailingBlanks(std::string &line);
        void resetVsIter(std::string const &re);
        std::string searchFor(std::string const &keyPattern, size_t count);

//        static bool match(std::string const &str, Pattern &pat);
        static bool finder(std::string const &haystack, 
                           std::string const &needle);
        static bool casefinder(std::string const &haystack, 
                               std::string const &needle);
};

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];
}

inline size_t ConfigFile::size() const
{
    return d_line.size();
}

inline void ConfigFile::setCommentHandling(Comment type)
{
    d_rmComment = type == RemoveComment;
}

inline std::string const &ConfigFile::operator[](size_t idx) const
{
    return d_line[idx];
}

inline void ConfigFile::setSearchCasing(SearchCasing type)
{
    d_caseSensitive = type == SearchCaseSensitive;
}

inline ConfigFile::const_iterator ConfigFile::begin() const
{
    return d_line.begin();
}
   
inline ConfigFile::const_iterator ConfigFile::end() const
{
    return d_line.end();
}
   
inline ConfigFile::const_RE_iterator ConfigFile::endRE() const
{
    return RE_iterator(d_vsIter, d_vsIter.size());
}

inline size_t ConfigFile::index(size_t lineNr)
{
    return d_index[lineNr];
}

inline size_t ConfigFile::index(const_iterator const &iterator)
{
    return d_index[iterator - begin()];
}


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

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

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


} // FBB

#endif

