// Copyright (C) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

#pragma once
#include <algorithm>
#include <functional>
#include <unordered_map>
#include <map>
#include <set>
#include <cctype>

/**
 * @brief provides case-less comparison for stl algorithms
 * @tparam Key type, usually std::string
 */
template<class Key>
class CaselessLess : public std::binary_function<Key, Key, bool> {
 public:
    bool operator () (const Key & a, const Key & b) const noexcept {
        return std::lexicographical_compare(std::begin(a),
                          std::end(a),
                          std::begin(b),
                          std::end(b),
                          [](const char&cha, const char&chb) {
                              return std::tolower(cha) < std::tolower(chb);
                          });
    }
};

/**
 * provides caseless eq for stl algorithms
 * @tparam Key
 */
template<class Key>
class CaselessEq : public std::binary_function<Key, Key, bool> {
 public:
    bool operator () (const Key & a, const Key & b) const noexcept {
        return a.size() == b.size() &&
            std::equal(std::begin(a),
                       std::end(a),
                       std::begin(b),
                       [](const char&cha, const char&chb) {
                           return std::tolower(cha) == std::tolower(chb);
                       });
    }
};

/**
 * To hash caseless
 */
template<class T>
class CaselessHash : public std::hash<T> {
 public:
    size_t operator()(T __val) const noexcept {
          T lc;
          std::transform(std::begin(__val), std::end(__val), std::back_inserter(lc), [](typename T::value_type ch) {
              return std::tolower(ch);
          });
          return std::hash<T>()(lc);
      }
};

template <class Key, class Value>
using caseless_unordered_map = std::unordered_map<Key, Value, CaselessHash<Key>, CaselessEq<Key>>;

template <class Key, class Value>
using caseless_unordered_multimap = std::unordered_multimap<Key, Value, CaselessHash<Key>, CaselessEq<Key>>;

template <class Key, class Value>
using caseless_map = std::map<Key, Value, CaselessLess<Key>>;

template <class Key>
using caseless_set = std::set<Key, CaselessLess<Key>>;