#ifndef INCLUDED_BOBCAT_READLINEHISTORY_
#define INCLUDED_BOBCAT_READLINEHISTORY_

#include <iterator>
#include <string>
#include <vector>
#include <memory>

#include <readline/history.h>

namespace FBB
{

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

    class HistoryElement                // stores the pointers to the 
    {                                   // relevant elements in HIST_ENTRY
        friend struct ReadLineHistory;  // elements

        char const *d_line;     
        char const *d_timestamp;

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

        private:
            HistoryElement() = default;

            HistoryElement &operator=(HIST_ENTRY const *element);
    };

    using ElementsPtr = HistoryElement *;
    using ElementsConstPtr = HistoryElement const *;

    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;               // idx of a history element

            ElementsConstPtr d_elements;

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

        private:
            const_iterator(HistoryElement const *vect); // last element    .f
            const_iterator(HistoryElement const *vect, size_t idx);     // .f

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

    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

    private:
        bool d_timestamps;              // store time stamps or not
        ElementsPtr d_elements;         // ptr to the HistoryElements

                                        // singleton
        static std::unique_ptr<ReadLineHistory> s_readLineHistory;       

    public:
        ReadLineHistory(ReadLineHistory const &other)           = delete;
        ~ReadLineHistory();

        static ReadLineHistory &instance(bool useTimestamps = false);

        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

                                            // write/read the timestamps at
                                            // insertions/extractions
        ReadLineHistory &setTimestampsIO(bool useTimestamps);       // .f

        size_t size() const;                                        // .f
        size_t maxSize() const;                                     // .f
        bool timestamps() const;                                    // .f


    private:
        ReadLineHistory();

        void destroy();

        std::istream &extract(std::istream &in);
        void setElements();

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

// free functions:
// ===============

inline std::istream &operator>>(std::istream &in, ReadLineHistory &history)
{
    return history.extract(in);
}

// ReadLineHistory:
// ================

inline ReadLineHistory::const_iterator ReadLineHistory::begin() const
{
    return const_iterator(d_elements, 0);
}

inline ReadLineHistory::const_iterator ReadLineHistory::end() const
{
    return const_iterator(d_elements);
}

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:
// ===============

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

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

// const_iterator:
// ===============

inline ReadLineHistory::const_iterator::const_iterator(
                                            ElementsConstPtr elements, 
                                            size_t idx)
:
    d_idx(idx),
    d_elements(elements)
{}

inline ReadLineHistory::const_iterator::const_iterator(
                                        ElementsConstPtr elements) 
:
    d_idx(history_length),
    d_elements(elements)
{}

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_elements, d_idx++);
}

inline ReadLineHistory::const_iterator
    &ReadLineHistory::const_iterator::operator--()
{
    --d_idx;
    return *this;
}

inline bool ReadLineHistory::const_iterator::operator!=(
                                            const_iterator const &rhs) const
{
    return not (*this == rhs);
}


} // FBB
#endif
