//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org> 
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
#ifndef cwNumericConvert_H
#define cwNumericConvert_H

namespace cw
{
  /*
  template< typename T >
    T minimum_value() { return 0; }

  template <> inline char  minimum_value<char>(){  return 0; }
  template <> inline int8_t  minimum_value<int8_t>(){  return INT8_MIN; }
  template <> inline int16_t minimum_value<int16_t>(){ return INT16_MIN; }
  template <> inline int32_t minimum_value<int32_t>(){ return INT32_MIN; }
  template <> inline int64_t minimum_value<int64_t>(){ return INT64_MIN; }
  template <> inline float   minimum_value<float>(){  return FLT_MIN; }
  template <> inline double  minimum_value<double>(){ return DBL_MIN; }

  template< typename T >
    T maximum_value() { cwAssert(0); }

  template <> inline char     maximum_value<char>(){  return 255; }
  template <> inline int8_t   maximum_value<int8_t>(){  return INT8_MAX; }
  template <> inline int16_t  maximum_value<int16_t>(){ return INT16_MAX; }
  template <> inline int32_t  maximum_value<int32_t>(){ return INT32_MAX; }
  template <> inline int64_t  maximum_value<int64_t>(){ return INT64_MAX; }
  template <> inline uint8_t  maximum_value<uint8_t>(){  return UINT8_MAX; }
  template <> inline uint16_t maximum_value<uint16_t>(){ return UINT16_MAX; }
  template <> inline uint32_t maximum_value<uint32_t>(){ return UINT32_MAX; }
  template <> inline uint64_t maximum_value<uint64_t>(){ return UINT64_MAX; }
  template <> inline bool     maximum_value<bool>(){ std::numeric_limits<bool>::max(); }
  template <> inline float    maximum_value<float>(){  return FLT_MAX; }
  template <> inline double   maximum_value<double>(){ return DBL_MAX; }
  */
    
    
  template< typename SRC_t, typename DST_t >
    rc_t numeric_convert( const SRC_t& src,  DST_t& dst )
  {
    // TODO: there is probably a way of using type_traits to make a more efficient comparison
    //       and avoid the double conversion
    double d_min = 0; // std::numeric_limits<DST_t>::min() return smallest positive number which then fails this test when 'src' is zero.
    double d_max = std::numeric_limits<DST_t>::max();
    if( (double)src <= d_max )
      dst = src;
    else
      return cwLogError(kInvalidArgRC,"Numeric conversion failed. The source value is outside the range of the destination value. min:%f max:%f src:%f",d_min,d_max,(double)src );
   

    return kOkRC;
  }
  
  template< typename SRC_t, typename DST_t >
    rc_t numeric_convert2( const SRC_t& src,  DST_t& dst,  const DST_t& minv,  const DST_t& maxv )
  {
    if( sizeof(SRC_t) < sizeof(DST_t) )
    {
      dst = src;
    }
    else
    {
      if( minv <= src && src <= maxv )
        dst = src;
      else
      {
        return cwLogError(kInvalidArgRC,"Numeric conversion failed. The source value is outside the range of the destination value." );
      }
    }

    return kOkRC;
  }


  
  template< typename T >
    rc_t string_to_number( const char* s, T& valueRef )
  {
    if( s == nullptr )
      valueRef = 0;  // BUG BUG BUG why is this not an error ????
    else
    {
      int base = 10;
      errno = 0;

      if( strlen(s) >= 2 && s[0]=='0' && s[1]=='x' )
        base = 16;
      
      long v = strtol(s,nullptr,base);
      if( v == 0 && errno != 0)
        return cwLogError(kOpFailRC,"String to number conversion failed on '%s'.", cwStringNullGuard(s));
      
      return numeric_convert(v,valueRef);
      
    }
    return kOkRC;
  }

  template < > inline
    rc_t string_to_number<double>( const char* s, double& valueRef )
  {
    if( s == nullptr )
      valueRef = 0;    // BUG BUG BUG why is this not an error ????
    else
    {
      errno = 0;
      valueRef = strtod(s,nullptr);
      if( valueRef == 0 && errno != 0)
        return cwLogError(kOpFailRC,"String to number conversion failed on '%s'.", cwStringNullGuard(s));
            
    }
    return kOkRC;
  }

  
  template < > inline
    rc_t string_to_number<float>( const char* s, float& valueRef )
  {
    double d;
    rc_t rc;
    if((rc = string_to_number<double>(s,d)) != kOkRC )
      return rc;

    return numeric_convert(d,valueRef);
  }

  template < > inline
    rc_t string_to_number<bool>( const char* s, bool& valueRef )
  {
    s = nextNonWhiteChar(s);
    
    if( s == nullptr )
      valueRef = false;  // BUG BUG BUG why is this not an error ????
    else
    {
      if( strncmp( "true", s, 4) == 0 )
        valueRef = true;
      else
        if( strncmp( "false", s, 5) == 0 )
          valueRef = false;
        else
          return cwLogError(kOpFailRC,"String to number conversion failed on '%s'.", cwStringNullGuard(s));
    }
    return kOkRC;
  }

  template< typename T >
    int number_to_string( const T& v, char* buf, int bufN, const char* fmt=nullptr  )
  { return snprintf(buf,bufN,fmt,v); }

  template < > inline
  int number_to_string( const int&      v, char* buf, int bufN, const char* fmt ) { return snprintf(buf,bufN,fmt==nullptr ? "%i" : fmt, v);  }
  
  template < > inline
  int number_to_string( const unsigned& v, char* buf, int bufN, const char* fmt ) { return snprintf(buf,bufN,fmt==nullptr ? "%i" : fmt, v);  }
  
  template < > inline
  int number_to_string( const double&   v, char* buf, int bufN, const char* fmt ) { return snprintf(buf,bufN,fmt==nullptr ? "%f" : fmt, v);  }

  rc_t numericConvertTest( const test::test_args_t& args );

  
}
#endif




