/*
 Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
*/

/*
ver: 2.x.x - build: 2026-04-28
*/

/*
The zlib License

Copyright (c) 2026 SD-Innovation S.A.S.

This software is provided ‘as-is’, without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.

2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.

3. This notice may not be removed or altered from any source
distribution.
*/

#pragma once

#include <bit>
#include <cstdint>
#include <span>
#include <string_view>

namespace sdi_toolBox::common::utils::hash
{
//--------------------------------------------------------------
/**
 * @brief Compute the FNV-1a 32-bit hash for a span of bytes.
 *
 * Primary API: accepts a `std::span<const std::byte>` so callers can pass any
 * contiguous buffer without copies.
 *
 * @param data Span of bytes to hash.
 * @return std::uint32_t 32-bit FNV-1a hash.
 */
constexpr std::uint32_t fnv1a(std::span<const std::byte> data) noexcept;

/**
 * @brief Compute the FNV-1a 32-bit hash for a std::string_view.
 *
 * Convenience overload forwarding to the byte-based implementation.
 *
 * @param sv Input string view.
 * @return std::uint32_t The 32-bit FNV-1a hash.
 */
constexpr std::uint32_t fnv1a(std::string_view sv) noexcept;

/**
 * @brief Compute the FNV-1a 32-bit hash for a trivially copyable POD object.
 *
 * The object is hashed as its raw bytes (binary representation).
 * Use only for POD/trivially-copyable types where this behaviour is intended.
 *
 * @tparam T Trivially copyable type.
 * @param value Reference to the object to hash.
 * @return std::uint32_t 32-bit FNV-1a hash.
 */
template<typename T>
  requires std::is_trivially_copyable_v<T>
constexpr std::uint32_t fnv1a(const T &value) noexcept;

/**
 * @brief Compute the FNV-1a 64-bit hash for a span of bytes.
 *
 * Primary API: accepts a `std::span<const std::byte>` so callers can pass any
 * contiguous buffer without copies.
 *
 * @param data Span of bytes to hash.
 * @return std::uint64_t 64-bit FNV-1a hash.
 */
constexpr std::uint64_t fnv1a_64(std::span<const std::byte> data) noexcept;

/**
 * @brief Compute the FNV-1a 64-bit hash for a std::string_view.
 *
 * Convenience overload forwarding to the byte-based implementation.
 *
 * @param sv Input string view.
 * @return std::uint64_t The 64-bit FNV-1a hash.
 */
constexpr std::uint64_t fnv1a_64(std::string_view sv) noexcept;

/**
 * @brief Compute the FNV-1a 64-bit hash for a trivially copyable POD object.
 *
 * The object is hashed as its raw bytes (binary representation).
 * Use only for POD/trivially-copyable types where this behaviour is intended.
 *
 * @tparam T Trivially copyable type.
 * @param value Reference to the object to hash.
 * @return std::uint64_t 64-bit FNV-1a hash.
 */
template<typename T>
  requires std::is_trivially_copyable_v<T>
constexpr std::uint64_t fnv1a_64(const T &value) noexcept;
//--------------------------------------------------------------

//--------------------------------------------------------------
/* Compute the FNV-1a 32-bit hash for a span of bytes */
inline constexpr std::uint32_t fnv1a(const std::span<const std::byte> data) noexcept
{
  constexpr std::uint32_t FNV_OFFSET_BASIS = 2166136261u;
  constexpr std::uint32_t FNV_PRIME        = 16777619u;

  std::uint32_t hash = FNV_OFFSET_BASIS;
  for (const auto b : data)
  {
    hash ^= static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(b));
    hash *= FNV_PRIME;
  }
  return hash;
}
//--------------------------------------------------------------
/* Compute the FNV-1a 32-bit hash for a std::string_view */
inline constexpr std::uint32_t fnv1a(const std::string_view sv) noexcept
{
  // Avoid reinterpret_cast on pointers: iterate characters (constexpr-friendly)
  constexpr std::uint32_t FNV_OFFSET_BASIS = 2166136261u;
  constexpr std::uint32_t FNV_PRIME        = 16777619u;

  std::uint32_t hash = FNV_OFFSET_BASIS;
  for (char c : sv)
  {
    hash ^= static_cast<std::uint32_t>(static_cast<std::uint8_t>(c));
    hash *= FNV_PRIME;
  }
  return hash;
}
//--------------------------------------------------------------
/* Compute the FNV-1a 32-bit hash for a trivially copyable POD object */
template<typename T>
  requires std::is_trivially_copyable_v<T>
inline constexpr std::uint32_t fnv1a(const T &value) noexcept
{
  // Use std::bit_cast to get bytes in a constexpr-friendly way
  auto bytes = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
  return fnv1a(std::span<const std::byte>(bytes.data(), bytes.size()));
}
//--------------------------------------------------------------
/* Compute the FNV-1a 64-bit hash for a span of bytes */
inline constexpr std::uint64_t fnv1a_64(const std::span<const std::byte> data) noexcept
{
  constexpr std::uint64_t FNV_OFFSET_BASIS = 14695981039346656037ull;
  constexpr std::uint64_t FNV_PRIME        = 1099511628211ull;

  std::uint64_t hash = FNV_OFFSET_BASIS;
  for (const auto b : data)
  {
    hash ^= static_cast<std::uint64_t>(std::to_integer<std::uint8_t>(b));
    hash *= FNV_PRIME;
  }
  return hash;
}
//--------------------------------------------------------------
/* Compute the FNV-1a 64-bit hash for a std::string_view */
inline constexpr std::uint64_t fnv1a_64(const std::string_view sv) noexcept
{
  // Iterate characters to remain constexpr-friendly
  constexpr std::uint64_t FNV_OFFSET_BASIS = 14695981039346656037ull;
  constexpr std::uint64_t FNV_PRIME        = 1099511628211ull;

  std::uint64_t hash = FNV_OFFSET_BASIS;
  for (char c : sv)
  {
    hash ^= static_cast<std::uint64_t>(static_cast<std::uint8_t>(c));
    hash *= FNV_PRIME;
  }
  return hash;
}
//--------------------------------------------------------------
/* Compute the FNV-1a 64-bit hash for a trivially copyable POD object */
template<typename T>
  requires std::is_trivially_copyable_v<T>
inline constexpr std::uint64_t fnv1a_64(const T &value) noexcept
{
  // Use std::bit_cast to get bytes in a constexpr-friendly way
  auto bytes = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
  return fnv1a_64(std::span<const std::byte>(bytes.data(), bytes.size()));
}
//--------------------------------------------------------------
}     // namespace sdi_toolBox::common::utils::hash
