#pragma once
#include <string>
#include <string_view>
#include <optional>
#include <cstdint>
#include <source_location>
#include <ranges>
#include <algorithm>

namespace Aftr
{
   //Common helpful string functions. Convert floats, doubles, ints to strings and vice versa. And
   //do it *fast*. Also parse or split strings.
  
   /// converts an integer to a string, i.e. 1 -> "1", 346 -> "346" ...
   std::string toString( std::int16_t x );
   std::string toString( std::int32_t x );
   std::string toString( std::int64_t x );
   std::string toString( std::uint16_t x );
   std::string toString( std::uint32_t x );
   std::string toString( std::uint64_t x );
   std::string toString( float x );
   std::string toString( double x );
   std::string toString( long double x );

   // Converts a string_view into a the corresponding numeric value. If the string
   // does not contain a valid numeric value, the returned optional is std::nullopt;
   // otherwise the optional will contain the requested type with the corresponding
   // value.
   std::optional<std::int8_t>   toByte( std::string_view v )        noexcept;
   std::optional<std::uint8_t>  toUByte( std::string_view v )       noexcept;
   std::optional<std::int16_t>  toShort( std::string_view v )       noexcept;
   std::optional<std::uint16_t> toUShort( std::string_view v )      noexcept;
   std::optional<std::int32_t>  toInt( std::string_view v )         noexcept;
   std::optional<std::uint32_t> toUInt( std::string_view v )         noexcept;
   std::optional<int64_t>       toInt64( std::string_view v )       noexcept;
   std::optional<uint64_t>      toUInt64( std::string_view v )      noexcept;
   std::optional<float>         toFloat( std::string_view v )       noexcept;
   std::optional<double>        toDouble( std::string_view v )      noexcept;
   std::optional<long double>   toLongDouble( std::string_view v )  noexcept;
   std::optional<unsigned int>  toUnsignedInt( std::string_view v ) noexcept;


   // "" -> false
   // "0" -> false;
   // "false ", " FALSE ", " fAlSe", etc (case insensitive, ignore whitespace) -> false
   // "  false true " -> false because false was found first.
   // everything else returns true
   std::optional<bool>         toBool( std::string_view v )        noexcept; ///< Returns false if x is "0" or "false" (case insenstive); true, otherwise



   /// Hashing functions that are common across all AftrBurner uses (std::hash varies between compilers).
   /// These hash functions are based on the FNV1a hash algorithm (it is in boost)
   /// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1
   /// https://www.boost.org/doc/libs/1_81_0/libs/unordered/examples/fnv1.hpp
   /// 
   /// However, Nykl implemented them again here to make them CONSTEXPR (compile time) so strings can
   /// be hashed to integers at compile time. This is useful for things like NetMsgs that
   /// can be registered earlier. As of C++23 ish, this is easier to consteval a const char* than a string
   consteval unsigned int hash_str_fnv1a_consteval_32_bit( std::string_view str )
   {
      // For 32 bit machines:
      const std::uint64_t fnv_prime = 16777619u;
      const std::uint64_t fnv_offset_basis = 2166136261u;

      // For 64 bit machines:
      // const std::size_t fnv_prime = 1099511628211u;
      // const std::size_t fnv_offset_basis = 14695981039346656037u;

      // For 128 bit machines:
      // const std::size_t fnv_prime = 309485009821345068724781401u;
      // const std::size_t fnv_offset_basis =
      //     275519064689413815358837431229664493455u;

      // For 256 bit machines:
      // const std::size_t fnv_prime =
      //     374144419156711147060143317175368453031918731002211u;
      // const std::size_t fnv_offset_basis =
      //     100029257958052580907070968620625704837092796014241193945225284501741471925557u;

      std::uint64_t hash = fnv_offset_basis;
      for( auto c : str )
      {
         hash ^= c;
         hash *= fnv_prime;
      }
      uint32_t h = static_cast<std::uint32_t>(hash); //convert the 64 bit operations back to 32 bit, truncating anything in the upper 32-bits
      return h;
   }

   /// This constexpr function will return the name of the class, as a string_view, which invoked this.
   /// function.
   constexpr std::string toString_ClassType( std::source_location const& loc )
   {
      using namespace std::string_view_literals;
      auto func_name = std::string_view{ loc.function_name() };
      auto groups = std::views::split( func_name, "::"sv ); //Output will be something like ClassName::getClassName()
      auto const numGroups = std::ranges::distance( groups );
      if( numGroups < 2 )
         return std::string{ std::string_view{ *groups.begin() } }; // class_name::mem_fn

      auto subrange = func_name
         | std::views::split( "::"sv )
         | std::views::drop( numGroups - 2 )
         | std::views::take( 1 )
         //| std::ranges::to<std::string>()
         ;
      //auto s = subrange; //if std::ranges::to<std::string>() worked, we could just return subrange, C++26, maybe?

      auto name_len = std::ranges::distance( subrange.front() );
      std::string s( name_len, '\0' );
      std::ranges::copy( subrange.front(), s.begin() );

      //fmt::print( "ERROR: {:s}: Must call from a class, see WO.h/.cpp WO::getClassName(...) for example...\n", AFTR_FILE_LINE_STR );
      return s;
   }


} //namespace Aftr