#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 <fstream>
#include <vector>
#include <string>
#include <iterator>
#include <bobcat/errno>
#include <bobcat/pattern>

namespace FBB
{

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

    typedef std::vector<std::string>::iterator iterator;
    iterator d_current;
    iterator d_end;
    Pattern  d_pattern;

    public:
        RE_iterator &operator++();

        bool operator==(RE_iterator const &other) const;
        bool operator!=(RE_iterator const &other) const;
        std::string const &operator*() const;
        std::string const *operator->() const;

    private:
        RE_iterator(iterator const &begin, iterator const &end, 
                    std::string const &re, bool caseSensitive);

        RE_iterator(iterator const &end);
        RE_iterator::iterator find();

        static bool reMatch(std::string &str, RE_iterator &re_iter);
};


class ConfigFile: public std::vector<std::string>
{
    bool d_rmComment;
    bool d_caseSensitive;
    bool d_indices;
    size_t d_rawIndex;        
    size_t d_nextIndex;        
    std::vector<size_t> d_index;

    public:
        typedef RE_iterator const_RE_iterator;

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

        enum Indices
        {
            IgnoreIndices,
            StoreIndices
        };

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

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

        void setCommentHandling(Comment type);
        void setSearchCasing(SearchCasing type);
        void open(std::string const &fname);
        const_RE_iterator beginRE(std::string const &re) const;
        const_RE_iterator endRE() const;
        const_iterator find(std::string const &target) const;
        const_iterator findRE(std::string const &re) const;
        size_t index(size_t lineNr);
        size_t index(const_iterator const &iterator);

    private:
        static bool contains(std::string const &str, std::string &target);
        static bool match(std::string const &str, Pattern &pat);
        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);
};

inline bool RE_iterator::operator==(RE_iterator const &other) const
{
    return d_current == other.d_current;
}

inline bool RE_iterator::operator!=(RE_iterator const &other) const
{
    return d_current != other.d_current;
}

inline std::string const &RE_iterator::operator*() const
{
    return *d_current;
}

inline std::string const *RE_iterator::operator->() const
{                     
    return &*d_current;
}

inline RE_iterator::RE_iterator(iterator const &end)
:
    d_current(end),
    d_end(end)
{}

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

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

inline ConfigFile::const_RE_iterator 
ConfigFile::beginRE(std::string const &re) const
{
    return RE_iterator(
        const_cast<ConfigFile *>(this)->begin(), 
        const_cast<ConfigFile *>(this)->end(), 
        re, d_caseSensitive);
}

inline ConfigFile::const_RE_iterator ConfigFile::endRE() const
{
    return RE_iterator(const_cast<ConfigFile *>(this)->end());
}

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 bool ConfigFile::contains(std::string const &str, 
                     std::string &target)
{
    return str.find(target) != std::string::npos;
}

inline bool ConfigFile::match(std::string const &str, Pattern &pat)
{
    return pat << str;
}

} // FBB

#endif

