//**************************************************************************************************
//                                         CmdGnuCapGEN.cpp                                        *
//                                        ------------------                                       *
// Started     : 2008-03-11                                                                        *
// Last Update : 2018-11-03                                                                        *
// Copyright   : (C) 2008-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 "CmdGnuCapGEN.hpp"

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

CmdGnuCapGEN::CmdGnuCapGEN( void ) : CmdBase( )
{
  bSetDefaults( );
}

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

CmdGnuCapGEN::~CmdGnuCapGEN( )
{
}

//**************************************************************************************************
// Check that the object attributes are valid.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdGnuCapGEN::bValidate( void )
{
  double  df1, df2, df3, df4;

  CmdBase::bValidate( );

  // Overall source characteristics
  if( ! CnvtType::bStrToFlt( m_osAmplitude, &df1 ) )
    SetErrMsg( wxT("overall amplitude value is invalid") );
//  if( df1 <= 0.0 ) ??? 05/10/2009
//    SetErrMsg( wxT("overall amplitude value must be greater than zero") );
  if( ! CnvtType::bStrToFlt( m_osOffset   , &df1 ) )
    SetErrMsg( wxT("overall offset value is invalid") );

  // Sinusoidal source characteristics
  if( ! CnvtType::bStrToFlt( m_osSinFreq  , &df1 ) )
    SetErrMsg( wxT("sinusoid frequency value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osSinPhase , &df1 ) )
    SetErrMsg( wxT("sinusoid phase value is invalid") );

  // Check the pulse time values
  if( ! CnvtType::bStrToFlt( m_osPulRise  , &df1 ) )
    SetErrMsg( wxT("pulse rise time value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulWidth , &df2 ) )
    SetErrMsg( wxT("pulse width value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulFall  , &df3 ) )
    SetErrMsg( wxT("pulse fall time value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulPeriod, &df4 ) )
    SetErrMsg( wxT("pulse period value is invalid") );
  if(      df4 < df1 )
    SetErrMsg( wxT("pulse rise time is greater than the pulse period") );
  else if( df4 < df2 )
    SetErrMsg( wxT("pulse width is greater than the pulse period") );
  else if( df4 < df3 )
    SetErrMsg( wxT("pulse fall time is greater than the pulse period") );
  else if( df4 < df1+df2+df3 )
    SetErrMsg( wxT("pulse period is less then the pulse width") );

  // Check the pulse level values
  if( ! CnvtType::bStrToFlt( m_osPulInitV , &df1 ) )
    SetErrMsg( wxT("initial pulse value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulMin   , &df2 ) )
    SetErrMsg( wxT("minimum pulse value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulMax   , &df3 ) )
    SetErrMsg( wxT("maximum pulse value is invalid") );
  if(      df1 < df2 )
    SetErrMsg( wxT("initial pulse value is less than the minimum pulse value") );
  else if( df1 > df3 )
    SetErrMsg( wxT("initial pulse value is greater than the maximum pulse value") );
  else if( df2 > df3 )
    SetErrMsg( wxT("minimum pulse value is less than the maximum pulse value") );

  // Check overall pulse source logic
  if( df4!=0.0  && (df2==0.0 && df3==0.0) )
    SetErrMsg( wxT("pulse timing has been defined but no pulse levels") );
  if( df4==0.0 && (df2!=0.0 || df3!=0.0) )
    SetErrMsg( wxT("pulse levels have been defined but no pulse timing") );

  return( true );
}

//**************************************************************************************************
// Set the object attributes to they're default values.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdGnuCapGEN::bSetDefaults( void )
{
  CmdBase::bSetDefaults( );

  m_eSimEng     = eSIMR_GNUCAP;
  m_eCmdType    = eCMD_GEN;

  m_osAmplitude = GCP_AMPLITUDE;
  m_osOffset    = GCP_OFFSET;

  m_osSinFreq   = GCP_SINFREQ;
  m_osSinPhase  = GCP_SINPHASE;

  m_osPulInitV  = GCP_PULINITV;
  m_osPulMin    = GCP_PULMIN;
  m_osPulMax    = GCP_PULMAX;
  m_osPulDelay  = GCP_PULDELAY;
  m_osPulRise   = GCP_PULRISE;
  m_osPulWidth  = GCP_PULWIDTH;
  m_osPulFall   = GCP_PULFALL;
  m_osPulPeriod = GCP_PULPERIOD;

  return( true );
}

//**************************************************************************************************
// Parse the command string.
//
// Eg.s : .GENERATOR AMP=2.00K OFFSET=1.00 FREQ=1.00K PHASE=50.00 INIT=5.00m MIN=2.00m MAX=10.00m
//                   DELAY=4.00m RISE=2.00m WIDTH=8.00m FALL=1.00m PERIOD=20.00m
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdGnuCapGEN::bParse( void )
{
  wxStringTokenizer  ostk1;
  wxString           os1, os2;
  size_t             szt1;
  int                i1;

  // Clear the object attributes
  os1 = (wxString &) *this;
  bSetDefaults( );
  assign( os1 );

  // Tokenize the command string
  ostk1.SetString( *this );
  i1 = ostk1.CountTokens( );
  if( i1<2 || i1>13 )      return( bValidate( ) );

  // Check command type
  os1 = ostk1.GetNextToken( ).Left( 4 ).Upper( );
  if( os1 != wxT(".GEN") ) return( bValidate( ) );

  // Extract each parameter value
  while( ostk1.HasMoreTokens( ) )
  {
    // Extract the field name and the associated value
    os1 = ostk1.GetNextToken( );
    os2 = wxT("");
    if( ( szt1=os1.find( wxT("=") ) ) != wxString::npos )
    {
      os2 = os1.Right( os1.Length( )-szt1-1 );
      os1 = os1.Left( szt1 );
    }

    // Set the object attribute values
    if(      os1 == wxT("AMP")    ) m_osAmplitude = os2;
    else if( os1 == wxT("OFFSET") ) m_osOffset    = os2;
    else if( os1 == wxT("FREQ")   ) m_osSinFreq   = os2;
    else if( os1 == wxT("PHASE")  ) m_osSinPhase  = os2;
    else if( os1 == wxT("INIT")   ) m_osPulInitV  = os2;
    else if( os1 == wxT("MIN")    ) m_osPulMin    = os2;
    else if( os1 == wxT("MAX")    ) m_osPulMax    = os2;
    else if( os1 == wxT("DELAY")  ) m_osPulDelay  = os2;
    else if( os1 == wxT("RISE")   ) m_osPulRise   = os2;
    else if( os1 == wxT("WIDTH")  ) m_osPulWidth  = os2;
    else if( os1 == wxT("FALL")   ) m_osPulFall   = os2;
    else if( os1 == wxT("PERIOD") ) m_osPulPeriod = os2;
    else                   return( bValidate( ) );
  }

  return( bValidate( ) );
}

//**************************************************************************************************
// Format the command string.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdGnuCapGEN::bFormat( void )
{
  wxString  os1;
  float     f1;
  bool      b1;

  os1 = wxT(".GENERATOR");

  if( ! m_osAmplitude .IsEmpty( ) ) os1 << wxT(" AMP=")    << m_osAmplitude;
  if( ! m_osOffset    .IsEmpty( ) ) os1 << wxT(" OFFSET=") << m_osOffset;

  f1 = 0.0;
  b1 = CnvtType::bStrToFlt( m_osSinFreq, &f1 );
  if( b1==true && f1!=0.0 )
  {
    os1 << wxT(" FREQ=")   << m_osSinFreq;
    os1 << wxT(" PHASE=")  << m_osSinPhase;
  }

  f1 = 0.0;
  b1 = CnvtType::bStrToFlt( m_osPulMax, &f1 );
  if( b1==true && f1!=0.0 )
  {
    os1 << wxT(" INIT=")   << m_osPulInitV;
    os1 << wxT(" MIN=")    << m_osPulMin;
    os1 << wxT(" MAX=")    << m_osPulMax;
    os1 << wxT(" DELAY=")  << m_osPulDelay;
    os1 << wxT(" RISE=")   << m_osPulRise;
    os1 << wxT(" WIDTH=")  << m_osPulWidth;
    os1 << wxT(" FALL=")   << m_osPulFall;
    os1 << wxT(" PERIOD=") << m_osPulPeriod;
  }

  assign( os1 );

  return( bValidate( ) );
}

//**************************************************************************************************
// Copy the contents of a CpntNgsIndSrc object.
//
// Argument List :
//   roIndSrc - A reference to a CpntNgsIndSrc object
//
// Return Values :
//   A reference to this object

CmdGnuCapGEN & CmdGnuCapGEN::operator = ( const CpntNgSpiceIndSrc & roIndSrc )
{
  double  df1, df2;
  int     i1;

  if( ! roIndSrc.m_osSinAmp.IsEmpty( ) ) m_osAmplitude = roIndSrc.m_osSinAmp;
  else                                   m_osAmplitude = wxT("1.0");
  m_osOffset     = roIndSrc.m_osSinOffset;

  m_osSinFreq    = roIndSrc.m_osSinFreq;
  if( CnvtType::bStrToFlt( roIndSrc.m_osSinFreq,  &df1 ) &&
      CnvtType::bStrToFlt( roIndSrc.m_osSinDelay, &df2 ) )
  {
    if( CnvtType::bIsEqual(df1,0.0) || CnvtType::bIsEqual(df2,0.0) ) m_osSinPhase = wxT("0.0m");
    else
    {
      i1 = CnvtType::iGetFltRes( );
      CnvtType::bSetFltRes( 2 );
      CnvtType::bFltToStrEng( df1 * df2 * 360.0, m_osSinPhase );
      CnvtType::bSetFltRes( i1 );
      m_osSinPhase.Trim( false );
    }
  }
  else
    m_osSinPhase = wxT("0.0m");

  m_osPulInitV  = roIndSrc.m_osPulInitV;
  m_osPulMax    = roIndSrc.m_osPulValue;
  m_osPulDelay  = roIndSrc.m_osPulDelay;
  m_osPulRise   = roIndSrc.m_osPulRise;
  m_osPulWidth  = roIndSrc.m_osPulWidth;
  m_osPulFall   = roIndSrc.m_osPulFall;
  m_osPulPeriod = roIndSrc.m_osPulPeriod;

  bFormat( );

  return( *this );
}

//**************************************************************************************************
// Print the object attributes.
//
// Argument List :
//   rosPrefix - A prefix to every line displayed (usually just spaces)

void  CmdGnuCapGEN::Print( const wxString & rosPrefix )
{
  CmdBase::Print( rosPrefix + wxT("CmdBase::") );

  std::cout << rosPrefix.mb_str( ) << "m_osAmplitude : " << m_osAmplitude.mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osOffset    : " << m_osOffset   .mb_str( ) << '\n';

  std::cout << rosPrefix.mb_str( ) << "m_osSinFreq   : " << m_osSinFreq  .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osSinPhase  : " << m_osSinPhase .mb_str( ) << '\n';

  std::cout << rosPrefix.mb_str( ) << "m_osPulInitV  : " << m_osPulInitV .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulMin    : " << m_osPulMin   .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulMax    : " << m_osPulMax   .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulDelay  : " << m_osPulDelay .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulRise   : " << m_osPulRise  .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulWidth  : " << m_osPulWidth .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulFall   : " << m_osPulFall  .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulPeriod : " << m_osPulPeriod.mb_str( ) << '\n';
}

//**************************************************************************************************
//                                          Test Utility                                           *
//**************************************************************************************************

#ifdef TEST_CMDGNUCAPGEN

using  namespace  std;

// Function prototypes

void  Usage( char * psAppName );

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

int  main( int argc, char * argv[ ] )
{
  wxString  osCmd;
  wxString  os1;

  // Validate the argument count passed to the application
  if( argc > 2 )           { Usage( argv[ 0 ] ); exit( EXIT_FAILURE ); }

  // Process the command line arguments
  os1 = wxConvLibc.cMB2WC( argv[ 1 ] );
  if( argc > 1 )
  {
    if( os1 == wxT("-h") ) { Usage( argv[ 0 ] ); exit( EXIT_SUCCESS ); }
    else                   { Usage( argv[ 0 ] ); exit( EXIT_FAILURE ); }
  }

  // Display the utility banner
  cout << "\n  Class CmdGnuCapGEN Test Utility"
       << "\n     Version 1.05 (2018-11-03)\n";

  // Create a GNU-CAP Transient command object
  CmdGnuCapGEN  tCmd_GEN;

  // Use the following command example to check the formatter and the parser :
  osCmd << wxT(".GENERATOR AMP=2.00K OFFSET=1.00 FREQ=1.00K PHASE=50.00 ")
        << wxT("INIT=5.00m MIN=2.00m MAX=10.00m DELAY=4.00m RISE=2.00m ")
        << wxT("WIDTH=8.00m FALL=1.00m PERIOD=20.00m");

  // Set things up for a formatter test
  tCmd_GEN.m_osAmplitude = wxT("2.00K");
  tCmd_GEN.m_osOffset    = wxT("1.00");
  tCmd_GEN.m_osSinFreq   = wxT("1.00K");
  tCmd_GEN.m_osSinPhase  = wxT("50.00");
  tCmd_GEN.m_osPulInitV  = wxT("5.00m");
  tCmd_GEN.m_osPulMin    = wxT("2.00m");
  tCmd_GEN.m_osPulMax    = wxT("10.00m");
  tCmd_GEN.m_osPulDelay  = wxT("4.00m");
  tCmd_GEN.m_osPulRise   = wxT("2.00m");
  tCmd_GEN.m_osPulWidth  = wxT("8.00m");
  tCmd_GEN.m_osPulFall   = wxT("1.00m");
  tCmd_GEN.m_osPulPeriod = wxT("20.00m");
  cout << "\nRun Formatter     : " << ( tCmd_GEN.bFormat( ) ? "OK" : "FAULT" );
  cout << "\nTest Cmd Format   : " << ( tCmd_GEN == osCmd   ? "OK" : "FAULT" );
  cout << "\nExample Command   : " << osCmd  .mb_str( );
  cout << "\ntCmd_GEN Contents : " << tCmd_GEN.mb_str( ) << '\n';

  // Set things up for a parser test
  tCmd_GEN.bSetString( osCmd );
  cout << "\nRun Parser        : " << ( tCmd_GEN.bParse( ) ? "OK" : "FAULT" );
  tCmd_GEN.bFormat( );
  cout << "\nTest Cmd Format   : " << ( tCmd_GEN == osCmd  ? "OK" : "FAULT" );
  cout << "\nExample Command   : " << osCmd  .mb_str( );
  cout << "\ntCmd_GEN Contents : " << tCmd_GEN.mb_str( ) << '\n';

  cout << '\n';

  exit( EXIT_SUCCESS );
}

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

void  Usage( char * psAppName )
{
  cout << "\nUsage   : " << psAppName << " [-OPTIONS]"
       << "\nOptions :"
       << "\n  -h : Print usage (this message)\n";
}

#endif // TEST_CMDGNUCAPGEN

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