#pragma once
#include <ranges>
#include <array>
#include <concepts>
#include <string_view>
#include <optional>

//Enables an easy way to get an index and value with using a for( auto [type,idx] 
//Example:
// static constexpr auto MODES = Aftr::array_of<char const *const>( "Freerun", "Eth Trig On Click", "Fixed Rate");
//for( auto const& [i, str] : Aftr::enumerate( Acquire_Settings::MODES ) )
//{
//   if( ImGui::RadioButton( str, &this->acquisition_info.current_mode, i ) )
//   {
//      this->acquisition_info.curModeState = nullptr; //a radio button was clicked, destroy the previous curModeState
//      fmt::print( "Configuring Open Camera for {:s} {:d}, currmode is {:d}\n", str, i, this->acquisition_info.current_mode );
//      if( std::string_view{ "Freerun" } == std::string_view{ str } )
//      {
//         if( auto r = Aftr::GCam::CamConfigMode_Freerun::New( this->camID ); r )
//            this->acquisition_info.curModeState = std::move( *r );
//         else
//            log.AddLog( "ERROR: Freerun mode *NOT SET*!!!\n%s\n\n", r.error().str.c_str() );
//      }

namespace Aftr
{
   //https://brevzin.github.io/c++/2022/12/05/enumerate/
   //Returns [idx, val], ie: for( auto auto const& [idx, val] : list )... idx is size_t zero based index and val is the value from list at idx
   struct Enumerate : std::ranges::range_adaptor_closure<Enumerate>
   {
      template <std::ranges::viewable_range R>
      constexpr auto operator()( R&& r ) const {
         if constexpr( std::ranges::sized_range<R> ) {
            auto d = std::ranges::distance( r );
            return std::views::zip( std::views::iota( 0, d ), (R&&)r );
         }
         else { return std::views::zip( std::views::iota( 0 ), (R&&)r ); }
      }
   };
   inline constexpr Enumerate enumerate; //lets one do for( auto const& [idx, val] : container )
   
   /// Given a string, returns the index i corresponding to that string within the container.
   /// Compile time error if string isn't found -- compiler enforces string is found.
   template< typename CONST_EXPR_CONT >
   consteval int str_to_idx( char const* const s, CONST_EXPR_CONT&& cont )
   {
      for( int i = 0; i < cont.size(); ++i )
         if( std::string_view{ s } == cont[i] )
            return i;
   }

   /// Given a string, returns the index i corresponding to that string within the container.
   /// Compile time error if string isn't found -- compiler enforces string is found.
   template< typename CONST_EXPR_CONT >
   consteval int str_to_idx( std::string_view s, CONST_EXPR_CONT&& cont )
   {
      for( int i = 0; i < cont.size(); ++i )
         if( s == cont[i] )
            return i;
   }


   /// Given a string, returns the index i corresponding to that string within the container.
   /// This version can run at compile time or run time. When run at runtime, the possibility
   /// exists that the user passed input string_view doesn't exist, in which case an empty
   /// optional is returned. For this version, the compiler cannot enforce that the string
   /// is found. This function has a runtime cost of O(n).
   template< typename CONST_EXPR_CONT >
   constexpr std::optional<int> str_to_idx_opt( std::string_view s, CONST_EXPR_CONT&& cont )
   {
      for( int i = 0; i < cont.size(); ++i )
         if( s == cont[i] )
            return i;
      return std::nullopt;
   }

   /// Given a string, returns a string_view that internally points to the static string
   /// data inside the in passed container. This ensures the returned string_view 
   /// does not dangle since the storage inside the container is static and lasts the
   /// lifetime of the program (or at least long enough for the user to use it).
   /// Compile time error if string isn't found -- compiler enforces string is found.
   template< typename CONST_EXPR_CONT >
   consteval std::string_view contains_str( std::string_view s, CONST_EXPR_CONT&& cont )
   {
      for( int i = 0; i < cont.size(); ++i )
         if( s == cont[i] )
            return std::string_view{ cont[i] };
   }

   /// Given a string, returns a string_view that internally points to the static string
   /// data inside the in passed container. This ensures the returned string_view 
   /// does not dangle since the storage inside the container is static and lasts the
   /// lifetime of the program (or at least long enough for the user to use it).
   /// This version can run at compile time or run time. When run at runtime, the possibility
   /// exists that the user passed input string_view doesn't exist, in which case an empty
   /// optional is returned. For this version, the compiler cannot enforce that the string
   /// is found. This function has a runtime cost of O(n).
   template< typename CONST_EXPR_CONT >
   constexpr std::optional< std::string_view > contains_str_opt( std::string_view s, CONST_EXPR_CONT&& cont )
   {
      for( int i = 0; i < cont.size(); ++i )
         if( s == cont[i] )
            return cont[i];
      return std::nullopt;
   }

   /// Given a string, returns a string_view that internally points to the static string
   /// data inside the in passed container. This ensures the returned string_view 
   /// does not dangle since the storage inside the container is static and lasts the
   /// lifetime of the program (or at least long enough for the user to use it).
   /// This version can run at compile time or run time. When run at runtime, the possibility
   /// exists that the user passed input string_view doesn't exist, in which case an empty
   /// optional is returned. For this version, the compiler cannot enforce that the string
   /// is found. This function has a runtime cost of O(n).
   template< typename CONST_EXPR_CONT >
   constexpr std::optional< std::string_view > contains_str_opt( char const* s, CONST_EXPR_CONT&& cont )
   {
      for( int i = 0; i < cont.size(); ++i )
         if( std::string_view{ s } == cont[i] )
            return cont[i];
      return std::nullopt;
   }


   //SLN Helper that avoids the need to specify the explicit size
   //of the std::array when adding more mappings into the list
   template <typename V, typename... T>
   constexpr auto array_of( T&&... t ) -> std::array< V, sizeof...(T) >
   {
      return { { std::forward<T>( t )... } };
   }



}
