//**************************************************************************************************
//                                           NetList.cpp                                           *
//                                          -------------                                          *
// Started     : 2003-09-01                                                                        *
// Last Update : 2016-10-25                                                                        *
// Copyright   : (C) 2003-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 "NetList.hpp"

//**************************************************************************************************
// Allocate storage for static data members.

wxFileName      NetList::m_ofnLoadFile;
wxFileName      NetList::m_ofnSaveFile;
ArrayFileName   NetList::m_oaSchemFiles;

wxArrayString   NetList::m_osaNetLst;
wxArrayString   NetList::m_osaTitle;
wxArrayString   NetList::m_osaIncludes;
ArrayComponent  NetList::m_oaCpnts;
wxArrayString   NetList::m_osaModels;
wxArrayString   NetList::m_osaSubCcts;

wxArrayString   NetList::m_osaNodeLbls;

bool            NetList::m_bIsValid=false;

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

NetList::NetList( void )
{
}

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

NetList::~NetList( )
{
}

//**************************************************************************************************
// Extract the title for the netlist.
//
// Noyes : - Traditionally the first line in the netlist is the title.
//         - An '*' at the start of a line seems to be used as a comment designator.
//         - If the file was created by gnetlist or gSpiceUI throw it away.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoadTitle( void )
{
  // Clear the title string array object attribute
  m_osaTitle.Clear( );

  // Check that the circuit isn't empty
  if( m_osaNetLst.IsEmpty( ) )          return( false );

  // If the first line is empty assume there is no title
  wxString  os1 = m_osaNetLst.Item( 0 );
  if( os1.IsEmpty( ) )                  return( true );

  // If the first line doesn't begin with an '*' then assume it is the title
  if( os1.GetChar( 0 ) != wxT('*') )
  {
    os1 = wxT("* ") + os1;
    m_osaTitle.Add( os1 );
    return( true );
  }

  // If the netlist was created using gnetlist the first line will look something like the
  // following :
  //   gnetlist -v -g spice-sdb -o filter-lp-1.ckt filter-lp-1.sch
  if( os1.Contains( wxT("gnetlist") ) ) return( true );

  // If the netlist was created by gSpiceUI the start of the file will look something like this :
  //   ********************************************************************************
  //   *           Electronic circuit simulation file generated by gSpiceUI           *
  //   *                         Version 0.9.93 (2015-04-03)                          *
  //   ********************************************************************************
  if(        m_osaNetLst.Item( 0 ).find_first_not_of( wxT('*') ) != wxString::npos ) ;
  else if( ! m_osaNetLst.Item( 1 ).Contains( APP_NAME )                            ) ;
  else if( ! m_osaNetLst.Item( 2 ).Contains( wxT("Version") )                      ) ;
  else if(   m_osaNetLst.Item( 3 ).find_first_not_of( wxT('*') ) != wxString::npos ) ;
  else                                  return( true );

  // Accept all lines beginning with an '*' as the title
  for( size_t sz1=0; sz1<m_osaNetLst.GetCount( ); sz1++ )
  {
    os1 = m_osaNetLst.Item( sz1 );
    if( os1.GetChar( 0 ) != wxT('*') ) break;
    m_osaTitle.Add( os1 );
  }

  return( true );
}

//**************************************************************************************************
// Extract any include directives.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoadIncludes( void )
{
  wxString  os1, os2;
  size_t    sz1;

  // Clear the includes string array object attribute
  m_osaIncludes.Clear( );

  // Need at least a title line, 2 components and an include directive
  if( m_osaNetLst.GetCount( ) < 4 ) return( true );

  // Scan circuit description for include directives
  for( sz1=0; sz1<m_osaNetLst.GetCount( ); sz1++ )
  {
    os1 = m_osaNetLst.Item( sz1 );

    if( bIsSubCkt( os1 ) ) continue;
    if( os1.IsEmpty( )   ) continue;

    os2 = os1;
    os2.MakeUpper( );

    if( os2.StartsWith( wxT(".INCLUDE ") ) ) m_osaIncludes.Add( os1 );
  }

  return( true );
}

//**************************************************************************************************
// Extract all the component description lines from the circuit description.
//
// A component line is identified if the first character in the line is an alpha character and its
// length is greater than 8. The following examples shows a component description of minimum length
// (a resistor connected to nodes 1 and 0 of value 9 Ohm) :
//
//   R1 1 0 9
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoadCpnts( void )
{
  wxString   os1, os2;
  size_t     sz1;
  Component  oCpnt;

  // Clear the components string array object attribute
  m_oaCpnts.Clear( );

  // Need at least a title line and 2 components
  if( m_osaNetLst.GetCount( ) < 3 ) return( false );

  // Reset the sub-circuit detection function
  os1 = wxT(".ENDS");
  bIsSubCkt( os1 );

  // Scan circuit description for components
  for( sz1=1; sz1<m_osaNetLst.GetCount( ); sz1++ )
  {
    // Retrieve the next line from the netlist
    os1 = m_osaNetLst.Item( sz1 );

    // Ignore all component definitions inside a sub-circuit block
    if( bIsSubCkt( os1 ) )    continue;

    // Determine if this line is a valid component description
    oCpnt = os1;
    if( ! oCpnt.bIsValid( ) ) continue;

    // A valid component description was found
    m_oaCpnts.Add( oCpnt );
  }

  // Circuit description must have components
  if( m_oaCpnts.IsEmpty( ) )         return( false );

  m_oaCpnts.Sort( &Component::iCompare );

  return( true );
}

//**************************************************************************************************
// Extract all the model description lines from the circuit description.
//
// A model description can consist of 1 or more lines and is identified if the line begins with
// ".MODEL ". Subsequent lines beginning with a '+' character are appended to the model description.
// The format is illustrated in the following example:
//
//   .MODEL CMOSN NMOS (LEVEL=2 LD=0.265073u TOX=418.0e-10
//   + NSUB=1.53142e+16 VTO=0.844345 KP=4.15964e-05 GAMMA=0.863074
//   + CJ=0.0003844 MJ=0.488400 CJSW=5.272e-10 MJSW=0.300200 PB=0.700000)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoadModels( void )
{
  wxString  osModel;
  wxString  os1, os2;
  size_t    sz1;

  // Clear the models string array object attribute
  m_osaModels.Clear( );

  // Need at least a title line, 2 components and a model description line
  if( m_osaNetLst.GetCount( ) < 4 ) return( true );

  // Reset sub-circuit detection function
  os1 = wxT(".ENDS");
  bIsSubCkt( os1 );

  // Scan circuit description for models
  for( sz1=0; sz1<m_osaNetLst.GetCount( ); sz1++ )
  {
    os1 = m_osaNetLst.Item( sz1 );

    if( bIsSubCkt( os1 ) )                     continue;
    if( os1.IsEmpty( ) && osModel.IsEmpty( ) ) continue;

    os2 = os1;
    os2.MakeUpper( );

    if( os2.StartsWith( wxT(".MODEL ") ) )
    { // First line of model description found
      if( ! osModel.IsEmpty( ) ) m_osaModels.Add( osModel );
      osModel = os1;
    }
    else if( os1.GetChar( 0 ) == wxT('+') )
    { // Intermediate line of model description found
      if( osModel.Length( ) > 1 ) osModel << wxT('\n') << os1;
    }
    else if( ! osModel.IsEmpty( ) )
    { // Last line of model description found
      m_osaModels.Add( osModel );
      osModel.Empty( );
    }
  }

  return( true );
}

//**************************************************************************************************
// Extract any sub-circuit descriptions from the circuit description (netlist).
//
// The format of a sub-circuit description is illustrated in the following example :
//
//   .SUBCKT CCTNAME 1 5
//   R1 1 2 1K
//   R2 2 3 2K
//   R3 3 4 3K
//   R4 4 5 4K
//   .ENDS CCTNAME
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoadSubCcts( void )
{
  wxString  osSubCct;
  wxString  os1, os2;
  size_t    sz1;

  // Clear the sub-circuit string array object attribute
  m_osaSubCcts.Clear( );

  // Need at least a title line, 2 components and a sub-circuit description containing 2 lines
  if( m_osaNetLst.GetCount( ) < 7 ) return( true );

  // Scan circuit description for sub-circuits
  for( sz1=0; sz1<m_osaNetLst.GetCount( ); sz1++ )
  {
    os1 = m_osaNetLst.Item( sz1 );

    if( os1.IsEmpty( ) && osSubCct.IsEmpty( ) ) continue;

    os2 = os1;
    os2.MakeUpper( );

    if(      os2.StartsWith( wxT(".SUBCKT ") ) )
    { // First line in sub-circuit description found
      osSubCct = os1;
    }
    else if( os2.StartsWith( wxT(".ENDS") ) )
    { // Last line in sub-circuit description found
      if( ! osSubCct.IsEmpty( ) )
      {
        osSubCct << wxT('\n') << os1;
        m_osaSubCcts.Add( osSubCct );
        osSubCct.Empty( );
      }
    }
    else if( ! osSubCct.IsEmpty( ) )
    { // Intermediate line in sub-circuit description found (ignoring all comments)
      if( os1[ 0 ] != wxT('*') ) osSubCct << wxT('\n') << os1;
    }
  }

  return( true );
}

//**************************************************************************************************
// Extract all nodes labels from the component array.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoadNodeLbls( void )
{
  wxString  os1;
  size_t    sz1, sz2;
  int       i1;

  // Clear the node lables string array object attribute
  m_osaNodeLbls.Clear( );

  // Check that there are some components
  if( m_oaCpnts.IsEmpty( ) )     return( false );

  for( sz1=0; sz1<m_oaCpnts.GetCount( ); sz1++ )
  {
    Component & roCpnt = m_oaCpnts.Item( sz1 );
    if( roCpnt.rosaGetNodes( ).IsEmpty( ) ) continue;

    for( sz2=0; sz2<roCpnt.rosaGetNodes( ).GetCount( ); sz2++ )
    {
      os1 = roCpnt.rosaGetNodes( ).Item( sz2 );
      if( m_osaNodeLbls.Index( os1 ) == wxNOT_FOUND )
        m_osaNodeLbls.Add( os1 );
    }
  }

  // Check that some node labels were collected
  if( m_osaNodeLbls.IsEmpty( ) ) return( false );

  // Remove the earth / ground nodes if present
  if( ( i1=m_osaNodeLbls.Index( wxT("0") ) )   != wxNOT_FOUND ) m_osaNodeLbls.RemoveAt( i1 );
  if( ( i1=m_osaNodeLbls.Index( wxT("GND") ) ) != wxNOT_FOUND ) m_osaNodeLbls.RemoveAt( i1 );

  // Sort the list of node labels
  m_osaNodeLbls.Sort( &iStrCmpNode );

  return( true );
}

//**************************************************************************************************
// Extract the schematic file name/s the netlist was derived from.
//
// When gSpiceUI imports a netlist from schematic file/s it places a reference to the schematic
// file/s near the top of the netlist file as follows :
//   "* Schematics : <path>/circuit1.sch <path>/circuit2.sch"
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoadSchemFiles( void )
{
  wxString  os1, os2;
  size_t    sz1;

  // Scan the netlist for a reference any schematic file name/s it was derived from
  for( sz1=0; sz1<m_osaNetLst.GetCount( ); sz1++ )
  {
    os1 = m_osaNetLst.Item( sz1 );

    if( os1.IsEmpty( )   )                              continue;
    if( os1.GetChar( 0 ) != wxT('*') )                  continue;
    if( os1.StartsWith( wxT("* Schematic : "), &os2 ) )
    {
      // Set the schematic file name/s
      if( ! bSetSchemFiles( os2 ) ) return( false );
      break;
    }
  }

  return( true );
}

//**************************************************************************************************
// This function indicates whether a line in a sequence of lines falls within a sub-circuit
// definition block.
//
// Argument List :
//   A line in a sequence of lines to be tested
//
// Return Values :
//   true  - The line does    falls within a su-circuit definition block
//   false - The line doesn't falls within a su-circuit definition block

bool  NetList::bIsSubCkt( wxString & roLine )
{
  static  bool      bIsSubCkt=false;  // This flag's state must be preserved between calls
          wxString  os1;

  os1 = roLine;
  os1.MakeUpper( );
  if( os1.StartsWith( wxT(".SUBCKT") ) ) bIsSubCkt = true;
  if( os1.StartsWith( wxT(".ENDS") ) )   bIsSubCkt = false;

  return( bIsSubCkt );
}

//**************************************************************************************************
// Makes the object and its attributes empty and frees memory allocated to it.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bClear( void )
{
  m_osaNetLst  .Clear( );

  m_osaTitle   .Clear( );
  m_osaIncludes.Clear( );
  m_oaCpnts    .Clear( );
  m_osaModels  .Clear( );
  m_osaSubCcts .Clear( );

  m_osaNodeLbls.Clear( );

  m_bIsValid = false;

  return( true );
}

//**************************************************************************************************
// Do the current object attributes constitute a valid netlist?
//
// Return Values:
//   true  - Valid
//   false - Not valid

bool  NetList::bValidate( void )
{
  m_bIsValid = true;

  // Check that there is a title
  if( m_osaTitle.IsEmpty( ) )     { m_bIsValid = false; return( false ); }

  // Check that there are at least two components defined
  if( m_oaCpnts.GetCount( ) < 2 ) { m_bIsValid = false; return( false ); }

  return( true );
}

//**************************************************************************************************
// Load (or reload) a circuit description from file.
//
// Argument List :
//   rosFName - The name of the file to be loaded (if this string is empty it's a reload operation)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoadFile( const wxString & rosFName )
{
  wxString  os1;

  // Is this a load or reload operation?
  if( ! rosFName.IsEmpty( ) )
       { if( ! bSetLoadFile( rosFName )          )        return( false ); }
  else { if( m_ofnLoadFile.GetFullPath( ).IsEmpty( ) )    return( false ); }

  bClear( );  // Clear the object attributes

  // Open the netlist file
  wxTextFile  oFileCct( m_ofnLoadFile.GetFullPath( ) );
  oFileCct.Open( );
  if( ! oFileCct.IsOpened( )       ) { oFileCct.Close( ); return( false ); }
  if( oFileCct.GetLineCount( ) < 3 ) { oFileCct.Close( ); return( false ); }

  // Load the netlist file contents into the netlist string array
  for( os1=oFileCct.GetFirstLine(); !oFileCct.Eof(); os1=oFileCct.GetNextLine() )
    m_osaNetLst.Add( os1 );

  // The following line picks up the last line in the file if it has no line termination character
  if( os1.Length( ) > 0 ) m_osaNetLst.Add( os1 );

  oFileCct.Close( ); // Close the netlist file

  return( true );
}

//**************************************************************************************************
// Save (or resave) the circuit description to file.
//
// Argument List :
//   rosFName - The name of the file to be saved
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bSaveFile( const wxString & rosFName )
{
  size_t  sz1;

  // Is this a save or resave?
  if( ! rosFName.IsEmpty( ) )
       { if( ! bSetSaveFile( rosFName ) ) return( false ); }
  else { if( ! m_ofnSaveFile.IsOk( )    ) return( false ); }

  // Open the file
  wxTextFile  oFileCct( m_ofnSaveFile.GetFullPath( ) );
  if( oFileCct.Exists( ) )
       { if( ! oFileCct.Open( )   )       return( false ); }
  else { if( ! oFileCct.Create( ) )       return( false ); }

  // Clear the file if it contains lines
  for( sz1=oFileCct.GetLineCount( ); sz1>0; sz1-- )
    oFileCct.RemoveLine( 0 );

  // Save the contents of the netlist string array to file
  for( sz1=0; sz1<m_osaNetLst.GetCount( ); sz1++ )
    oFileCct.AddLine( m_osaNetLst.Item( sz1 ) );

  // Save the changes to disk
  oFileCct.Write( );
  oFileCct.Close( );

  return( true );
}

//**************************************************************************************************
// Load the various object attributes with information from the netlist string array : m_osaNetLst.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bLoad( void )
{
  // Does the netlist string array contain anything?
  if( m_osaNetLst.IsEmpty( ) )
    m_bIsValid = false;
  else
  {
    m_bIsValid = true;

    // Attempt to extract the circuit description information
    if( ! bLoadTitle     ( ) ) m_bIsValid = false;
    if( ! bLoadIncludes  ( ) ) m_bIsValid = false;
    if( ! bLoadCpnts     ( ) ) m_bIsValid = false;
    if( ! bLoadModels    ( ) ) m_bIsValid = false;
    if( ! bLoadSubCcts   ( ) ) m_bIsValid = false;
    if( ! bLoadNodeLbls  ( ) ) m_bIsValid = false;
    if( ! bLoadSchemFiles( ) ) m_bIsValid = false;
  }

  return( m_bIsValid );
}

//**************************************************************************************************
// Save the various object attributes information to the netlist string array : m_osaNetLst.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bSave( void )
{
  wxString  os1, os2;
  size_t    sz1;

  // Clear the netlist string array contents and frees memory
  m_osaNetLst.Clear( );

  // Save the gSpiceUI banner lines eg. :
  //   ********************************************************************************
  //   *           Electronic circuit simulation file generated by gSpiceUI           *
  //   *                         Version 0.9.93 (2015-04-03)                          *
  //   ********************************************************************************
  os1.Empty( );
  os1.append( 78, wxT('*') );
  m_osaNetLst.Add( os1 );

  os2 = wxString( wxT("Electronic circuit simulation file generated by ") ) + APP_NAME;
  os2.insert( (size_t) 0, (os1.Len( ) - os2.Len( ) - 2)/2, wxT(' ') );
  os2.append(              os1.Len( ) - os2.Len( ) - 2   , wxT(' ') );
  m_osaNetLst.Add( wxT('*') + os2 + wxT('*') );

  os2 = wxString( wxT("Version ") ) + APP_VERSION + wxT(" (") + APP_DATE + wxT(")");
  os2.insert( (size_t) 0, (os1.Len( ) - os2.Len( ) - 2)/2, wxT(' ') );
  os2.append(              os1.Len( ) - os2.Len( ) - 2   , wxT(' ') );
  m_osaNetLst.Add( wxT('*') + os2 + wxT('*') );

  m_osaNetLst.Add( os1 );
  m_osaNetLst.Add( wxT("") );

  // Save the schematic file name/s if specified
  if( ! m_oaSchemFiles.IsEmpty( ) )
  {
    os1 = wxT("* Schematic : ") + rosGetSchemFiles( );
    m_osaNetLst.Add( os1 );
    m_osaNetLst.Add( wxT("") );
  }

  // Save any pre-existing title line/s
  if( ! m_osaTitle.IsEmpty( ) )
  {
    for( sz1=0; sz1<m_osaTitle.GetCount( ); sz1++ )
      m_osaNetLst.Add( m_osaTitle[ sz1 ] );
    m_osaNetLst.Add( wxT("") );
  }

  // Save the include directives
  if( ! m_osaIncludes.IsEmpty( ) )
  {
    m_osaNetLst.Add( wxT("********** Include Directives **********") );
    m_osaNetLst.Add( wxT("") );
    for( sz1=0; sz1<m_osaIncludes.GetCount( ); sz1++ )
      m_osaNetLst.Add( m_osaIncludes[ sz1 ] );
    m_osaNetLst.Add( wxT("") );
  }

  // Save the component definition lines
  m_osaNetLst.Add( wxT("********** Component Definitions **********") );
  m_osaNetLst.Add( wxT("") );
  for( sz1=0; sz1<m_oaCpnts.GetCount( ); sz1++ )
    m_osaNetLst.Add( m_oaCpnts[ sz1 ].rosGetString( ) );
  m_osaNetLst.Add( wxT("") );

  // Save the sub-circuit definitions
  if( ! m_osaSubCcts.IsEmpty( ) )
  {
    m_osaNetLst.Add( wxT("********** Sub-Circuit Definitions **********") );
    m_osaNetLst.Add( wxT("") );
    for( sz1=0; sz1<m_osaSubCcts.GetCount( ); sz1++ )
    {
      m_osaNetLst.Add( m_osaSubCcts[ sz1 ] );
      m_osaNetLst.Add( wxT("") );
    }
  }

  // Save the model definition lines
  if( ! m_osaModels.IsEmpty( ) )
  {
    m_osaNetLst.Add( wxT("********** Model Definitions **********") );
    m_osaNetLst.Add( wxT("") );
    for( sz1=0; sz1<m_osaModels.GetCount( ); sz1++ )
    {
      m_osaNetLst.Add( m_osaModels[ sz1 ] );
      m_osaNetLst.Add( wxT("") );
    }
  }

  // Save the circuit description terminator
  m_osaNetLst.Add( wxT(".END") );

  return( true );
}

//**************************************************************************************************
// Set the load (netlist) file name.
//
// Argument List :
//   rosFName - The load file name
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bSetLoadFile( const wxString & rosFName )
{
  wxFileName  ofn1;

  ofn1 = rosFName;

  // Is the file name valid and does it exist?
  if( ! ofn1.IsOk( ) )       return( false );
  if( ! ofn1.FileExists( ) ) return( false );

  m_ofnLoadFile = ofn1;

  return( true );
}

//**************************************************************************************************
// Set the save (netlist) file name.
//
// Argument List :
//   rosFName - The save file name
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bSetSaveFile( const wxString & rosFName )
{
  wxFileName  ofn1;

  ofn1 = rosFName;

  // Is the file name valid?
  if( ! ofn1.IsOk( ) ) return( false );

  m_ofnSaveFile = ofn1;

  return( true );
}

//**************************************************************************************************
// Set the schematic file name/s from which the netlist file was derived.
//
// Argument List :
//   rosaFName - The schematic file name/s
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bSetSchemFiles( const wxArrayString & rosaFNames )
{
  wxFileName  ofn1;
  size_t      sz1;

  // Clear the schematic file name/s attribute
  m_oaSchemFiles.Clear( );

  for( sz1=0; sz1<rosaFNames.GetCount( ); sz1++ )
  {
    ofn1 = rosaFNames.Item( sz1 );

    // Is the file name valid and does it exist?
    if( ! ofn1.IsOk( ) )       continue;
    if( ! ofn1.FileExists( ) ) continue;

    m_oaSchemFiles.Add( ofn1 );
  }

  return( true );
}

//**************************************************************************************************
// Set the schematic file name/s from which the netlist was derived.
//
// Argument List :
//   rosFName - The schematic file name/s
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NetList::bSetSchemFiles( const wxString & rosFNames )
{
  return( bSetSchemFiles( roasStrToArr( rosFNames ) ) );
}

//**************************************************************************************************
// Return a reference to an wxArrayString object containing a list of the schematic file names.
//
// Return Values :
//   Success - The schematic file names
//   Failure - An empty wxArrayString object

const wxArrayString & NetList::roasGetSchemFiles( void )
{
  static  wxArrayString  oas1;
          size_t         sz1;

  oas1.Empty( );

  for( sz1=0; sz1<m_oaSchemFiles.GetCount( ); sz1++ )
    oas1.Add( m_oaSchemFiles.Item( sz1 ).GetFullPath( ) );

  return( oas1 );
}

//**************************************************************************************************
// Return a reference to a wxString object containing a list of the schematic file names.
//
// Return Values :
//   Success - The schematic file names
//   Failure - An empty wxString object

const wxString & NetList::rosGetSchemFiles( void )
{
  static  wxString  os1;
          size_t    sz1;

  os1.Empty( );

  for( sz1=0; sz1<m_oaSchemFiles.GetCount( ); sz1++ )
  {
    if( ! os1.IsEmpty( ) ) os1 << wxT(' ');
    os1 << m_oaSchemFiles.Item( sz1 ).GetFullPath( );
  }

  return( os1 );
}

//**************************************************************************************************
// Get a reference to the Component object in m_oaCpnts with a specified name.
//
// Argument List :
//   rosName - The component name (label)
//
// Return Values :
//   Success - The component reference in m_oaCpnts
//   Failure - An empty Component object

const Component & NetList::roGetCpnt( const wxString & rosName )
{
  static  Component  oCpntEmpty;
          wxString   os1, os2;
          size_t     sz1;

  if( rosName  .IsEmpty( ) ) return( oCpntEmpty );
  if( m_oaCpnts.IsEmpty( ) ) return( oCpntEmpty );

  for( sz1=0; sz1<m_oaCpnts.GetCount( ); sz1++ )
    if( m_oaCpnts.Item( sz1 ).rosGetName( ) == rosName )
      return( m_oaCpnts.Item( sz1 ) );

  return( oCpntEmpty );
}

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

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

  os1 = m_ofnLoadFile.GetFullPath( );
  std::cout << rosPrefix.mb_str( ) << "m_ofnLoad        : " << os1.mb_str( ) << '\n';
  os1 = m_ofnSaveFile.GetFullPath( );
  std::cout << rosPrefix.mb_str( ) << "m_ofnSave        : " << os1.mb_str( ) << '\n';
  os1 = rosGetSchemFiles( );
  std::cout << rosPrefix.mb_str( ) << "m_osSchemFiles   : " << os1.mb_str( ) << '\n';

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

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

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

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

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

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

  std::cout << rosPrefix.mb_str( ) << "m_bIsValid       : "
            << ( m_bIsValid ? "true" : "false") << '\n';
}

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

#ifdef TEST_NETLIST

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 > 3 )                    { 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  Netlist Class Test Utility"
       << "\n  Version 1.03 (2016-09-27)\n";

  // Create a NetList object
  NetList  oNetLst;

  cout << "\nLoad the netlist file : ";
  if( oNetLst.bLoadFile( os1 ) ) cout << "Success";
  else                           cout << "Failure";
  cout << "\n";

  cout << "\ntNetLst.Print( ) :\n";
  oNetLst.Print( );

  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] [FILE]"
       << "\nOptions : -h   : Print usage (this message)"
       << "\n          FILE : A circuit description (NetList) file"
       << "\n\n";
}

#endif // TEST_NETLIST

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