#ifndef INCLUDED_BOBCAT_MAILHEADERS_
#define INCLUDED_BOBCAT_MAILHEADERS_

#include <istream>
#include <string>
#include <vector>

#include <bobcat/string>
#include <bobcat/exception>

namespace FBB
{

class MailHeaders
{
    public:
        using const_iterator = std::vector<std::string>::const_iterator;
        using const_reverse_iterator = 
                            std::vector<std::string>::const_reverse_iterator;
        enum Mode
        {
            DONT_READ,
            READ
        };
        enum Match
        {
            FAIL,
            INITIAL,
            PARTIAL,
            FULL,

            caseInsensitive,
            CASE_INITIAL = caseInsensitive,
            CASE_PARTIAL,
            CASE_FULL,
            lastMatch = CASE_FULL
        };

    private:
        enum Size
        {
            SIZEOFMATCH = lastMatch + 1,
        };

        std::vector<std::string> d_lines;
        std::istream            &d_in;
        std::string             d_hdr;
        Match                   d_match;

    public:
        struct const_hdr_iterator
        {
            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 &;


            friend MailHeaders;
            using Comparator = 
                bool(*)(std::string const &header, std::string const &key);

            MailHeaders const  *d_mh;
            std::string         d_key;
            Comparator          d_comparator;
            const_iterator      d_current;

            static Comparator s_comparator[];

            const_hdr_iterator(MailHeaders const *mailHeaders,
                               const_iterator begin);

            const_iterator lookup(const_iterator const &old) const;
            const_iterator lookdown(const_iterator const &old) const;

            static bool fail(std::string const &hdr,            // .f
                                std::string const &key);
            static bool initial(std::string const &hdr,         // .f
                                std::string const &key);
            static bool partial(std::string const &hdr,         // .f
                                std::string const &key);
            static bool full(std::string const &hdr,            // .f
                                std::string const &key);
            static bool caseInitial(std::string const &hdr,     // .f
                                std::string const &key);
            static bool casePartial(std::string const &hdr,     // .f
                                std::string const &key);
            static bool caseFull(std::string const &hdr,        // .f
                                std::string const &key);

            public:
                const_hdr_iterator &operator++();               // opinc.f
                const_hdr_iterator &operator--();               // opdec.f
                const_hdr_iterator operator++(int);             // oppostinc.f

                // not used, but potentially available in consthdropdec.cc
                // const_hdr_iterator operator--(int);

                bool operator==(const_hdr_iterator const &other)    // opeq.f
                                                            const;

                bool operator!=(const_hdr_iterator const &other)    // opneq.f
                                                            const;

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


        using const_reverse_hdr_iterator = 
                        std::reverse_iterator<const_hdr_iterator>;

        explicit MailHeaders(std::istream &in, Mode mode = READ);
        MailHeaders(MailHeaders &&tmp);

        MailHeaders &operator=(MailHeaders &&tmp);

        void read();
        void setHeaderIterator(char const *header, Match match = FULL); // .f

        const_hdr_iterator beginh() const;                      // .f
        const_hdr_iterator endh() const;                        // .f

        const_reverse_hdr_iterator rbeginh() const;             // .f
        const_reverse_hdr_iterator rendh() const;               // .f

        // available from vector<string>:

        size_t size() const;                                    // .f
        std::string const &operator[](size_t idx) const;        // opindex.f

        const_iterator begin() const;                           // .f
        const_iterator end() const;                             // .f

        const_reverse_iterator rbegin() const;                  // .f
        const_reverse_iterator rend() const;                    // .f
};

inline FBB::MailHeaders::const_iterator FBB::MailHeaders::begin() const
{
    return d_lines.begin();
}
inline FBB::MailHeaders::const_hdr_iterator FBB::MailHeaders::beginh() const
{
    return const_hdr_iterator(this, begin());
                                        // returns iterator over all headers
                                        // matching d_hdr by the d_match type
}
inline FBB::MailHeaders::const_iterator FBB::MailHeaders::end() const
{
    return d_lines.end();
}
inline FBB::MailHeaders::const_hdr_iterator FBB::MailHeaders::endh() const
{
                                        // returns address of the sentinel
    return const_hdr_iterator(this, end());
}
inline std::string const &FBB::MailHeaders::operator[](size_t idx) const
{
    return d_lines[idx];
}
inline FBB::MailHeaders::const_reverse_iterator FBB::MailHeaders::rbegin() const
{
    return d_lines.rbegin();
}
inline FBB::MailHeaders::const_reverse_hdr_iterator FBB::MailHeaders::rbeginh() const
{
    return const_reverse_hdr_iterator(endh());
}
inline FBB::MailHeaders::const_reverse_iterator FBB::MailHeaders::rend() const
{
    return d_lines.rend();
}
inline FBB::MailHeaders::const_reverse_hdr_iterator FBB::MailHeaders::rendh() const
{
    return const_reverse_hdr_iterator(beginh());
}
inline void FBB::MailHeaders::setHeaderIterator(char const *header, Match match)
{
    d_hdr = header;
    d_match = match;
}
inline size_t FBB::MailHeaders::size() const
{
    return d_lines.size();
}

inline std::string const *FBB::MailHeaders::const_hdr_iterator::operator->() const
{
    return &*d_current;
}
inline FBB::MailHeaders::const_hdr_iterator
&FBB::MailHeaders::const_hdr_iterator::operator--()
{
    d_current = lookdown(d_current);
    return *this;
}
inline bool FBB::MailHeaders::const_hdr_iterator::operator==(
                                    const_hdr_iterator const &other) const
{
    return d_current == other.d_current;
}
inline FBB::MailHeaders::const_hdr_iterator
&FBB::MailHeaders::const_hdr_iterator::operator++()
{
    d_current = lookup(++d_current);
    return *this;
}
inline bool FBB::MailHeaders::const_hdr_iterator::operator!=(
                                    const_hdr_iterator const &other) const
{
    return d_current != other.d_current;
}
inline FBB::MailHeaders::const_hdr_iterator
FBB::MailHeaders::const_hdr_iterator::operator++(int)
{
    return const_hdr_iterator(d_mh, d_current++);
}
inline std::string const &FBB::MailHeaders::const_hdr_iterator::operator*() const
{
    return *d_current;
}

} // FBB

#endif
