//**************************************************************************************************
//                                          UnitsBase.cpp                                          *
//                                         ---------------                                         *
// Started     : 2014-03-08                                                                        *
// Last Update : 2018-11-04                                                                        *
// Copyright   : (C) 2014-2018 MSWaters                                                            *
//**************************************************************************************************

//**************************************************************************************************
//                                                                                                 *
//      This program is free software; you can redistribute it and/or modify it under the          *
//      terms of the GNU General Public License as published by the Free Software Foundation;      *
//      either version 3 of the License, or (at your option) any later version.                    *
//                                                                                                 *
//**************************************************************************************************

#include "UnitsBase.hpp"

//**************************************************************************************************
// Constructor.

UnitsBase::UnitsBase( void )
{
}

//**************************************************************************************************
// Destructor.

UnitsBase::~UnitsBase( )
{
}

//**************************************************************************************************
// Combine a value with it's units to produce the equivalent long value.
//
// Argument List :
//   dfValue - The float value
//
// Return Values :
//   Success - The value combined with the units as a long
//   Failure - NOVAL_LONG

long  UnitsBase::liGetValue( double dfValue )
{
  double  df1;
  long    li1;

  df1 = dfGetValue( dfValue );
  if( df1 == NOVAL_DBL )                              return( NOVAL_LNG );

  if( df1<(double)-LONG_MAX || df1>(double)LONG_MAX ) return( NOVAL_LNG );
  li1 = lround( df1 );

  return( li1 );
}

//**************************************************************************************************
// Combine a value with it's units to produce a single value.
//
// Argument List :
//   dfValue  - The float value
//
// Return Values :
//   Success - The value combined with the units as a double
//   Failure - NOVAL_DBL

double  UnitsBase::dfGetValue( double dfValue )
{
  wxString  os1;
  double    df1;
  int       i1;

  if( ! bIsCreated( ) ) return( NOVAL_DBL );
  if( dfValue == 0.0 )  return( 0.0 );

  i1 = iGetUnits( );
  if( i1 == NOVAL_INT ) return( NOVAL_DBL );
  df1 = dfValue;
  if( i1 != 0 ) df1 *= EXP10( (double) i1 );

  return( df1 );
}

//**************************************************************************************************
// Combine a value with it's units to produce the equivalent string value.
//
// Argument List :
//   dfValue - The float value
//   iRes    - The resolution (ie. how many digits after the decimal point)
//
// Return Values :
//   Success - The string containing the engineering format
//   Failure - An empty string

const wxString & UnitsBase::rosGetValue( double dfValue, int iRes )
{
  static  wxString  osValue;
          wxString  osFmt, os1;
          wxChar    oc1;
          int       i1;

  osValue.Empty( );

  if( ! bIsCreated( ) )                  return( osValue );

  // Create the format string and convert fValue to a string
  osFmt = wxString::Format( wxT("%%#.%if"), iRes );
  if( os1.Printf( osFmt, dfValue ) < 0 ) return( osValue );
  osValue = os1;

  // Add the units to the float value
  os1 = rosGetUnits( );
  if( ! os1.IsEmpty( ) )
  {
    oc1 = os1.GetChar( 0 );
    os1.MakeLower( );

    if(      os1.StartsWith( wxT("micro") ) )   osValue << wxT('u');
    else if( os1.StartsWith( wxT("x 1") ) )
    {
      i1 = iGetUnits( );
      if( CnvtType::bUnitExpToPfx( i1, &oc1 ) ) osValue << oc1;
    }
    else if( os1.StartsWith( wxT("metre") ) ) ; // Do nothing
    else
    {
      if( CnvtType::bUnitPfxToExp( oc1, &i1 ) ) osValue << oc1;
      if( oc1 == wxT('M') )                     osValue << wxT("EG");
    }
  }

  return( osValue );
}

//**************************************************************************************************
// Return the units string associated with a units enumerated type specifier.
//
// (Eg. eUNITS_RES returns "Ohm".)
//
// Argument List :
//   eUType   - the units enumerated type
//   rosUnits - the units string
//
// Return Values :
//   true  - Success
//   false - Failure

bool  UnitsBase::bGetUnitsType( eTypeUnits eUType, wxString & rosUnits )
{
  switch( eUType )
  {
    case eUNITS_CAP  :   // Capacitance
      rosUnits = wxT("F");
      break;

    case eUNITS_IND  :   // Inductance
      rosUnits = wxT("H");
      break;

    case eUNITS_RES  :   // Resistance
      rosUnits = wxT("Ohm");
      break;

    case eUNITS_COND :  // Conductance
      rosUnits = wxT("S");
      break;

    case eUNITS_VOLT :  // Voltage
      rosUnits = wxT("V");
      break;

    case eUNITS_CURR :  // Current
      rosUnits = wxT("A");
      break;

    case eUNITS_TIME :  // Time
      rosUnits = wxT("Sec");
      break;

    case eUNITS_FREQ :  // Frequency
      rosUnits = wxT("Hz");
      break;

    case eUNITS_CHRG :  // Charge
      rosUnits = wxT("C");
      break;

    case eUNITS_PHAD :  // Phase / angle in Degree
      rosUnits = wxT("Degree");
      break;

    case eUNITS_PHAR :  // Phase / angle in Radian
      rosUnits = wxT("Radian");
      break;

    case eUNITS_TMPC :  // Temperature in Celcius
      rosUnits = wxT("°C");
      break;

    case eUNITS_TMPF :  // Temperature in Fahrenheit
      rosUnits = wxT("°F");
      break;

    case eUNITS_EXP  :  // Dimensionless (append an exponent)

    case eUNITS_NONE :  // No units specified
      rosUnits = wxT("");
      break;

    default :           // No associated units
      return( false );
  }

  return( true );
}

//**************************************************************************************************
// Attempt to determine the units type from a string.
//
// Eg. 34.0mVolt produces the units type of eUNITS_VOLT.
//
// Note : This function will not cope multi-character units prefixes eg. milliVolt would fail.
//
// Argument List :
//   rosUnits - the string containing a units specifier
//   peUType  - the returned enumerated units type
//
// Return Values :
//   true  - Success
//   false - Failure

bool  UnitsBase::bGetUnitsType( const wxString & rosUnits, eTypeUnits * peUType )
{
  wxString  os1, os2;
  wxChar    oc1, oc2;
  size_t    sz1;
  int       i1;

  // Set the default units type value
  *peUType = eUNITS_NONE;

  // Extract everything after the numeric value including any units prefix
  sz1 = rosUnits.find_first_not_of( wxT(" .-+0123456789Ee") );
  if( sz1 == std::string::npos )                 return( false );
  if( CnvtType::bUnitPfxToExp( rosUnits[ sz1 ], &i1 ) ) sz1++;
  if( sz1 >= rosUnits.length( ) )                return( false );
  os1 = rosUnits.Mid( sz1 ).Upper( );

  // Now attempt to determine the units type
  switch( (char) os1.GetChar( 0 ) )
  {
    case wxT('A') :
      if( os1.length( ) > 1 )
        if( ! os1.StartsWith( wxT("AMP") ) )     return( false );
      *peUType = eUNITS_CURR;       // Current (A or Amp)
      break;

    case wxT('C') :
      if( os1.length( ) > 1 )
        if( ! os1.StartsWith( wxT("COULOMB") ) ) return( false );
      *peUType = eUNITS_CHRG;       // Current (A or Amp)
      break;

    case wxT('D') :
      if( ! os1.StartsWith( wxT("DEG") ) )       return( false );
      oc1 = oc2 = 0;
      if( os1.length( ) >= 4 ) oc1 = os1.GetChar( 3 );
      if( os1.length( ) >= 5 ) oc2 = os1.GetChar( 4 );
      if(      oc1==wxT('C') || oc2==wxT('C') )
           *peUType = eUNITS_TMPC;  // Temperature (DegC or Deg C or Deg.C)
      else if( oc1==wxT('F') || oc2==wxT('F') )
           *peUType = eUNITS_TMPF;  // Temperature (DegF or Deg F or Deg.F)
      else *peUType = eUNITS_PHAD;  // Phase / angle (Deg)
      break;

    case wxT('F') :
      if( os1.length( ) > 1 )
        if( ! os1.StartsWith( wxT("FARAD") ) )   return( false );
      *peUType = eUNITS_CAP;        // Capacitance (F or Farad)
      break;

    case wxT('H') :                 // Inductance (H or Henry) or Frequency (Hz or Hertz)
      if(      os1.length( )==1           || os1.StartsWith( wxT("HENRY") ) )
        *peUType = eUNITS_IND;
      else if( os1.GetChar( 1 )==wxT('Z') || os1.StartsWith( wxT("HERTZ") ) )
        *peUType = eUNITS_FREQ;
      else                                       return( false );
      break;

    case wxT('O') :
      if( os1.StartsWith( wxT("OHM") ) )
        *peUType = eUNITS_RES;      // Resistance (Ohm)
      else                                       return( false );
      break;

    case wxT('S') :                 // Conductance (S or Siemen) or Time (s or Sec)
      if( os1.length( ) == 1 )
      {
        if( rosUnits.GetChar( sz1 ) == wxT('s') ) *peUType = eUNITS_TIME;
        else                                      *peUType = eUNITS_COND;
      }
      else if( os1.StartsWith( wxT("SEC") ) )     *peUType = eUNITS_TIME;
      else if( os1.StartsWith( wxT("SIEMEN") ) )  *peUType = eUNITS_COND;
      else                                       return( false );
      break;

    case wxT('R') :
      if( ! os1.StartsWith( wxT("RAD") ) )       return( false );
      *peUType = eUNITS_PHAR;       // Phase / angle (Rad)
      break;

    case wxT('V') :
      if( os1.length( ) > 1 )
        if( ! os1.StartsWith( wxT("VOLT") ) )    return( false );
      *peUType = eUNITS_VOLT;       // Voltage (V or Volt)
      break;

    default :                                    return( false );
  }

  return( true );
}

//**************************************************************************************************
