//**************************************************************************************************
//                                         CmdGnuCapPR.cpp                                         *
//                                        -----------------                                        *
// Started     : 2008-03-17                                                                        *
// Last Update : 2016-09-29                                                                        *
// Copyright   : (C) 2008-2016 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 "CmdGnuCapPR.hpp"

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

CmdGnuCapPR::CmdGnuCapPR( void )
{
  bSetDefaults( );
}

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

CmdGnuCapPR::~CmdGnuCapPR( )
{
}

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

bool  CmdGnuCapPR::bValidate( void )
{
  size_t  sz1;
  bool    b1;

  CmdBase::bValidate( );

  if( ( m_osaNodes.GetCount( ) + m_osaCpnts.GetCount( ) ) <= 0 )
    SetErrMsg( wxT("No components or nodes have been selected.") );
  for( sz1=0, b1=false; sz1<(size_t)ePARAM_NONE; sz1++ )
    if( m_bParams[ sz1 ] ) b1 = true;
  if( ! b1 )
    SetErrMsg( wxT("No parameters selected.") );

  return( bIsValid( ) );
}

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

bool  CmdGnuCapPR::bSetDefaults( void )
{
  int  i1;

  CmdBase::bSetDefaults( );

  m_eSimEng  = eSIMR_GNUCAP;
  m_eCmdType = eCMD_PR;

  m_eAnaType = eCMD_NONE;

  m_osaNodes.Empty( );
  m_osaCpnts.Empty( );

  for( i1=0; i1<(int)ePARAM_NONE; i1++ ) m_bParams[ i1 ] = false;
  for( i1=0; i1<(int)eCPXPT_NONE; i1++ ) m_bCpxPts[ i1 ] = false;

  return( true );
}

//**************************************************************************************************
// Set the analysis.
//
// Argument List :
//   eAnaType - An analysis command type
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdGnuCapPR::bSetAnaType( eTypeCmd eAnaType )
{
  // Does anything actually need to be done?
  if( eAnaType == m_eAnaType ) return( true );

  // Check that the desired analysis type is supported
  switch( eAnaType )
  {
    case eCMD_OP :
    case eCMD_DC :
    case eCMD_AC :
    case eCMD_TR :
    case eCMD_FO : break;
    default      : return( false );
  }

  m_eAnaType = eAnaType;

  return( true );
}

//**************************************************************************************************
// Parse the command string.
//
// Eg.s : .PRINT AC VM(R1) VM(C1) VM(2) VP(C1) VP(R1) VP(2)
//        .PRINT AC VDB(2)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdGnuCapPR::bParse( void )
{
  wxStringTokenizer  ostk1;
  wxString           os1;
  int                i1, i2;

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

  // Tokenize the command string
  ostk1.SetString( *this );
  if( ostk1.CountTokens( ) < 3 ) return( bValidate( ) );

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

  // Extract the analysis type
  os1 = ostk1.GetNextToken( ).Left( 2 ).Upper( );
  if(      os1 == wxT("OP") ) m_eAnaType = eCMD_OP;
  else if( os1 == wxT("DC") ) m_eAnaType = eCMD_DC;
  else if( os1 == wxT("AC") ) m_eAnaType = eCMD_AC;
  else if( os1 == wxT("TR") ) m_eAnaType = eCMD_TR;
  else if( os1 == wxT("FO") ) m_eAnaType = eCMD_FO;
  else                           return( bValidate( ) );

  // Extract the parameters to derive, any complex parts and the test component
  // and/or test node labels
  while( ostk1.HasMoreTokens( ) )
  {
    // Extract the next field
    os1 = ostk1.GetNextToken( );

    // Extract the parameter specifiers
    switch( (char) os1.GetChar( 0 ) )
    {
      case wxT('V') : m_bParams[ ePARAM_VLT ] = true; break;
      case wxT('I') : m_bParams[ ePARAM_CUR ] = true; break;
      case wxT('P') : m_bParams[ ePARAM_PWR ] = true; break;
      case wxT('R') : m_bParams[ ePARAM_RES ] = true; break;
      default :                  return( bValidate( ) );
    }

    // Extract the complex parts if the analysis type is AC
    if( m_eAnaType == eCMD_AC )
    {
      switch( (char) os1.GetChar( 1 ) )
      {
        case wxT('M') : m_bCpxPts[ eCPXPT_MAG   ] = true; break;
        case wxT('P') : m_bCpxPts[ eCPXPT_PHASE ] = true; break;
        case wxT('R') : m_bCpxPts[ eCPXPT_REAL  ] = true; break;
        case wxT('I') : m_bCpxPts[ eCPXPT_IMAG  ] = true; break;
        case wxT('D') :
          if( os1.Mid( 1, 2 ).Upper( ) == wxT("DB") )
          {
            m_bCpxPts[ eCPXPT_MAG   ] = true;
            m_bCpxPts[ eCPXPT_MAGDB ] = true;
            break;
          }
        default :                return( bValidate( ) );
      }
    }

    // Extract the node or component label/s eg. V(1) or V(C1)
    i1 = os1.Index( wxT('(') );
    i2 = os1.Index( wxT(')') );
    if( i1!=-1 && i2!=-1 && i2>(i1+1) )
    {
      // Get the label
      os1 = os1.Mid( (size_t) i1+1, (size_t) i2-i1-1 );

      // Add the label to the node or component label lists
      if( wxIsdigit( os1.GetChar( 0 ) ) || Component::eGetType( os1 ) == eCPNT_NONE ||
                                                NetList::m_osaNodeLbls.Index( os1 ) != wxNOT_FOUND )
           { if( m_osaNodes.Index( os1 ) == wxNOT_FOUND ) m_osaNodes.Add( os1 ); }
      else { if( m_osaCpnts.Index( os1 ) == wxNOT_FOUND ) m_osaCpnts.Add( os1 ); }
    }
    else                         return( bValidate( ) );
  }

  return( bValidate( ) );
}

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

bool  CmdGnuCapPR::bFormat( void )
{
  wxString  osCmd, osParam, osCpxPt, os1;
  int       i1, i2, i3;

  // Set the command name
  osCmd = wxT(".PRINT ");

  // Append the analysis type
  switch( m_eAnaType )
  {
    case eCMD_OP : osCmd << wxT("OP"); break;
    case eCMD_DC : osCmd << wxT("DC"); break;
    case eCMD_AC : osCmd << wxT("AC"); break;
    case eCMD_TR : osCmd << wxT("TR"); break;
    case eCMD_FO : osCmd << wxT("FO"); break;
    default      :                     break;
  }

  // Sequence through the various parameter types
  osCmd << wxT(' ');
  for( i1=0; i1<ePARAM_NONE; i1++ )
  {
    if( ! m_bParams[ i1 ] ) continue;

    switch( (eTypeParam) i1 )
    {
      case ePARAM_VLT : osParam = wxT('V'); break;
      case ePARAM_CUR : osParam = wxT('I'); break;
      case ePARAM_PWR : osParam = wxT('P'); break;
      case ePARAM_RES : osParam = wxT('R'); break;
      default         :                     break;
    }

    // Sequence through the various complex parts
    for( i2=0; i2<(int)eCPXPT_NONE-1; i2++ )
    {
      if( m_eAnaType == eCMD_AC )
      {
        if( ! m_bCpxPts[ i2 ] ) continue;

        switch( (eTypeCpxPt) i2 )
        {
          case eCPXPT_MAG   : osCpxPt = (m_bCpxPts[ eCPXPT_MAGDB ] ? wxT("DB") : wxT("M")); break;
          case eCPXPT_PHASE : osCpxPt = wxT('P'); break;
          case eCPXPT_REAL  : osCpxPt = wxT('R'); break;
          case eCPXPT_IMAG  : osCpxPt = wxT('I'); break;
          default           :                     break;
        }
      }
      else osCpxPt = wxT("");

      // Sequence throught all the components and nodes
      for( i3=0; i3<(int)m_osaCpnts.GetCount( ); i3++ )
        osCmd << osParam << osCpxPt << wxT('(') << m_osaCpnts.Item( i3 ) << wxT(") ");
      for( i3=0; i3<(int)m_osaNodes.GetCount( ); i3++ )
        osCmd << osParam << osCpxPt << wxT('(') << m_osaNodes.Item( i3 ) << wxT(") ");

      if( m_eAnaType != eCMD_AC ) break;
    }
  }
  osCmd.Trim( );

  // Assign the command string value to the base class
  assign( osCmd );

  return( bValidate( ) );
}

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

CmdGnuCapPR & CmdGnuCapPR::operator = ( const CmdNgSpicePR & roCmdPR )
{
  int  i1;

  (CmdBase &) *this = (CmdBase &) roCmdPR;

  bSetAnaType( roCmdPR.eGetAnaType( ) );

  m_osaNodes = roCmdPR.m_osaNodes;
  m_osaCpnts = roCmdPR.m_osaCpnts;

  for( i1=0; i1<(int)ePARAM_NONE; i1++ ) m_bParams[ i1 ] = roCmdPR.m_bParams[ i1 ];
  for( i1=0; i1<(int)eCPXPT_NONE; i1++ ) m_bCpxPts[ i1 ] = roCmdPR.m_bCpxPts[ i1 ];

  bFormat( );

  return( *this );
}

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

void  CmdGnuCapPR::Print( const wxString & rosPrefix )
{
  int     i1;
  size_t  sz1;

  CmdBase::Print( rosPrefix + wxT("CmdBase::") );

  std::cout << rosPrefix.mb_str( ) << "m_eAnaType      : ";
  switch( m_eAnaType )
  {
    case eCMD_OP   : std::cout << "eCMD_OP";   break;  // Operating point analysis
    case eCMD_DC   : std::cout << "eCMD_DC";   break;  // DC analysis
    case eCMD_AC   : std::cout << "eCMD_AC";   break;  // AC analysis
    case eCMD_TR   : std::cout << "eCMD_TR";   break;  // Transient analysis
    case eCMD_FO   : std::cout << "eCMD_FO";   break;  // Fourier analysis
    case eCMD_DI   : std::cout << "eCMD_DI";   break;  // Distortion analysis
    case eCMD_NO   : std::cout << "eCMD_NO";   break;  // Noise analysis
    case eCMD_PZ   : std::cout << "eCMD_PZ";   break;  // Pole-zero analysis
    case eCMD_SE   : std::cout << "eCMD_SE";   break;  // Sensitivity analysis
    case eCMD_TF   : std::cout << "eCMD_TF";   break;  // Transfer function analysis
    case eCMD_NONE : std::cout << "eCMD_NONE"; break;  // Analysis type not set
    default        : std::cout << "Invalid";   break;
  }
  std::cout << '\n';

  std::cout << rosPrefix.mb_str( ) << "m_osaCpnts[ n ] :";
  for( sz1=0; sz1<m_osaCpnts.GetCount( ); sz1++ )
    std::cout << ' ' << m_osaCpnts[ sz1 ].mb_str( );
  std::cout << '\n';

  std::cout << rosPrefix.mb_str( ) << "m_osaNodes[ n ] :";
  for( sz1=0; sz1<m_osaNodes.GetCount( ); sz1++ )
    std::cout << ' ' << m_osaNodes[ sz1 ].mb_str( );
  std::cout << '\n';

  std::cout << rosPrefix.mb_str( ) << "m_bParams [ n ] :";
  for( i1=0; i1<(int)ePARAM_NONE; i1++ )
    std::cout << ( m_bParams[ i1 ] ? " true " : " false" );
  std::cout << '\n';

  std::cout << rosPrefix.mb_str( ) << "m_bCpxPts [ n ] :";
  for( i1=0; i1<(int)eCPXPT_NONE; i1++ )
    std::cout << ( m_bCpxPts[ i1 ] ? " true " : " false" );
  std::cout << '\n';
}

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

#ifdef TEST_CMDGNUCAPPR

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 CmdGnuCapPR Test Utility"
       << "\n    Version 1.03 (2016-09-29)\n";

  // Create a NG-SPICE PRINT command object
  CmdGnuCapPR  oCmd_PR;

  // Use the following command example to check the formatter and the parser :
  osCmd = wxT(".PRINT AC VM(C1) VM(R1) VM(2) VP(C1) VP(R1) VP(2)");

  // Set things up for a formatter test
  oCmd_PR.bSetAnaType( eCMD_AC );
  oCmd_PR.m_osaCpnts.Add( wxT("C1") );
  oCmd_PR.m_osaCpnts.Add( wxT("R1") );
  oCmd_PR.m_osaNodes.Add( wxT("2") );
  oCmd_PR.m_bParams[ ePARAM_VLT   ] = true;
  oCmd_PR.m_bCpxPts[ eCPXPT_MAG   ] = true;
  oCmd_PR.m_bCpxPts[ eCPXPT_PHASE ] = true;
  cout << "\nRun Formatter   : " << ( oCmd_PR.bFormat( ) ? "OK" : "FAULT" );
  cout << "\nTest Cmd Format : " << ( oCmd_PR == osCmd   ? "OK" : "FAULT" );
  cout << "\nExample Command : " << osCmd .mb_str( );
  cout << "\noCmdPR Contents : " << oCmd_PR.mb_str( ) << '\n';

  // Set things up for a parser test
  oCmd_PR.bSetString( osCmd );
  cout << "\nRun Parser      : " << ( oCmd_PR.bParse( ) ? "OK" : "FAULT" );
  oCmd_PR.bFormat( );
  cout << "\nTest Cmd Format : " << ( oCmd_PR == osCmd  ? "OK" : "FAULT" );
  cout << "\nExample Command : " << osCmd .mb_str( );
  cout << "\noCmdPR Contents : " << oCmd_PR.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_CMDGNUCAPPR

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