#ifndef INCLUDED_BOBCAT_HASH_
#define INCLUDED_BOBCAT_HASH_

#include <string>
#include <cstring>
#include <unordered_map>

#include <bobcat/string>

namespace FBB
{

// Support structs
// ===============

struct CaseHash
{
    size_t operator()(std::string const &key) const;        // opfun1.f
};

struct CaseEqual
{
    bool operator()(char const *s1, char const *s2) const;  // opfun2.f
    bool operator()(std::string const &s1,                  // opfun3.f
                    std::string const &s2) const;
};

struct CharPtrEqual
{
    bool operator()(char const *s1, char const *s2) const;  // opfun4.f
};

// HashCharPtr: case sensitive char const *keys
// ============================================

template<typename Value>
class HashCharPtr: public std::unordered_map<
                                char const *, Value,
                                std::hash<std::string>,  CharPtrEqual
                             >
{
    using BaseClass =  std::unordered_map<
                                char const *, Value,
                                std::hash<std::string>,  CharPtrEqual
                             >;
    public:
        using value_type =  typename BaseClass::value_type;

        HashCharPtr()                   = default;
        HashCharPtr(HashCharPtr &&tmp);                             // 1.f
        HashCharPtr(std::initializer_list<value_type> iniValues);   // 2.f

        template <typename InputIterator>
        HashCharPtr(InputIterator first, InputIterator beyond);     // 3.f

        HashCharPtr<Value> &operator=(HashCharPtr &&tmp);           // 4.f
};


// HashCharCasePtr: case insensitive char const *keys
// ==================================================

template<typename Value>
class HashCharCasePtr: public std::unordered_map<
                                char const *, Value,
                                CaseHash,     CaseEqual
                             >
{
    using BaseClass =  
                std::unordered_map<char const *, Value, CaseHash, CaseEqual>;
    public:
        using value_type =  typename BaseClass::value_type;

        HashCharCasePtr()                       = default;
        HashCharCasePtr(HashCharCasePtr &&tmp);                         // 1.f
        HashCharCasePtr(std::initializer_list<value_type> iniValues);   // 2.f

        template <typename InputIterator>
        HashCharCasePtr(InputIterator first, InputIterator beyond);     // 3.f

        HashCharCasePtr<Value> &operator=(HashCharCasePtr &&tmp);       // 4.f
};


// HashString: case sensitive std::string keys
// ===========================================

template<typename Value>
class HashString: public std::unordered_map<std::string, Value>
{
    using BaseClass =  std::unordered_map<std::string, Value>;

    public:
        using value_type =  typename BaseClass::value_type;

        HashString()                            = default;
        HashString(HashString &&tmp);                               // 1.f
        HashString(std::initializer_list<value_type> iniValues);    // 2.f

        template <typename InputIterator>
        HashString(InputIterator first, InputIterator beyond);      // 3.f

        HashString<Value> &operator=(HashString &&tmp);             // 4.f
};



// HashStringCase: case insensitive std::string keys
// =================================================

template<typename Value>
class HashStringCase: public std::unordered_map<
                                std::string, Value, CaseHash, CaseEqual
                             >
{
    using BaseClass =  
                std::unordered_map<std::string, Value, CaseHash, CaseEqual>;
    public:
        using value_type =  typename BaseClass::value_type;

        HashStringCase()                        = default;
        HashStringCase(HashStringCase &&tmp);                           // 1.f

        HashStringCase(std::initializer_list<value_type> iniValues);    // 2.f

        template <typename InputIterator>
        HashStringCase(InputIterator first, InputIterator beyond);      // 3.f

        HashStringCase<Value> &operator=(HashStringCase &&tmp);         // 4.f
};

inline size_t CaseHash::operator()(std::string const &key) const
{
    return std::hash<std::string>()(FBB::String::lc(key));
}
inline bool CaseEqual::operator()(char const *s1, char const *s2) const
{
    return strcasecmp(s1, s2) == 0;
}
inline bool CaseEqual::operator()(std::string const &s1,
                                        std::string const &s2) const
{
    return FBB::String::casecmp(s1, s2) == 0;
}
inline bool CharPtrEqual::operator()(char const *s1, char const *s2) const
{
    return strcmp(s1, s2) == 0;
}

template<typename Value>
inline HashCharPtr<Value>::HashCharPtr(HashCharPtr &&tmp)
:
    BaseClass(std::move(tmp))
{}
template<typename Value>
inline HashCharPtr<Value>::HashCharPtr(std::initializer_list<value_type>
                                                                iniValues)
:
    BaseClass(iniValues)
{}
template<typename Value>
template <typename InputIterator>
inline HashCharPtr<Value>::HashCharPtr(InputIterator first,
                                               InputIterator beyond)
:
    BaseClass(first, beyond)
{}
template<typename Value>
inline HashCharPtr<Value> &HashCharPtr<Value>::operator=(HashCharPtr &&tmp)
{
    static_cast<BaseClass &>(*this) = std::move(tmp);
    return *this;
}

template<typename Value>
inline HashCharCasePtr<Value>::HashCharCasePtr(HashCharCasePtr &&tmp)
:
    BaseClass(std::move(tmp))
{}
template<typename Value>
inline HashCharCasePtr<Value>::HashCharCasePtr(
                                std::initializer_list<value_type> iniValues)
:
    BaseClass(iniValues)
{}
template<typename Value>
template <typename InputIterator>
inline HashCharCasePtr<Value>::HashCharCasePtr(InputIterator first,
                                               InputIterator beyond)
:
    BaseClass(first, beyond)
{}
template<typename Value>
inline HashCharCasePtr<Value> &HashCharCasePtr<Value>::operator=(
                                                HashCharCasePtr &&tmp)
{
    static_cast<BaseClass &>(*this) = std::move(tmp);
    return *this;
}

template<typename Value>
inline HashString<Value>::HashString(HashString &&tmp)
:
    BaseClass(std::move(tmp))
{}
template<typename Value>
inline HashString<Value>::HashString(std::initializer_list<value_type>
                                                                iniValues)
:
    BaseClass(iniValues)
{}
template<typename Value>
template <typename InputIterator>
inline HashString<Value>::HashString(InputIterator first,
                                     InputIterator beyond)
:
    BaseClass(first, beyond)
{}
template<typename Value>
inline HashString<Value> &HashString<Value>::operator=(HashString &&tmp)
{
    static_cast<BaseClass &>(*this) = std::move(tmp);
    return *this;
}

template<typename Value>
inline HashStringCase<Value>::HashStringCase(HashStringCase &&tmp)
:
    BaseClass(std::move(tmp))
{}
template<typename Value>
inline HashStringCase<Value>::HashStringCase(std::initializer_list<value_type>
                                                                iniValues)
:
    BaseClass(iniValues)
{}
template<typename Value>
template <typename InputIterator>
inline HashStringCase<Value>::HashStringCase(InputIterator first,
                                             InputIterator beyond)
:
    BaseClass(first, beyond)
{}
template<typename Value>
inline HashStringCase<Value> &HashStringCase<Value>::operator=(
                                                HashStringCase &&tmp)
{
    static_cast<BaseClass &>(*this) = std::move(tmp);
    return *this;
}

} // FBB

#endif
