//**************************************************************************************************
//                                         PrcGNetList.cpp                                         *
//                                        -----------------                                        *
// Started     : 2004-01-28                                                                        *
// Last Update : 2018-10-28                                                                        *
// 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 "PrcGNetList.hpp"

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

wxArrayString  PrcGNetList::m_oasGuileProcs;

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

PrcGNetList::PrcGNetList( void ) : PrcBase( wxPROCESS_REDIRECT )
{
  // Load the Guile procedure array with some default values
  m_oasGuileProcs.Add( wxT("") );
  m_oasGuileProcs.Add( GNETLST_GUILE_PROC );
  m_szGuileProc = 0;

  // Clear the object attributes
  bClear( );

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

#ifndef TEST_APPPRCGNETLIST
  // Attempt to set and find the binary file
  bSetEdaSuite( g_oConfig.eGetEdaSuite( ) );

  // Set the Guile procedure
  bSetGuileProc( g_oConfig.rosGetGuileProc( ) );
#endif // TEST_PRCGNETLIST
}

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

PrcGNetList::~PrcGNetList( )
{
}

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

bool  PrcGNetList::bClear( void )
{
  bool  bRtn=true;

  if( bSetGuileProc ( wxT("") ) ) bRtn = false;
  if( bSetSchemFiles( wxT("") ) ) bRtn = false;
  if( bSetNetLstFile( wxT("") ) ) bRtn = false;

  return( bRtn );
}

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

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

  // Temporarily record the currently selected Guile procedure
  osGProc = m_oasGuileProcs.Item( m_szGuileProc );

  m_oasGuileProcs.Clear( );        // Clear the list of possible Guile procedures
  m_oasGuileProcs.Add( wxT("") );  // First location is the default and is deliberately left empty

  os1 = roGetBinFile( ).GetFullPath( ) + wxT(" --list-backends");
  if( wxExecute( os1, 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( ) ) m_oasGuileProcs.Add( os1 );
      }
    }
  }
  else m_oasGuileProcs.Add( GNETLST_GUILE_PROC ); // Default Guile procedure

  // Now attempt to reinstate the previous Guile procedure selection otherwise use the default
  if( ! bSetGuileProc( osGProc ) ) bSetGuileProc( GNETLST_GUILE_PROC );
}

//**************************************************************************************************
// Set the EDA tool suite..
//
// Argument List :
//   eEDA - The enumerated type specifier for the desired tool suite
//
// Return Values :
//   true  - Success
//   false - Failure

bool  PrcGNetList::bSetEdaSuite( eTypeEDA eEDA )
{
  bool  bRtn;

  switch( eEDA )
  {
    case eEDA_LEPTON : bRtn = bSetBinFile( BIN_LNETLIST ); break;
    case eEDA_GEDA   : bRtn = bSetBinFile( BIN_GNETLIST ); break;
    default          : bRtn = FALSE;
  }

  InitGuileProcs( );

  return( bRtn );
}

//**************************************************************************************************
// 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;

  if( m_oasGuileProcs.IsEmpty( ) ) return( false );

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

  m_szGuileProc = (size_t) i1;

  return( true );
}

//**************************************************************************************************
// 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_oNameNetList.Clear( );

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

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

  return( true );
}

//**************************************************************************************************
// Get the EDA tool suite.
//
// Return Values :
//   Success - The enumerate type for the EDA tool suite
//   Failure - eEDA_NONE

eTypeEDA  PrcGNetList::eGetEdaSuite( void )
{
  wxString  os1;

  os1 = roGetBinFile( ).GetFullName( );

  if( os1 == BIN_LNETLIST ) return( eEDA_LEPTON );
  if( os1 == BIN_GNETLIST ) return( eEDA_GEDA   );
  else                      return( eEDA_NONE   );
}

//**************************************************************************************************
// 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=wxEmptyString;

  if( szIndex == GNETLST_CUR_PROC ) szIndex = m_szGuileProc;

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

  return( m_oasGuileProcs[ szIndex ] );
}

//**************************************************************************************************
// 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 ] );
}

//**************************************************************************************************
// 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;
  wxFileName  ofn1;
  wxString    os1;
  size_t      sz1;

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

  // Set the various gnetlist modes
#ifndef TEST_APPPRCGNETLIST
  if(   g_oConfig.bGetVerboseMode( ) ) osArgLst << wxT("--verbose ");
  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 ");
#endif // TEST_PRCGNETLIST
  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 output file name
#ifndef __WXMSW__
  osArgLst << wxT("-o ") << rosEscSpaceChrs( m_oNameNetList.GetFullPath( ) );
#else
  os1 = m_oNameNetLst.GetFullPath( );
  os1.Replace( wxT("\\"), wxT("/"), true );
  osArgLst << wxT("-o ") << os1;
#endif // __WXMSW__

  // Append input file name/s
#ifndef __WXMSW__
  for( sz1=0; sz1<m_oaNameSchems.GetCount( ); sz1++ )
    osArgLst << wxT(" ") << rosEscSpaceChrs( m_oaNameSchems.Item( sz1 ).GetFullPath( ) );
#else
  osArgLst << wxT(" -- ");  // Treat all remaining arguments as schematic filenames
  for( sz1=0; sz1<m_oaNameSchems.GetCount( ); sz1++ )
  {
    os1 = m_oaNameSchems.Item( sz1 ).GetFullPath( );
    os1.Replace( wxT("\\"), wxT("/"), true );
    osArgLst << wxT(" ") << os1;
  }
#endif // __WXMSW__

  // Set the argument list
  bSetArgLst( osArgLst );

  // Set the working directory
  bSetCwd( roGetSchemFile( ).GetPath( ) );

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

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

  return( true );
}

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

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

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

  // size_t  m_szGuileProc
  std::cout << rosPrefix.mb_str( ) << "m_szGuileProc      : " << m_szGuileProc << '\n';

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

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

  // wxFileName  m_oNameNetLst
  std::cout << rosPrefix.mb_str( ) << "m_oNameNetList     : "
            << m_oNameNetList.GetFullPath( ).mb_str( ) << '\n';
}

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