//**************************************************************************************************
//                                         PrcGNetList.cpp                                         *
//                                        -----------------                                        *
// Started     : 2004-01-28                                                                        *
// Last Update : 2016-10-30                                                                        *
// Copyright   : (C) 2004-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 "PrcGNetList.hpp"

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

wxArrayString  PrcGNetList::m_oasGuileProcs;

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

PrcGNetList::PrcGNetList( void ) : PrcBase( wxPROCESS_REDIRECT )
{
  // Clear the object attributes
  bClear( );

  // Attempt to set and find the gnetlist binary
  bSetBinFile( BIN_GNETLIST );

  // Set the log file name
  bSetLogFile( GNETLST_LOG_FILE );
}

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

PrcGNetList::~PrcGNetList( )
{
}

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

bool  PrcGNetList::bClear( void )
{
  bSetSchemFiles( wxT("") );
  bSetNetLstFile( wxT("") );
  bSetGuileProc ( wxT("") );

  // Initialize the array of Guile procedure names and set the default
  InitGuileProcs( );

  return( true );
}

//**************************************************************************************************
// Initialize the array of possible Guile procedure names.

void  PrcGNetList::InitGuileProcs( void )
{
  wxArrayString  oas1;
  wxString       os1, os2;
  size_t         sz1;

  m_oasGuileProcs.Clear( );
  m_oasGuileProcs.Add( wxT("") );

  if( wxExecute( wxT("gnetlist --list-backends"), oas1 ) == 0 )
  {
    if( oas1.GetCount( ) > 1 )
    {
      for( sz1=1; sz1<oas1.GetCount( ); sz1++ )
      {
        os1 = oas1.Item( sz1 );
        os1.Trim( true);    // Remove white-space, etc. from the right end of the string
        os1.Trim( false );  // Remove white-space, etc. from the left  end of the string
        if( !os1.IsEmpty( ) && os1!=os2 ) m_oasGuileProcs.Add( os1 );
        os2 = os1;
        os1.Empty( );
      }
    }
  }
  else m_oasGuileProcs.Add( GNETLST_GUILE_PROC ); // Default Guile procedure

  bSetGuileProc( GNETLST_GUILE_PROC );
}

//**************************************************************************************************
// Set the schematic file name/s.
//
// Argument List :
//   rosFileNames - A string containing the full path and file name/s
//
// Return Values :
//   true  - Success
//   false - Failure

bool  PrcGNetList::bSetSchemFiles( const wxString & rosFileNames )
{
  wxStringTokenizer  ostk1;
  wxArrayString      osa1;

  ostk1.SetString( rosFileNames );
  while( ostk1.HasMoreTokens( ) ) osa1.Add( ostk1.GetNextToken( ) );

  return( bSetSchemFiles( osa1 ) );
}

//**************************************************************************************************
// Set the schematic file name/s.
//
// Argument List :
//   roasFileNames - A string array containing the full path and file name/s
//
// Return Values :
//   true  - Success
//   false - Failure

bool  PrcGNetList::bSetSchemFiles( const wxArrayString & roasFileNames )
{
  wxFileName  ofn1;
  size_t      sz1;

  // Clear the current list of schematic files
  m_oaNameSchems.Clear( );

  // Check the argument is empty
  if( roasFileNames.IsEmpty( ) )                                return( true );

  // Add the new schematic file name/s to the list
  for( sz1=0; sz1<roasFileNames.GetCount( ); sz1++ )
  {
    ofn1 = roasFileNames.Item( sz1 );
    if( ofn1.GetPath( ).IsEmpty( ) ) ofn1.SetPath( wxT(".") );

    if( ! ofn1.IsOk( )       ) continue;
    if( ! ofn1.FileExists( ) ) continue;

    m_oaNameSchems.Add( ofn1 );
  }

  // Check that at least one schematic file name was accepted
  if( m_oaNameSchems.IsEmpty( ) )                               return( false );

  // Set the log file path
  ofn1 = roGetLogFile( );
  ofn1.SetPath( m_oaNameSchems.Item( 0 ).GetPath( ) );
  bSetLogFile( ofn1.GetFullPath( ) );

  // Check if any of the schematic files were invalid
  if( m_oaNameSchems.GetCount( ) != roasFileNames.GetCount( ) ) return( false );

  return( true );
}

//**************************************************************************************************
// Set the full netlist file name.
//
// Argument List :
//   rosFileName - A string containing the full path and file name
//                 (If rosFName = "use-schem-name" the netlist name is to be generated from the
//                  schematic file name)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  PrcGNetList::bSetNetLstFile( const wxString & rosFileName )
{
  wxFileName  ofn1;

  // Set the netlist name
  if( ! rosFileName.IsEmpty( ) )
  {
    if( rosFileName == GNETLST_USE_SCHEM )
    { // Generate the netlist file name from the schematic file name
      ofn1 = roGetSchemFile( );
      ofn1.SetExt( wxT("ckt") );
    }
    else ofn1 = rosFileName;

    // Set the netlist path if it hasn't been set
    if( ofn1.GetPath( ).IsEmpty( ) ) ofn1.SetPath( wxT(".") );
    ofn1.Normalize( );  // Make path absolute (ie. no ".." or ".") and expand environment variables

    // Check that the file name is OK
    if( ! ofn1.IsOk( ) ) return( false );
  }
  else m_oNameNetLst.Clear( );

  // Set the netlist file path and name
  m_oNameNetLst = ofn1;

  // Set the log file path
  ofn1 = roGetLogFile( );
  ofn1.SetPath( m_oNameNetLst.GetPath( ) );
  bSetLogFile( ofn1.GetFullPath( ) );

  return( true );
}

//**************************************************************************************************
// Set the Guile procedure to be used to import the schematic file.
//
// Argument List :
//   rosGProc - The Guile procedure name
//
// Return Values :
//   true  - Success
//   false - Failure

bool  PrcGNetList::bSetGuileProc( const wxString & rosGProc )
{
  int  i1;

  i1 = m_oasGuileProcs.Index( rosGProc );
  if( i1 == wxNOT_FOUND ) return( false );

  m_szGuileProc = (size_t) i1;

  return( true );
}

//**************************************************************************************************
// Get a schematic file from the list of schematic files.
//
// Argument List :
//   szIndex - A zero based index to the file name
//
// Return Values :
//   Success - The procedure name
//   Failure - An empty file name

const wxFileName & PrcGNetList::roGetSchemFile( size_t szIndex )
{
  static  wxFileName  oFnEmpty;

  if( szIndex >= m_oaNameSchems.GetCount( ) ) return( oFnEmpty );

  return( m_oaNameSchems[ szIndex ] );
}

//**************************************************************************************************
// Get a Guile procedure from the list of procedure names.
//
// Argument List :
//   szIndex - A zero based index to the procedure name
//             (If szIndex = CUR_SEL_PROC return currently selected procedure)
//
// Return Values :
//   Success - The procedure name
//   Failure - An empty string

const wxString & PrcGNetList::rosGetGuileProc( size_t szIndex )
{
  static  wxString  osEmpty;

  if( szIndex == GNETLST_CUR_PROC ) szIndex = m_szGuileProc;

  if( szIndex >= m_oasGuileProcs.GetCount( ) ) return( osEmpty );

  return( m_oasGuileProcs[ szIndex ] );
}

//**************************************************************************************************
// Make a netlist file from a schematic file.
//
// (Eg. using the following: gnetlist -v -g spice-sdb -o test.ckt test.sch)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  PrcGNetList::bExec( void )
{
  wxString  osArgLst;
  wxString  os1;
  size_t    sz1;

  // Test file names needed by this function
  if( ! bBinExists( ) )           return( false );
  if( m_oaNameSchems.IsEmpty( ) ) return( false );
  if( ! m_oNameNetLst.IsOk( ) )
    if( ! bSetNetLstFile( ) )     return( false );

  // Set the various gnetlist modes
  if(   g_oConfig.bGetVerboseMode( ) ) osArgLst << wxT("-v ");
  if( ! g_oConfig.bGetIncludeMode( ) ) osArgLst << wxT("-O include_mode ");
  if(   g_oConfig.bGetEmbedMode( )   ) osArgLst << wxT("-O embedd_mode ");
  if(   g_oConfig.bGetNoMungeMode( ) ) osArgLst << wxT("-O nomunge_mode ");
  osArgLst << wxT("-O sort_mode ");

  // Specify the guile procedure name to be used
  osArgLst << wxT("-g ");
  os1 = m_oasGuileProcs.Item( m_szGuileProc );
  if( os1.IsEmpty( ) ) os1 = GNETLST_GUILE_PROC;
  osArgLst << os1 << wxT(' ');

  // Append input and output file names
  osArgLst << wxT("-o ") << m_oNameNetLst.GetFullPath( );
  for( sz1=0; sz1<m_oaNameSchems.GetCount( ); sz1++ )
    osArgLst << wxT(" ") << m_oaNameSchems.Item( sz1 ).GetFullPath( );
  bSetArgLst( osArgLst );

  // Set the working directory
  os1 = roGetSchemFile( ).GetPath( );
  wxSetWorkingDirectory( os1 );

  // Execute the process
  if( ! PrcBase::bExecAsync( ) )  return( false );

  // Capture the process output
  if( ! bLogOutput( ) )           return( false );

  return( true );
}

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