#ifndef INCLUDED_BOBCAT_HASH_
#define INCLUDED_BOBCAT_HASH_

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

#include <bobcat/string>

namespace FBB
{

struct CaseHash
{
    size_t operator()(std::string const &key) const;
};
inline size_t CaseHash::operator()(std::string const &key) const
{
    return std::hash<std::string const &>()(FBB::String::lc(key));
}

struct CaseEqual
{
    bool operator()(char const *s1, char const *s2) const;
    bool operator()(std::string const &s1, std::string const &s2) const;
};
inline bool CaseEqual::operator()(std::string const &s1, 
                                        std::string const &s2) const
{ 
    return FBB::String::casecmp(s1, s2) == 0;
}
inline bool CaseEqual::operator()(char const *s1, char const *s2) const
{ 
    return strcasecmp(s1, s2) == 0;
}


template<typename Value>
class HashStringCase: public std::unordered_map<
                                std::string,    Value, 
                                CaseHash, CaseEqual
                             >
{
    public:
        HashStringCase();   //  = default;

        template <typename InputIterator>
        HashStringCase(InputIterator first, InputIterator beyond);
};

template<typename Value>
inline HashStringCase<Value>::HashStringCase()
{}

template<typename Value>
template <typename InputIterator>
inline HashStringCase<Value>::HashStringCase(InputIterator first, 
                                             InputIterator beyond)
:
    std::unordered_map<
        std::string,    Value, 
        CaseHash,       CaseEqual
    >(first, beyond)
{}



template<typename Value>
class HashString: public std::unordered_map< std::string, Value>
{
    public:
        HashString();   //  = default;

        template <typename InputIterator>
        HashString(InputIterator first, InputIterator beyond);
};

template<typename Value>
inline HashString<Value>::HashString()
{}

template<typename Value>
template <typename InputIterator>
inline HashString<Value>::HashString(InputIterator first, 
                                     InputIterator beyond)
:
    std::unordered_map<std::string, Value>(first, beyond)
{}



template<typename Value>
class HashCharCasePtr: public std::unordered_map<
                                char const *, Value, 
                                CaseHash,     CaseEqual
                             >
{
    public:
        HashCharCasePtr();  //  = default;

        template <typename InputIterator>
        HashCharCasePtr(InputIterator first, InputIterator beyond);
};

template<typename Value>
inline HashCharCasePtr<Value>::HashCharCasePtr()
{}

template<typename Value>
template <typename InputIterator>
inline HashCharCasePtr<Value>::HashCharCasePtr(InputIterator first, 
                                               InputIterator beyond)
:
    std::unordered_map<
        char const *, Value, 
        CaseHash,     CaseEqual
    >(first, beyond)
{}


struct CharPtrEqual
{
    bool operator()(char const *s1, char const *s2) const;
};
inline bool CharPtrEqual::operator()(char const *s1, char const *s2) const
{ 
    return strcmp(s1, s2) == 0;
}


template<typename Value>
class HashCharPtr: public std::unordered_map<
                                char const *, Value, 
                                std::hash<std::string>,  CharPtrEqual
                             >
{
    public:
        HashCharPtr();  //  = default;

        template <typename InputIterator>
        HashCharPtr(InputIterator first, InputIterator beyond);
};

template<typename Value>
inline HashCharPtr<Value>::HashCharPtr()
{}

template<typename Value>
template <typename InputIterator>
inline HashCharPtr<Value>::HashCharPtr(InputIterator first, 
                                               InputIterator beyond)
:
    std::unordered_map<
        char const *,  Value, 
        std::hash<std::string>,   CharPtrEqual
    >(first, beyond)
{}

} // FBB

#endif
