#ifndef INCLUDED_BOBCAT_LINEARMAP_
#define INCLUDED_BOBCAT_LINEARMAP_

#include <vector>
#include <algorithm>
#include <stdexcept>
#include <initializer_list>

namespace FBB
{

template <typename Key, typename Value>
class LinearMap: private std::vector<std::pair<Key, Value>>
{
        using Base =  std::vector<std::pair<Key, Value>>;
        using LinMap =  LinearMap<Key, Value>;
        using ConstIterPair =  std::pair<
                    typename LinearMap<Key, Value>::const_iterator,
                    typename LinearMap<Key, Value>::const_iterator
                >;
        using IterPair = std::pair<
                    typename LinearMap<Key, Value>::iterator,
                    typename LinearMap<Key, Value>::iterator
                >;

    public:
        using value_type =  std::pair<Key, Value>;
        using iterator =  typename Base::iterator;
        using const_iterator =  typename Base::const_iterator;
        using reverse_iterator =  typename Base::reverse_iterator;
        using const_reverse_iterator =  typename Base::const_reverse_iterator;

        LinearMap()                     = default;

        template <typename Iterator>
        LinearMap(Iterator begin, Iterator end);

        LinearMap(std::initializer_list<value_type> initial);

        Value &operator[](Key const &key);

        Value &at(Key const &key);

        using Base::begin;
        using Base::capacity;
        using Base::cbegin;
        using Base::cend;
        using Base::clear;

        size_t count(Key const &key) const;

        using Base::crbegin;
        using Base::crend;

        using Base::emplace;
        using Base::empty;
        using Base::end;

        IterPair        equal_range(Key const &key);
        ConstIterPair   equal_range(Key const &key) const;

        bool erase(Key const &key);
        void erase(iterator iter);
        void erase(iterator begin, iterator end);

        iterator find(Key const &key);
        const_iterator find(Key const &key) const;

        using Base::get_allocator;

        std::pair<iterator, bool> insert(value_type const &keyvalue);
        iterator insert(iterator pos, value_type const &keyValue);
        template <typename Iterator>
        void insert(Iterator begin, Iterator end);

        iterator lower_bound(Key const &key);
        const_iterator lower_bound(Key const &key) const;

        using Base::max_size;
        using Base::rbegin;
        using Base::rend;
        using Base::reserve;
        using Base::size;
        using Base::swap;

        iterator upper_bound(Key const &key);
        const_iterator upper_bound(Key const &key) const;

    private:
        value_type *findPtr(Key const &key) const;
};

// size_t count(Key const &key) const

template <typename Key, typename Value>
inline size_t LinearMap<Key, Value>::count(Key const &key) const
{
    return find(key) != end();
}
// std::pair<iterator, iterator> equal_range(Key const &key)

template <typename Key, typename Value>
inline typename LinearMap<Key, Value>::IterPair
        LinearMap<Key, Value>::equal_range(Key const &key)
{
    iterator iter = lower_bound(key);
    return IterPair(iter, iter);
}
// bool erase(Key const &key)
//
template <typename Key, typename Value>
bool LinearMap<Key, Value>::erase(Key const &key)
{
    auto iter = find(key);
    if (iter == end())
        return false;

    erase(iter);
    return true;
}
// bool erase(Key const &key)
//
template <typename Key, typename Value>
void LinearMap<Key, Value>::erase(iterator iter)
{
    Base::erase(iter);
}
// bool erase(Key const &key)
//
template <typename Key, typename Value>
void LinearMap<Key, Value>::erase(iterator begin, iterator end)
{
    Base::erase(begin, end);
}
// iterator find(Key const &key)

template <typename Key, typename Value>
inline typename LinearMap<Key, Value>::iterator
        LinearMap<Key, Value>:: find(Key const &key)
{
    return iterator(findPtr(key));
}
// const_iterator find(Key const &key) const

template <typename Key, typename Value>
inline typename LinearMap<Key, Value>::const_iterator
        LinearMap<Key, Value>::find(Key const &key) const
{
    return const_iterator(findPtr(key));
}
// value_type *findPtr(Key const &key) const

template <typename Key, typename Value>
inline typename LinearMap<Key, Value>::value_type *
        LinearMap<Key, Value>:: findPtr(Key const &key) const
{
    value_type *ptr = const_cast<value_type *>(&*begin());

    return std::find_if(ptr, ptr + size(),
        [&](value_type const &value)
        {
            return key == value.first;
        }
    );
}
//     pair<iterator, bool> insert(value_type const keyvalue);

template <typename Key, typename Value>
std::pair<typename LinearMap<Key, Value>::iterator, bool>
    LinearMap<Key, Value>::insert(value_type const &keyValue)
{
    bool inserted;

    auto iter = find(keyValue.first);
    if (iter != end())
        inserted = false;
    else
    {
        Base::push_back(keyValue);
        iter = iterator(&Base::back());
        inserted = true;
    }

    return std::pair<iterator, bool>(iter, inserted);
}
//  iterator insert(iterator pos, value_type const &keyValue);

template <typename Key, typename Value>
typename LinearMap<Key, Value>::iterator
    LinearMap<Key, Value>::insert(iterator pos,
              value_type const &keyvalue)
{
    auto iter = find(keyvalue.first);
    if (iter == end())
    {
        push_back(keyvalue);
        iter = iterator(&Base::back());
    }
    return iter;
}
//  iterator insert(iterator pos, value_type const &keyValue);

template <typename Key, typename Value>
template <typename Iterator>
inline void LinearMap<Key, Value>::insert(Iterator begin, Iterator end)
{
    for (; begin != end; ++begin)
        insert(*begin);
}
// LinearMap(Iterator begin, Iterator end)

template <typename Key, typename Value>
template <typename Iterator>
inline LinearMap<Key, Value>::
    LinearMap(Iterator begin, Iterator end)
{
    insert(begin, end);
}
// LinearMap(initializer_list<value_type> initial)

template <typename Key, typename Value>
inline LinearMap<Key, Value>::
    LinearMap(std::initializer_list<value_type> initial)
{
    for (auto &value: initial)
        insert(value);
}
// iterator lower_bound(Key const &key)

template <typename Key, typename Value>
inline typename LinearMap<Key, Value>::iterator
        LinearMap<Key, Value>:: lower_bound(Key const &key)
{
    return iterator(findPtr(key));
}
// const_iterator lower_bound(Key const &key) const

template <typename Key, typename Value>
inline typename LinearMap<Key, Value>::const_iterator
        LinearMap<Key, Value>::lower_bound(Key const &key) const
{
    return const_iterator(findPtr(key));
}
//Value &operator[])Key const &key)

template <typename Key, typename Value>
        Value &LinearMap<Key, Value>::operator[](Key const &key)
{
    auto iter = find(key);
    if (iter != end())
        return iter->second;

    Base::push_back(value_type(key, Value()));
    return Base::back().second;
}
// iterator upper_bound(Key const &key)

template <typename Key, typename Value>
inline typename LinearMap<Key, Value>::iterator
        LinearMap<Key, Value>:: upper_bound(Key const &key)
{
    return iterator(findPtr(key));
}
// const_iterator upper_bound(Key const &key) const

template <typename Key, typename Value>
inline typename LinearMap<Key, Value>::const_iterator
        LinearMap<Key, Value>::upper_bound(Key const &key) const
{
    return const_iterator(findPtr(key));
}

} // FBB
#endif
