//**************************************************************************************************
//                                         CmdLinePcr.cpp                                          *
//                                        ----------------                                         *
// Started     : 2005-02-22                                                                        *
// Last Update : 2018-10-22                                                                        *
// Copyright   : (C) 2005-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 "CmdLinePcr.hpp"

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

CmdLinePcr::CmdLinePcr( void ) : wxCmdLineParser( )
{
  bClear( );

  m_iArgC   = -1;
  m_ppsArgV = NULL;
}

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

CmdLinePcr::~CmdLinePcr( )
{
}

//**************************************************************************************************
// Process command line option -a : analysis page specifier.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_a( int * piArg )
{
  wxString  osArg;

  ( *piArg )++;

  if( *piArg >= m_iArgC )
  {
    std::cout << "Missing analysis type specifier.\n";
    return( false );
  }

  osArg = m_ppsArgV[ *piArg ];

  m_eAnalysis = eStrToEnumCmd( osArg );

  if( m_eAnalysis == eCMD_NONE )
  {
    std::cout << "Analysis type invalid or not supported : "
              << wxString( m_ppsArgV[ *piArg ] ).mb_str( ) << '\n';
    return( false );
  }

  return( true );
}

//**************************************************************************************************
// Process command line option -c : Rebuild/clean the configuration file.
//
// Note : This function must be called twice to actually rebuild/clean the configuration file.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_c( int * piArg __attribute__ ((unused)) )
{
  // Signify that the configuration file needs to be rebuilt/cleaned
  m_bCleanCfgFile = true;

  return( true );
}

//**************************************************************************************************
// Process command line option -d : Enable debug mode. Print debug info. to the console during
//                                  program execution.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_d( int * piArg __attribute__ ((unused)) )
{
  g_bDebug = true;

  std::cout << "\nDEBUG : Debug mode enabled\n";

  return( true );
}

//**************************************************************************************************
// Process command line option -e : EDA tool suite specifier.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_e( int * piArg )
{
  wxString  osArg;

  ( *piArg )++;

  if( *piArg >= m_iArgC )
  {
    std::cout << "Missing EDA tool suite specifier.\n";
    return( false );
  }

  osArg = m_ppsArgV[ *piArg ];
  if( osArg.Len( ) < 2 ) osArg = wxT("None");

  if(      osArg.CmpNoCase( rosEnumEdaToStr( eEDA_LEPTON ).Left( osArg.Len( ) ) ) == 0 )
    m_eEdaSuite = eEDA_LEPTON;
  else if( osArg.CmpNoCase( rosEnumEdaToStr( eEDA_GEDA   ).Left( osArg.Len( ) ) ) == 0 )
    m_eEdaSuite = eEDA_GEDA;
  else
  {
    std::cout << "Invalid EDA tool suite specifier : "
              << wxString( m_ppsArgV[ *piArg ] ).mb_str( ) << '\n';
    return( false );
  }

  return( true );
}

//**************************************************************************************************
// Process command line option -g : Guile procedure used when importing schematic file/s.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_g( int * piArg )
{
  PrcGNetList  oGNetList;
  wxString     osArg;

  // Check that there are more arguments and that the next is not an option
  if( *piArg < (m_iArgC-1) )
  {
    osArg = m_ppsArgV[ (*piArg)+1 ];

    if( osArg.GetChar( 0 ) != '-' )
    {
      // Check if the argument is a valid Guile procedure name
      if( oGNetList.bSetGuileProc( osArg ) )
        m_osGuileProc = osArg;
      else
      { // The argument isn't a Guile procedure so is it a file name? If it is a file name assume
        // it's a schematic file and use the default Guile procedure.
        wxRegEx  oRegEx( wxT("[./]") );
        if( ! oRegEx.Matches( osArg ) )
        {
          std::cout << "Invalid Guile procedure name : " << osArg.mb_str( ) << '\n';
          return( false );
        }
        m_osGuileProc = GNETLST_GUILE_PROC;
      }
    }

    // Increment argument pointer
    ( *piArg )++;
  }
  else // Use the default value
    m_osGuileProc = GNETLST_GUILE_PROC;

  return( true );
}

//**************************************************************************************************
// Process command line option -h : display usage and exit.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_h( int * piArg )
{
  wxString  os1;

  ( *piArg )++;

  if( *piArg == m_iArgC ) Usage( );
  else                    TooManyArgs( *piArg );

  return( false );
}

//**************************************************************************************************
// Process command line option -r : specify a configuration file.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_r( int * piArg )
{
  wxFileName  ofn1;

  ( *piArg )++;

  // Check that there are enough arguments
  if( *piArg >= m_iArgC )
  {
    std::cout << "Missing configuration file name.\n";
    return( false );
  }

  // Check the validity of the file name
  ofn1 = wxString( m_ppsArgV[ *piArg ], *wxConvCurrent );
  if( ! ofn1.IsOk( ) )
  {
    std::cout << "Invalid file name : " << m_ppsArgV[ *piArg ] << '\n';
    return( false );
  }
  if( ! ofn1.FileExists( ) )
  {
    std::cout << "File doesn't exist : " << m_ppsArgV[ *piArg ] << '\n';
    return( false );
  }
  if( ofn1.IsRelative( ) ) ofn1.MakeAbsolute( );

  m_osConfigFile = ofn1.GetFullPath( );

  return( true );
}

//**************************************************************************************************
// Process command line option -s : simulator engine specifier.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_s( int * piArg )
{
  wxString  osArg;

  ( *piArg )++;

  if( *piArg >= m_iArgC )
  {
    std::cout << "Missing simulator engine specifier.\n";
    return( false );
  }

  osArg = m_ppsArgV[ *piArg ];
  if( osArg.Len( ) < 2 ) osArg = wxT("None");

  if(      osArg.CmpNoCase( rosEnumEngToStr( eSIMR_NGSPICE ).Left( osArg.Len( ) ) ) == 0 )
    m_eSimEng = eSIMR_NGSPICE;
  else if( osArg.CmpNoCase( rosEnumEngToStr( eSIMR_GNUCAP  ).Left( osArg.Len( ) ) ) == 0 )
    m_eSimEng = eSIMR_GNUCAP;
  else
  {
    std::cout << "Invalid simulator engine specifier : "
              << wxString( m_ppsArgV[ *piArg ] ).mb_str( ) << '\n';
    return( false );
  }

  return( true );
}

//**************************************************************************************************
// Process command line option -v : display app. version and exit.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_v( int * piArg )
{
  wxString  os1;

  ( *piArg )++;

  if( *piArg == m_iArgC )
  {
    os1 << APP_NAME << wxT(", Version ") << APP_VERSION << wxT(" (") << APP_DATE << wxT(")\n");
    std::cout << os1.mb_str( );
  }
  else TooManyArgs( *piArg );

  return( false );
}

//**************************************************************************************************
// Process command line option -w : waveform viewer specifier.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcOption_w( int * piArg )
{
  wxString  osArg;

  ( *piArg )++;

  if( *piArg >= m_iArgC )
  {
    std::cout << "Missing waveform data viewer utility specifier.\n";
    return( false );
  }

  osArg = m_ppsArgV[ *piArg ];

  m_eDataViewer = eStrToEnumVwr( osArg );

  if( m_eDataViewer == eVIEW_NONE )
  {
    std::cout << "Invalid waveform data viewer utility specifier : "
              << wxString( m_ppsArgV[ *piArg ] ).mb_str( ) << '\n';
    return( false );
  }

  return( true );
}

//**************************************************************************************************
// Do general checks of arguments which should be file names. If any of the args. is not a valid
// file name display an error message and return false.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bCheckFiles( int * piArg )
{
  wxFileName  ofn1;
  wxString    os1;
  int         i1;

  // Check the validity of the file name/s
  for( i1=*piArg; i1<m_iArgC; i1++ )
  {
    ofn1 = wxString( m_ppsArgV[ i1 ], *wxConvCurrent );

    if( ! ofn1.IsOk( ) )
    {
      os1 = m_ppsArgV[ i1 ];
      std::cout << "Invalid file name : " << os1.mb_str( ) << '\n';

      return( false );
    }

    if( ! ofn1.FileExists( ) )
    {
      os1 = m_ppsArgV[ i1 ];
      std::cout << "File doesn't exist : " << os1.mb_str( ) << '\n';

      return( false );
    }
  }

  return( true );
}

//**************************************************************************************************
// Attempt to process the remaining command line argument/s as schematic file name/s.
//
// If a Guile procedure has been specified the command line argument/s will be treated as schematic
// file names. If no Guile procedure has been specified the decision will be made based on file name
// extensions.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcSchems( int * piArg )
{
  wxFileName  ofn1;
  int         i1;

  // Check that there are unprocessed command line argument/s
  if( *piArg >= m_iArgC ) return( false );

  // If no Guile procedure has been specified test the file name extensions
  if( m_osGuileProc.IsEmpty( ) )
  {
    for( i1=*piArg; i1<m_iArgC; i1++ )
    {
      ofn1 = wxString( m_ppsArgV[ i1 ], *wxConvCurrent );

      if( ofn1.GetExt( ).Upper( ) != wxT("SCH") ) return( false );
    }
  }

  // Load the schematic file name/s into m_osaSchemFiles
  while( *piArg < m_iArgC )
  {
    ofn1 = wxString( m_ppsArgV[ *piArg ], *wxConvCurrent );

    if( ofn1.IsRelative( ) ) ofn1.MakeAbsolute( );

    m_osaSchemFiles.Add( ofn1.GetFullPath( ) );

    ( *piArg )++;
  }

  return( true );
}

//**************************************************************************************************
// Attempt to process a command line argument as a netlist file name.
//
// Argument List :
//   piArg - A pointer to the current command line argument (incremented as part of processing)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bProcNetList( int * piArg )
{
  wxFileName  ofn1;

  // Check that there are unprocessed command line argument/s
  if( *piArg >= m_iArgC ) return( false );

  // Load the netlist file name into m_osNetLstFile
  ofn1 = wxString( m_ppsArgV[ *piArg ], *wxConvCurrent );

  if( ofn1.IsRelative( ) ) ofn1.MakeAbsolute( );

  m_osNetLstFile = ofn1.GetFullPath( );

  ( *piArg )++;

  return( true );
}

//**************************************************************************************************
// Print usage message on console.

void  CmdLinePcr::Usage( void )
{
  wxString  os1, os2;

  os2 = wxString( m_ppsArgV[ 0 ] ).AfterLast( wxT('/') );

  os1 << wxT("\n")
      << wxT("Analyse a electronic circuit using a GUI to a numerical simulation engine\n")
      << wxT("\n")
      << wxT("USAGE   : ") << os2 << wxT(" [-OPTION [ARG]] [FILE/S]\n")
      << wxT("\n")
      << wxT("OPTIONS : -h        : Print usage (this message)\n")
      << wxT("          -v        : Print version information\n")
      << wxT("          -d        : Enable debug mode (generate console spew on standard error)\n")
      << wxT("          -r RCFILE : Specify a configuration file\n")
      << wxT("                      RCFILE = ~/.gspiceui.conf (default)\n")
      << wxT("          -c        : Rebuild/clean the configuration file\n")
      << wxT("          -s SIMENG : Specify the simulation engine to be used\n")
      << wxT("                      SIMENG = ngspice or gnucap\n")
      << wxT("          -e EDA    : Specify the EDA tool suite to be used\n")
      << wxT("                      EDA    = lepton (Lepton-EDA) or geda (gEDA / GAF)\n")
      << wxT("          -a ANA    : Specify the analysis page to be displayed\n")
      << wxT("                      ANA    = op, dc, ac, tr\n")
      << wxT("          -g [PROC] : Guile procedure for importing a schematic file with gnetlist\n")
      << wxT("                      PROC   = spice-sdb, pcb, protelii, verilog, etc.\n")
      << wxT("          -w WAVVWR : Specify the waveform data viewer to be used\n")
      << wxT("                      WAVVWR = gaw or gwave\n")
      << wxT("\n")
      << wxT("FILE/S  : Import schematic file/s or load a circuit description file\n")
      << wxT("\n");

  std::cout << os1.mb_str( );
}

//**************************************************************************************************
// Print version information on console.

void  CmdLinePcr::Version( void )
{
  wxString  os1;

  os1 << wxT("\n           ") << APP_NAME
      << wxT("\n  Version ") << APP_VERSION << wxT(" (") << APP_DATE
      << wxT(")\n\n");

  std::cout << os1.mb_str( );
}

//**************************************************************************************************
// Print too many arguments error message on console.
//
// Argument List :
//   iArg - The offset to the first unprocessed argument

void  CmdLinePcr::TooManyArgs( int iArg )
{
  wxString  osArg, osErr;

  osErr << wxT("Too many arguments :");
  for( osArg=m_ppsArgV[ iArg ]; iArg<m_iArgC; osArg=m_ppsArgV[ ++iArg ] )
    osErr << wxT(' ') << osArg;
  std::cout << osErr.mb_str( ) << '\n';
}

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

bool  CmdLinePcr::bClear( void )
{
  m_eSimEng     = eSIMR_NONE;
  m_eEdaSuite   = eEDA_NONE;
  m_eDataViewer = eVIEW_NONE;
  m_eAnalysis   = eCMD_NONE;

  m_osGuileProc  .Empty( );
  m_osConfigFile .Empty( );
  m_osNetLstFile .Empty( );
  m_osaSchemFiles.Empty( );

  m_bCleanCfgFile = false;

  return( true );
}

//**************************************************************************************************
// Set the command line to be processed.
//
// Argument List :
//   iArgV   - The argment count
//   ppsArgV - The string array of argument fields
//
// Return Values:
//   true  - Success
//   false - Failure

bool  CmdLinePcr::bSetCmdLn( int iArgC, wxChar ** ppsArgV )
{
  // Check validity of arguments
  if( iArgC <= 0 )      return( false );
  if( ppsArgV == NULL ) return( false );

  m_iArgC   = iArgC;
  m_ppsArgV = ppsArgV;

  return( true );
}

//**************************************************************************************************
// Process any command line arguments past to the application at start-up.
//
// Return Values :
//   true  - Success (continue  application execution)
//   false - Failure (terminate application execution)

bool  CmdLinePcr::bProcArgs( void )
{
  wxString  osArg, os1;
  int       iArg;

  // Check validity of arguments
  if( m_iArgC   <= 0 )               return( false );
  if( m_ppsArgV == NULL )            return( false );

  // Process options
  for( iArg=1; iArg<m_iArgC; ++iArg )
  {
    osArg = (wxChar *) m_ppsArgV[ iArg ];

    if( osArg.GetChar( 0 )=='-' && osArg.Length( )>2 )
    { // Found concatenated options --> error
      std::cout << "Options must be specified separately : "
                << osArg.mb_str( ) << '\n';
      return( false );
    }
    else if( osArg == wxT("-v") )
    { // Print version information
      if( ! bProcOption_v( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-h") )
    { // Print usage information
      if( ! bProcOption_h( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-d") )
    { // Enable debug mode
      if( ! bProcOption_d( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-r") )
    { // Specify a configuration file
      if( ! bProcOption_r( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-c") )
    { // Rebuild/clean the configuration file
      if( ! bProcOption_c( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-s") )
    { // Process simulation engine specifier
      if( ! bProcOption_s( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-a") )
    { // Process analysis type specfier
      if( ! bProcOption_a( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-g") )
    { // Specify Guile procedure for importing a schematic file
      if( ! bProcOption_g( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-e") )
    { // Process EDA tool suite specifier
      if( ! bProcOption_e( &iArg ) ) return( false );
    }
    else if( osArg == wxT("-w") )
    { // Process waveform data viewer utility specifier
      if( ! bProcOption_w( &iArg ) ) return( false );
    }
    else if( osArg.GetChar( 0 ) == wxT('-') )
    { // Found invalid option --> error
      std::cout << "Invalid option : " << osArg.mb_str( ) << '\n';
      return( false );
    } // The argument is not an option
    else break;
  }

  // Attempt to process any remaining argument/s as file name/s
  if( ! bCheckFiles( &iArg ) )       return( false );
  bProcSchems ( &iArg );
  bProcNetList( &iArg );

  // Check that if a Guile procedure has been specified there are schematic file/s to be imported
  if( ! m_osGuileProc.IsEmpty( ) )
  {
    if( m_osaSchemFiles.IsEmpty( ) )
    {
      std::cout << "Option -g specified without accompanying schematic file/s\n";
      return( false );
    }
  }

  // At this stage all arguments should have been processed
  if( iArg < m_iArgC )
  {
    TooManyArgs( iArg );
    return( false );
  }

  // All's well so display the system banner
  Version( );

  return( true );
}

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

void  CmdLinePcr::Print( const wxString & rosPrefix )
{
  wxString  osPrefix, os1;
  int       i1;

  osPrefix = rosPrefix + wxT( "CmdLinePcr::" );

  std::cout << osPrefix.mb_str( ) << "m_iArgC            : " << m_iArgC << '\n';

  std::cout << osPrefix.mb_str( ) << "m_ppsArgV          :";
  for( i1=0; i1<m_iArgC; i1++ )
  {
    os1 = m_ppsArgV[ i1 ];
    std::cout << ' ' << os1.mb_str( );
  }
  std::cout << '\n';

  std::cout << osPrefix.mb_str( ) << "m_eSimEng          : " << m_eSimEng     << '\n';
  std::cout << osPrefix.mb_str( ) << "m_eEdaSuite        : " << m_eEdaSuite   << '\n';
  std::cout << osPrefix.mb_str( ) << "m_eDataViewer      : " << m_eDataViewer << '\n';
  std::cout << osPrefix.mb_str( ) << "m_eAnalysis        : " << m_eAnalysis   << '\n';

  std::cout << osPrefix.mb_str( ) << "m_osGuileProc      : " << m_osGuileProc .mb_str( ) << '\n';
  std::cout << osPrefix.mb_str( ) << "m_osConfigFile     : " << m_osConfigFile.mb_str( ) << '\n';
  std::cout << osPrefix.mb_str( ) << "m_osNetLstFile     : " << m_osNetLstFile.mb_str( ) << '\n';

  std::cout << osPrefix.mb_str( ) << "m_osaSchemFiles[ ] : ";
  for( i1=0; i1<(int)m_osaSchemFiles.GetCount( ); i1++ )
  {
    if( i1 > 0 ) std::cout << ", ";
    std::cout << m_osaSchemFiles.Item( i1 ).mb_str( );
  }
  std::cout << '\n';

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

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