#ifndef INCLUDED_BOBCAT_READLINEHISTORY_
#define INCLUDED_BOBCAT_READLINEHISTORY_

#include <iterator>
#include <string>

#include <readline/history.h>

namespace FBB
{

class ReadLineHistory;

class ReadLineHistory
{
    friend std::ostream &operator<<(std::ostream &out, 
                                    ReadLineHistory const &history);
    friend std::istream &operator>>(std::istream &out, 
                                    ReadLineHistory &history);

    static ReadLineHistory s_readLineHistory;
    bool d_timestamps;

    public:
        class HistoryElement
        {
            friend ReadLineHistory;

            char const *d_line;
            char const *d_timestamp;

            public:
                char const *line() const;                               // .f
                char const *timestamp() const;                          // .f

            private:
                HistoryElement();                                       // 1.f
                HistoryElement const &set(HIST_ENTRY const *element);
        };

        struct const_iterator
        {
            using iterator_category = std::input_iterator_tag;
            using difference_type   = std::ptrdiff_t;
            using value_type        = HistoryElement const;
            using pointer           = value_type *;
            using reference         = value_type &;

            private:
                friend ReadLineHistory;
                friend std::reverse_iterator<const_iterator>;
    
                size_t d_idx;
                mutable HistoryElement d_element;

            public:
                const_iterator &operator++();                   //     opinc.f
                const_iterator operator++(int);                 // opincpost.f
                bool operator==(const_iterator const &rhs) const;   //  opeq.f
                bool operator!=(const_iterator const &rhs) const;   // opneq.f
                HistoryElement const &operator*() const;
                HistoryElement const *operator->() const;       //   oparrow.f

            private:
                const_iterator();                   // last element        1.f
                const_iterator(size_t idx);         //                     2.f

                                                // for the reverse iter. 
                const_iterator &operator--();                       // opdec.f
                const_iterator operator--(int);                 // opdecpost.f
        };

        typedef std::reverse_iterator<const_iterator> 
                const_reverse_iterator;

        ReadLineHistory(ReadLineHistory const &other)           = delete;
        ReadLineHistory &operator=(ReadLineHistory const &rhs)  = delete;

        const_iterator begin() const;       // begin of the history    .f
        const_iterator end() const;         // end of the history      .f
        const_reverse_iterator rbegin() const;                      // .f    
        const_reverse_iterator rend() const;                        // .f

        ReadLineHistory &setTimestampsIO(bool useTimestamps);       // .f
        size_t size() const;                                        // .f
        size_t maxSize() const;                                     // .f
        bool timestamps() const;                                    // .f

        static ReadLineHistory &instance();                         // 1.f
        static ReadLineHistory &instance(bool useTimestamps);       // 2.f

    private:
        ReadLineHistory();                                          // .f

        static void insertHistoryElement(HistoryElement const &he,
                                         std::ostream &out);
        static void insertLine(HistoryElement const &he, std::ostream &out);

        static std::istream &extractTimestamps(std::istream &in);
        static std::istream &extractLines(std::istream &in);
};

inline ReadLineHistory::ReadLineHistory()
{}

inline ReadLineHistory::const_iterator ReadLineHistory::begin() const
{
    return const_iterator(0);
}
inline ReadLineHistory::const_iterator ReadLineHistory::end() const
{
    return const_iterator();
}
inline ReadLineHistory &ReadLineHistory::instance()
{
    return s_readLineHistory;
}
inline ReadLineHistory &ReadLineHistory::instance(bool useTimestamps)
{
    return s_readLineHistory.setTimestampsIO(useTimestamps);
}
inline size_t ReadLineHistory::maxSize() const
{
    return history_max_entries;
}
inline ReadLineHistory::const_reverse_iterator ReadLineHistory::rbegin() const
{
    return const_reverse_iterator(end());
}
inline ReadLineHistory::const_reverse_iterator ReadLineHistory::rend() const
{
    return const_reverse_iterator(begin());
}
inline ReadLineHistory &ReadLineHistory::setTimestampsIO(bool useTimestamps)
{
    d_timestamps = useTimestamps;
    return *this;
}
inline size_t ReadLineHistory::size() const
{
    return history_length;
}
inline bool ReadLineHistory::timestamps() const
{
    return d_timestamps;
}

//  ======= HistoryElement members ============

inline ReadLineHistory::HistoryElement::HistoryElement()
:
    d_line(0),
    d_timestamp(0)
{}

inline char const *ReadLineHistory::HistoryElement::line() const
{
    return d_line;
}
inline char const *ReadLineHistory::HistoryElement::timestamp() const
{
    return d_timestamp;
}

// ======== const_iterator members

inline ReadLineHistory::const_iterator::const_iterator()
:
    d_idx(history_length)
{}
inline ReadLineHistory::const_iterator::const_iterator(size_t idx)
:
    d_idx(idx)
{}

inline ReadLineHistory::HistoryElement const 
    *ReadLineHistory::const_iterator::operator->() const
{
    return &operator*();
}
inline bool ReadLineHistory::const_iterator::operator==(
                                            const_iterator const &rhs) const
{
    return d_idx == rhs.d_idx;
}
inline ReadLineHistory::const_iterator 
    &ReadLineHistory::const_iterator::operator++()
{
    ++d_idx;
    return *this;
}
inline ReadLineHistory::const_iterator 
    ReadLineHistory::const_iterator::operator++(int)
{
    return const_iterator(d_idx++);
}
inline bool ReadLineHistory::const_iterator::operator!=(
                                            const_iterator const &rhs) const
{
    return not (*this == rhs);
}

} // FBB        
#endif
