// SPDX-FileComment: This file is part of TNL - Template Numerical Library (https://tnl-project.org/)
// SPDX-License-Identifier: MIT

#pragma once

#include <string>
#include <stdexcept>
#include <type_traits>
#include <vector>
#include <variant>

namespace TNL::Config {

using std::get;
using std::holds_alternative;
using std::monostate;
using std::variant;

// aliases for integer types
using UnsignedInteger = std::size_t;
using Integer = std::make_signed_t< std::size_t >;

using Parameter = variant<
   monostate,
   bool,
   Integer,
   UnsignedInteger,
   double,
   std::string,
   std::vector< bool >,
   std::vector< Integer >,
   std::vector< UnsignedInteger >,
   std::vector< double >,
   std::vector< std::string > >;

template< typename T >
struct ParameterTypeCoercion
{
   using type = std::conditional_t<
      std::is_same_v< T, bool >,
      bool,
      std::conditional_t<
         std::is_integral_v< T > && std::is_signed_v< T >,
         Integer,
         std::conditional_t<
            std::is_integral_v< T > && std::is_unsigned_v< T >,
            UnsignedInteger,
            std::conditional_t<
               std::is_floating_point_v< T >,
               double,
               std::conditional_t<
                  std::is_base_of_v< std::string, T >,
                  std::string,
                  std::conditional_t< std::is_same_v< std::decay_t< T >, const char* >, std::string, T > > > > > >;

   static type
   convert( const T& v )
   {
      return v;
   }
   template< typename Result >
   static Result
   convert_back( const type& v )
   {
      return v;
   }
};

template< typename T >
struct ParameterTypeCoercion< std::vector< T > >
{
   using type = std::vector< typename ParameterTypeCoercion< T >::type >;

   static type
   convert( const std::vector< T >& vec )
   {
      type new_vec;
      for( const auto& value : vec )
         new_vec.push_back( value );
      return new_vec;
   }

   template< typename Result >
   static Result
   convert_back( const type& vec )
   {
      Result new_vec;
      for( const auto& value : vec )
         new_vec.push_back( value );
      return new_vec;
   }
};

template< typename EntryType >
std::string
getUIEntryType()
{
   throw std::logic_error( "getUIEntryType called with unknown type." );
}

template<>
inline std::string
getUIEntryType< bool >()
{
   return "bool";
}
template<>
inline std::string
getUIEntryType< Integer >()
{
   return "integer";
}
template<>
inline std::string
getUIEntryType< UnsignedInteger >()
{
   return "unsigned integer";
}
template<>
inline std::string
getUIEntryType< double >()
{
   return "real";
}
template<>
inline std::string
getUIEntryType< std::string >()
{
   return "string";
}

template<>
inline std::string
getUIEntryType< std::vector< bool > >()
{
   return "list of bool";
}
template<>
inline std::string
getUIEntryType< std::vector< Integer > >()
{
   return "list of integer";
}
template<>
inline std::string
getUIEntryType< std::vector< UnsignedInteger > >()
{
   return "list of unsigned integer";
}
template<>
inline std::string
getUIEntryType< std::vector< double > >()
{
   return "list of real";
}
template<>
inline std::string
getUIEntryType< std::vector< std::string > >()
{
   return "list of string";
}

}  // namespace TNL::Config
