/*
** Copyright 2012-2013 Centreon
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** For more information : contact@centreon.com
*/

#ifndef CC_HASH_HH
#define CC_HASH_HH

#if __cplusplus == 201103L
#define CXX0X_UNORDERED 1
#elif defined(__clang__)
#if __has_feature(cxx_variadic_templates)
#define TR1_UNORDERED 1
#endif  // cxx_variadic_templates.
#elif defined(__GNUC__) && __GNUC__ >= 4
#define TR1_UNORDERED 1
#endif  // C++0x, tr1


// Used c++0x implementation.
#ifdef CXX0X_UNORDERED
#include <functional>

namespace com::centreon {

template <typename T>
std::size_t hash(T const& data) {
  std::hash<T> h;
  return (h(data));
}

}

// Used tr1 implementation.
#elif defined(TR1_UNORDERED)
#include <tr1/functional>

namespace com::centreon {

template <typename T>
std::size_t hash(T const& data) {
  std::tr1::hash<T> h;
  return (h(data));
}

}

// Used own implementation.
#else

namespace com::centreon {

template <typename T>
std::size_t hash(T const& data) {
  std::size_t res(14695981039346656037ULL);
  for (typename T::const_iterator it(data.begin()), end(data.end()); it != end;
       ++it) {
    res ^= static_cast<std::size_t>(*it);
    res *= static_cast<std::size_t>(1099511628211ULL);
  }
  return (res);
}

template <>
inline std::size_t hash<bool>(bool val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<char>(char val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<int>(int val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<long long>(long long val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<long>(long val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<short>(short val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<unsigned char>(unsigned char val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<unsigned int>(unsigned int val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<unsigned long long>(unsigned long long val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<unsigned long>(unsigned long val) {
  return (static_cast<std::size_t>(val));
}

template <>
inline std::size_t hash<unsigned short>(unsigned short val) {
  return (static_cast<std::size_t>(val));
}

}

#endif  // C++0X, tr1 or std

namespace com::centreon {

template <typename T, typename U>
std::size_t hash(std::pair<T, U> const& data) {
  std::size_t id(hash(data.first));
  return (hash(data.second) + 0x9e3779b9 + (id << 6) + (id >> 2));
}

template <typename T>
std::size_t hash(T begin, T end) {
  std::size_t res(0);
  while (begin != end) {
    res ^= hash(*begin) + 0x9e3779b9 + (res << 6) + (res >> 2);
    ++begin;
  }
  return (res);
}

template <typename T>
std::size_t hash_combine(std::size_t& seed, T const& data) {
  seed ^= hash(data) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
  return (seed);
}

template <typename T>
std::size_t hash_combine(std::size_t& seed, T begin, T end) {
  seed ^= hash(begin, end) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
  return (seed);
}

}

#endif  // !CC_HASH_HH
