//**************************************************************************************************
//                                          Component.cpp                                          *
//                                         ---------------                                         *
// Started     : 2004-05-14                                                                        *
// Last Update : 2018-10-03                                                                        *
// Copyright   : (C) 2004-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 "Component.hpp"

//**************************************************************************************************
// Finish the definition of the ArrayComponent type. The following expands into
// some C++ code and so should only be compiled once (ie. don't put this into a
// header file rather put it in a source file or there will be linker errors).

#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY( ArrayComponent )

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

Component::Component( void ) : wxString( )
{
  m_osErrMsg = wxT("Invalid");

  m_eType = eCPNT_NONE;
}

//**************************************************************************************************
// Copy constructor.
//
// Argument List :
//   roCpnt - A reference to the object to be copied

Component::Component( const Component & roCpnt ) : wxString( )
{
  *this = roCpnt;
}

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

Component::~Component( )
{
}

//**************************************************************************************************
// Get the component ports.

void  Component::GetPorts( void )
{
  // Not yet implemented ???
}

//**************************************************************************************************
// Test the values against each other.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bValidate( void )
{
  m_osErrMsg.Empty( );

  if( IsEmpty( ) )                      bSetErrMsg( wxT("component is empty") );
  else if( m_osName.IsEmpty( ) )        bSetErrMsg( wxT("component has no name") );
  else if( m_osaNodes.GetCount( ) < 2 ) bSetErrMsg( wxT("component has less than two nodes") );
  else if( m_osValue.IsEmpty( ) )       bSetErrMsg( wxT("component has no value") );

  return( bIsValid( ) );
}

//**************************************************************************************************
// Attempt to set the objects error message string attribute.
//
// Argument List :
//   rosErrMsg - A reference to a string containing the new error message
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bSetErrMsg( const wxString & rosErrMsg )
{
  if( ! bIsValid( ) ) return( false );

  m_osErrMsg = rosErrMsg;

  return( true );
}

//**************************************************************************************************
// Compare algorithm for sorting Component objects.
//
// Argument List :
//   ppo1 - The address of a pointer to the first  Component object to compare
//   ppo2 - The address of a pointer to the second Component object to compare
//
// Return Values :
//   >0 - If *(*ppo1) is greater than *(*ppo2)
//   =0 - If *(*ppo1) is equal to     *(*ppo2)
//   <0 - If *(*ppo1) is less    than *(*ppo2)

int  Component::iCompare( Component ** ppo1, Component ** ppo2 )
{
  const wxString & ros1 = (const wxString &) **ppo1;
  const wxString & ros2 = (const wxString &) **ppo2;

  return( ros1.Cmp( ros2 ) );
}

//**************************************************************************************************
// Clear the object attributes.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bClear( void )
{
  wxString::Empty( );

  m_osErrMsg = wxT("Invalid");

  m_osName  .Clear( );
  m_osaNodes.Clear( );
  m_osValue .Clear( );

  m_eType = eCPNT_NONE;
  m_osaPorts.Clear( );

  return( true );
}

//**************************************************************************************************
// Parse a component definition string extracted from a netlist.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bParse( void )
{
  wxStringTokenizer  ostk1;
  wxString           os1;
  size_t             sz1;

  // Clear the object attributes
  os1 = (wxString &) *this;
  bClear( );
  wxString::assign( os1 );
  m_osErrMsg.Empty( );

  // Tokenize the command string
  if( IsEmpty( ) )               return( bValidate( ) );
  ostk1.SetString( os1 );

  // Extract the component name
  bSetName( ostk1.GetNextToken( ) );

  // Extract the nodes the component is connected to
  if( ! ostk1.HasMoreTokens( ) ) return( bValidate( ) );
  bSetNodes( ostk1.GetString( ) );
  for( sz1=0; sz1<m_osaNodes.GetCount( ); sz1++ )
    ostk1.GetNextToken( );

  // Extract the component value
  if( ! ostk1.HasMoreTokens( ) ) return( bValidate( ) );
  bSetValue( ostk1.GetString( ) );

  return( bValidate( ) );
}

//**************************************************************************************************
// Format the command string.
//
// Eg. : C1 6 2 100n
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bFormat( void )
{
  wxString  osCmd;
  size_t    sz1;

  // Format the value part of the component definition
  bFormatValue( );

  // Set the name
  osCmd = m_osName;

  // Append the nodes
  for( sz1=0; sz1<m_osaNodes.GetCount( ); sz1++ )
    osCmd << wxT(' ') << m_osaNodes.Item( sz1 );

  // Append the value
  osCmd << wxT(' ') << m_osValue;

  wxString::assign( osCmd );

  return( bValidate( ) );
}

//**************************************************************************************************
// Set the object attributes via a component definition string.
//
// Argument List :
//   rosCmd - A reference to a string containing a component definition
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bSetString( const wxString & rosCmd )
{
  // Clear the component attributes
  bClear( );

  // Set the underlying string to the raw component definition
  wxString::assign( rosTrim( rosCmd ) );

  // Parse the component definition
  bParse( );

  return( bIsValid( ) );
}

//**************************************************************************************************
// Set the component name.
//
// Argument List :
//   rosName - A reference to a string containing a compoent name
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bSetName( const wxString & rosName )
{
  wxStringTokenizer  ostk1;
  wxString           os1;
  wxChar             oc1;

  // Tokenize the name string
  ostk1.SetString( rosName );
  if( ostk1.CountTokens( ) <=0 )                                               return( false );

  // Extract the component name and check that it begins with an alpha character
  os1 = ostk1.GetNextToken( );
  oc1 = os1.GetChar( 0 );
  if( !(oc1>=wxT('a') && oc1<=wxT('z')) && !(oc1>=wxT('A') && oc1<=wxT('Z')) ) return( false );
  m_osName = os1;

  // Determine the component type and units type
  m_eType = eGetType( m_osName );

  return( true );
}

//**************************************************************************************************
// Set the component nodes.
//
// Argument List :
//   rosNodes - A reference to a string containing component nodes eg. "2,1".
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bSetNodes( const wxString & rosNodes )
{
  wxStringTokenizer  ostk1;
  size_t             szNodeCnt;
  size_t             sz1;

  // Tokenize the name string
  ostk1.SetString( rosNodes, wxT(" \t\r\n,") );
  if( ostk1.CountTokens( ) <=0 )         return( false );

  // Determine how many nodes to look for based on the component type
  switch( m_eType )
  {
    case eCPNT_CAP    :
    case eCPNT_RES    :
    case eCPNT_IND    :
    case eCPNT_CIND   :
    case eCPNT_DIODE  :
    case eCPNT_VCVS   :
    case eCPNT_CCCS   :
    case eCPNT_VCCS   :
    case eCPNT_CCVS   :
    case eCPNT_IVS    :
    case eCPNT_ICS    :
    case eCPNT_NLDS   :
    case eCPNT_NLDCS  :
    case eCPNT_NLDVS  :
    case eCPNT_STJ    : szNodeCnt = 2;                        break;

    case eCPNT_JFET   :
    case eCPNT_MOSFET :
    case eCPNT_MESFET :
    case eCPNT_BJT    :
    case eCPNT_VCSW   :
    case eCPNT_CCSW   :
    case eCPNT_URC    : szNodeCnt = 3;                        break;

    case eCPNT_TLINE  :
    case eCPNT_LTRA   :
    case eCPNT_TXL    : szNodeCnt = 4;                        break;

    case eCPNT_LOGIC  : szNodeCnt = ostk1.CountTokens( ) - 2; break;

    case eCPNT_CPL    :
    case eCPNT_SUBCKT : szNodeCnt = ostk1.CountTokens( ) - 1; break;

    // For the default guess that all but the last token are nodes
    default           : szNodeCnt = ostk1.CountTokens( ) - 1; break;
  }
  if( szNodeCnt > ostk1.CountTokens( ) ) return( false );

  // Extract the nodes the component is connected to
  m_osaNodes.Empty( );
  if( szNodeCnt > 0 )
  {
    for( sz1=0; sz1<szNodeCnt; sz1++ )
      m_osaNodes.Add( ostk1.GetNextToken( ) );
  }
  else
  {
    for( sz1=0; sz1<ostk1.CountTokens( ); sz1++ )
    {
      if( isalpha( ostk1.GetString( ).GetChar( 0 ) ) ) break;
      m_osaNodes.Add( ostk1.GetNextToken( ) );
    }
  }

  // Determine the device ports
  GetPorts( );

  return( true );
}

//**************************************************************************************************
// Set the component value.
//
// Argument List :
//   rosName - A reference to a string containing a compoent value
//
// Return Values :
//   true  - Success
//   false - Failure

bool  Component::bSetValue( const wxString & rosValue )
{
  wxString  os1;

  // Check if the string is empty
  os1 = rosTrim( rosValue );
  if( os1.IsEmpty( ) ) return( false );

  // Extract the component value
  m_osValue = os1;

  return( bParseValue( ) );
}

//**************************************************************************************************
// Get the component definition string.
//
// Return Values :
//   A reference to the component definition string

const wxString & Component::rosGetString( void )
{
  bFormat( );

  return( (wxString &) *this );
}

//**************************************************************************************************
// Get the component nodes as a string separated by a comma.
//
// Return Values :
//   A reference to the component nodes string

const wxString & Component::rosGetNodes( void )
{
  static  wxString  osNodes;
          size_t    sz1;

  osNodes.Empty( );

  if( ! m_osaNodes.IsEmpty( ) )
  {
    for( sz1=0; sz1<m_osaNodes.GetCount( ); sz1++ )
    {
      if( ! osNodes.IsEmpty( ) ) osNodes << wxT(",");
      osNodes << m_osaNodes.Item( sz1 );
    }
  }

  return( osNodes );
}

//**************************************************************************************************
// Get the numeric value as a mantissa and exponent if possible.
//
// Return Values :
//   A reference to the component nodes string

const wxString & Component::rosGetNumValue( void )
{
  static  wxString   osNumValue;
  wxStringTokenizer  ostk1;
  wxString           os1;

  // Clear the local static variable
  osNumValue.Empty( );

  // Tokenize the value string
  ostk1.SetString( m_osValue );

  // Attempt to parse a field in the value string as a numeric quantity
  while( ostk1.HasMoreTokens( ) )
  {
    os1 = ostk1.GetNextToken( );

    if(      CnvtType::bIsInteger( os1 ) )
    {
      osNumValue = os1;
      break;
    }
    else if( CnvtType::bIsFloat( os1 ) )
    {
      osNumValue = os1;
      break;
    }
  }

  return( osNumValue );
}

//**************************************************************************************************
// Get the component type.
//
// Argument List :
//   rosName - A component name
//
// Return Values :
//   An enumerate type indicating the type of the component

eTypeCpnt  Component::eGetType( const wxString & rosName )
{
  // Determine the component type from the first character of the name
  switch( toupper( rosName.GetChar( 0 ) ) )
  {
    case wxT('B') : return( eCPNT_NLDS   );
    case wxT('C') : return( eCPNT_CAP    );
    case wxT('D') : return( eCPNT_DIODE  );
    case wxT('E') : return( eCPNT_VCVS   );
    case wxT('F') : return( eCPNT_CCCS   );
    case wxT('G') : return( eCPNT_VCCS   );
    case wxT('H') : return( eCPNT_CCVS   );
    case wxT('I') : return( eCPNT_ICS    );
    case wxT('J') : return( eCPNT_JFET   );
    case wxT('K') : return( eCPNT_CIND   );
    case wxT('L') : return( eCPNT_IND    );
    case wxT('M') : return( eCPNT_MOSFET );
    case wxT('O') : return( eCPNT_LTRA   );
    case wxT('P') : return( eCPNT_CPL    );
    case wxT('Q') : return( eCPNT_BJT    );
    case wxT('R') : return( eCPNT_RES    );
    case wxT('S') : return( eCPNT_VCSW   );
    case wxT('T') : return( eCPNT_TLINE  );
    case wxT('U') : return( eCPNT_URC    );
    case wxT('V') : return( eCPNT_IVS    );
    case wxT('W') : return( eCPNT_CCSW   );
    case wxT('X') : return( eCPNT_SUBCKT );
    case wxT('Y') : return( eCPNT_TXL    );
    case wxT('Z') : return( eCPNT_MESFET );

    default       : return( eCPNT_NONE   );
  }
}

//**************************************************************************************************
// Get the units type (where appropriate).
//
// Argument List :
//   rosName - A component name
//
// Return Values :
//   An enumerate type indicating the type of units associated with this component

eTypeUnits  Component::eGetUnitsType( const wxString & rosName )
{
  // Determine the units type from the first character of the name
  switch( toupper( rosName.GetChar( 0 ) ) )
  {
    case wxT('B') : return( eUNITS_EXP  );
    case wxT('C') : return( eUNITS_CAP  );
    case wxT('D') : return( eUNITS_NONE );
    case wxT('E') : return( eUNITS_EXP  );
    case wxT('F') : return( eUNITS_EXP  );
    case wxT('G') : return( eUNITS_EXP  );
    case wxT('H') : return( eUNITS_EXP  );
    case wxT('I') : return( eUNITS_CURR );
    case wxT('J') : return( eUNITS_NONE );
    case wxT('K') : return( eUNITS_IND  );
    case wxT('L') : return( eUNITS_IND  );
    case wxT('M') : return( eUNITS_NONE );
    case wxT('Q') : return( eUNITS_NONE );
    case wxT('R') : return( eUNITS_RES  );
    case wxT('S') : return( eUNITS_NONE );
    case wxT('T') : return( eUNITS_NONE );
    case wxT('U') : return( eUNITS_NONE );
    case wxT('V') : return( eUNITS_VOLT );
    case wxT('W') : return( eUNITS_NONE );
    case wxT('X') : return( eUNITS_NONE );
    case wxT('Y') : return( eUNITS_RES  );

    default       : return( eUNITS_NONE );
  }
}

//**************************************************************************************************
// Trim a component definition string by replacing all field separaters with a single space
// character and removing any line terminators.
//
// Argument List :
//   rosDefn - A reference to a string containing a component definition
//
// Return Values :
//   A reference to a string containing a trimmed copy of the component defn

const wxString & Component::rosTrim( const wxString & rosDefn )
{
  static  wxString   osDefn;
  wxStringTokenizer  ostk1;

  // Tokenize the component definition
  ostk1.SetString( rosDefn, wxT(" \t\r\n"), wxTOKEN_STRTOK );

  // Reconstruct the component definition
  osDefn = ostk1.GetNextToken( );
  while( ostk1.HasMoreTokens( ) )
    osDefn << wxT(' ') << ostk1.GetNextToken( );

  return( osDefn );
}

//**************************************************************************************************
// Copy the contents of a wxString object.
//
// Argument List :
//   rosCmd - A reference to a string containing a component definition
//
// Return Values :
//   A reference to this object

wxString & Component::operator = ( const wxString & rosCmd )
{
  bSetString( rosCmd );

  return( *((wxString *) this) );
}

//**************************************************************************************************
// Copy the contents of another Component object.
//
// Argument List :
//   roCpnt - A reference to a Component object
//
// Return Values :
//   A reference to this object

Component & Component::operator = ( const Component & roCpnt )
{
  (wxString &) *this = (wxString &) roCpnt;

  m_eType    = roCpnt.m_eType;

  m_osName   = roCpnt.m_osName;
  m_osaNodes = roCpnt.m_osaNodes;
  m_osValue  = roCpnt.m_osValue;

  m_osaPorts = roCpnt.m_osaPorts;

  m_osErrMsg = roCpnt.m_osErrMsg;

  return( *this );
}

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

void  Component::Print( const wxString & rosPrefix )
{
  size_t  sz1;

  std::cout << rosPrefix.mb_str( ) << "wxString::        : " << this            ->mb_str( ) << '\n';

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

  std::cout << rosPrefix.mb_str( ) << "m_eType           : ";
  switch( m_eType )
  {
    case eCPNT_CAP    : std::cout << "eCPNT_CAP"   ; break; // Capacitor
    case eCPNT_RES    : std::cout << "eCPNT_RES"   ; break; // Resistor
    case eCPNT_IND    : std::cout << "eCPNT_IND"   ; break; // Inductor
    case eCPNT_CIND   : std::cout << "eCPNT_CIND"  ; break; // Coupled (Mutual) Inductors
    case eCPNT_DIODE  : std::cout << "eCPNT_DIODE" ; break; // Diode
    case eCPNT_BJT    : std::cout << "eCPNT_BJT"   ; break; // BJT (Bipolar Junction Transistor)
    case eCPNT_JFET   : std::cout << "eCPNT_JFET"  ; break; // JFET (Junction Field-Effect Transistor)
    case eCPNT_MOSFET : std::cout << "eCPNT_MOSFET"; break; // MOSFET (Metal-Oxide Semiconductor Field-Effect Transistor)
    case eCPNT_MESFET : std::cout << "eCPNT_MESFET"; break; // MESFET (Metal–Semiconductor Field Effect Transistor)
    case eCPNT_VCVS   : std::cout << "eCPNT_VCVS"  ; break; // Voltage Controlled Voltage Source
    case eCPNT_CCCS   : std::cout << "eCPNT_CCCS"  ; break; // Current Controlled Current Source
    case eCPNT_VCCS   : std::cout << "eCPNT_VCCS"  ; break; // Voltage Controlled Current Source
    case eCPNT_CCVS   : std::cout << "eCPNT_CCVS"  ; break; // Current Controlled Voltage Source
    case eCPNT_TLINE  : std::cout << "eCPNT_TLINE" ; break; // Lossless Transmission Line
    case eCPNT_LTRA   : std::cout << "eCPNT_LTRA"  ; break; // Lossy Transmission Line (LTRA)
    case eCPNT_CPL    : std::cout << "eCPNT_CPL"   ; break; // Coupled Multi-conductor Transmission Line (CPL)
    case eCPNT_TXL    : std::cout << "eCPNT_TXL"   ; break; // Single Lossy Transmission Line (TXL)
    case eCPNT_URC    : std::cout << "eCPNT_URC"   ; break; // Uniform Distributed RC Transmission Line (URC)
    case eCPNT_ICS    : std::cout << "eCPNT_ICS"   ; break; // Independent Current Source
    case eCPNT_IVS    : std::cout << "eCPNT_IVS"   ; break; // Independent Voltage Source
    case eCPNT_NLDS   : std::cout << "eCPNT_NLDS"  ; break; // Non-Linear Dependent Source
    case eCPNT_NLDCS  : std::cout << "eCPNT_NLDCS" ; break; // Non-Linear Dependent Current Source
    case eCPNT_NLDVS  : std::cout << "eCPNT_NLDVS" ; break; // Non-Linear Dependent Voltage Source
    case eCPNT_CCSW   : std::cout << "eCPNT_CCSW"  ; break; // Current Controlled Switch
    case eCPNT_VCSW   : std::cout << "eCPNT_VCSW"  ; break; // Voltage Controlled Switch
    case eCPNT_SUBCKT : std::cout << "eCPNT_SUBCKT"; break; // Sub-circuit
    case eCPNT_LOGIC  : std::cout << "eCPNT_LOGIC" ; break; // Logic Device
    case eCPNT_STJ    : std::cout << "eCPNT_STJ"   ; break; // Super-conducting Tunnel Junction

    default           : std::cout << "Invalid";
  }
  std::cout << '\n';

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

  std::cout << rosPrefix.mb_str( ) << "m_osaNodes[ ]     : ";
  for( sz1=0; sz1<m_osaNodes.GetCount( ); sz1++ )
  {
    if( sz1 > 0 ) std::cout << ", ";
    std::cout << m_osaNodes.Item( sz1 ).mb_str( );
  }
  std::cout << '\n';

  std::cout << rosPrefix.mb_str( ) << "m_osaPorts[ ]     : ";
  for( sz1=0; sz1<m_osaPorts.GetCount( ); sz1++ )
  {
    if( sz1 > 0 ) std::cout << " ";
    std::cout << m_osaPorts.Item( sz1 ).mb_str( );
  }
  std::cout << '\n';

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

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

  std::cout << rosPrefix.mb_str( ) << "eGetUnitsType( )  : ";
  switch( eGetUnitsType( m_osName ) )
  {
    case eUNITS_CAP  : std::cout << "eUNITS_CAP";  break;
    case eUNITS_IND  : std::cout << "eUNITS_IND";  break;
    case eUNITS_RES  : std::cout << "eUNITS_RES";  break;
    case eUNITS_VOLT : std::cout << "eUNITS_VOLT"; break;
    case eUNITS_CURR : std::cout << "eUNITS_CURR"; break;
    case eUNITS_TIME : std::cout << "eUNITS_TIME"; break;
    case eUNITS_FREQ : std::cout << "eUNITS_FREQ"; break;
    case eUNITS_PHAD : std::cout << "eUNITS_PHAD"; break;
    case eUNITS_PHAR : std::cout << "eUNITS_PHAR"; break;
    case eUNITS_TMPC : std::cout << "eUNITS_TMPC"; break;
    case eUNITS_TMPF : std::cout << "eUNITS_TMPF"; break;
    case eUNITS_EXP  : std::cout << "eUNITS_EXP";  break;
    case eUNITS_NONE : std::cout << "eUNITS_NONE"; break;

    default          : std::cout << "Invalid";     break;
  }
  std::cout << '\n';
}

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

#ifdef TEST_COMPONENT

using  namespace  std;

// Function prototypes

void  Usage( char * psAppName );

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

int  main( int argc, char * argv[ ] )
{
  // This function is used in wxBase only and only if an wxApp object isn't created at all. In this
  // case wxInitialize( ) must be called in main( ) before calling any other wxWidgets functions.
  if( ! wxInitialize( ) ) exit( EXIT_FAILURE );

  wxString  osCpnt;
  wxString  os1;

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

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

  // Display the utility banner
  cout << "\n  Component Class Test Utility"
       << "\n   Version 1.09 (2016-09-29)\n";

  // Create a component object
  Component  oCpnt;

  // Use the following command example to check the formatter and the parser :
  osCpnt = wxT("Vin	 2	0	 AC 1V");  // This definition does contain TABs and SPACEs
  cout << "\nCpnt defn to test : "
       << osCpnt.mb_str( ) << "\n\n";

  // ******************** Test the formatter ********************
  cout << "Test the component formatter :";

  // Set things up for a formatter test
  oCpnt.bClear( );
  oCpnt.bSetName ( wxT("Vin")   );
  oCpnt.bSetNodes( wxT("2,0")   );
  oCpnt.bSetValue( wxT("AC 1V") );

  // Run the formatter
  cout << "\n  Run formatter  : ";
  if( oCpnt.bFormat( ) )
       cout << "OK";
  else cout << "FAULT (" << oCpnt.rosGetErrMsg( ).mb_str( ) << ')';

  // Test the formatter output
  cout << "\n  Test format    : ";
  if( (wxString) oCpnt == Component::rosTrim( osCpnt ) )
    cout << "OK\n" << "  Component defn : "
         << oCpnt.rosGetString( ).mb_str( ) << '\n';
  else
  { // There's been an error so print the component contents
    cout << "FAULT\n";
    cout << "  oCpnt Contents : \n";
    oCpnt.Print( wxT("    ") );
  }

  cout << "\n";

  // ********************* Test the parser *********************
  cout << "Test the component parser :";

  // Set things up for a parser test
  oCpnt.bClear( );
  oCpnt = osCpnt;

  // Run the parser
  cout << "\n  Run parser     : ";
  if( oCpnt.bParse( ) )
       cout << "OK";
  else cout << "FAULT (" << oCpnt.rosGetErrMsg( ).mb_str( ) << ')';

  // Test the parser output
  oCpnt.bFormat( );
  cout << "\n  Test format    : ";
  if( (wxString) oCpnt == Component::rosTrim( osCpnt ) )
    cout << "OK\n" << "  Component defn : "
         << oCpnt.rosGetString( ).mb_str( ) << '\n';
  else
  { // There's been an error so print the component contents
    cout << "FAULT\n";
    cout << "  oCpnt Contents : \n";
    oCpnt.Print( wxT("    ") );
  }

  cout << "\n";

  // This function must be called for each successful call to wxInitialize( ). Clean up; the library
  // can't be used any more.
  wxInitialize( );

  exit( EXIT_SUCCESS );
}

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

void  Usage( char * psAppName )
{
  cout << "\nUsage   : " << psAppName << " [-OPTIONS] [CPNTDEF]"
       << "\nOptions : -h      : Print usage (this message)"
       << "\n          CPNTDEF : An component definition (not yet implemented)"
       << "\n\n";
}

#endif // TEST_COMPONENT

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