#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
            {
                return d_current == other.d_current;
            }
            bool operator!=(RE_iterator const &other) const
            {
                return d_current != other.d_current;
            }
            std::string const &operator*() const
            {
                return *d_current;
            }
            std::string const *operator->() const
            {                     
                return &*d_current;
            }
        private:
            RE_iterator(iterator const &begin, iterator const &end, 
                        std::string const &re, bool caseSensitive);
    
            RE_iterator(iterator const &end)
            :
                d_current(end),
                d_end(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)
            {
                d_rmComment = type == RemoveComment;
            }

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

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

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

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

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

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

        private:
            static bool contains(std::string const &str, 
                                 std::string &target)
            {
                return str.find(target) != std::string::npos;
            }

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

}

#endif

