//**************************************************************************************************
//                                         CmdLinePcr.cpp                                          *
//                                        ----------------                                         *
// Started     : 2005-02-22                                                                        *
// Last Update : 2015-10-25                                                                        *
// Copyright   : (C) 2005-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 "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 : 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_e( int * piArg )
{
  wxString  osArg;

  ( *piArg )++;

  if( *piArg >= m_iArgC )
  {
    std::cout << "Missing schematic capture / editor program specifier.\n";
    return( false );
  }

  osArg = m_ppsArgV[ *piArg ];
  osArg.MakeLower( );

  if(      osArg == BIN_GSCHEM   ) m_osSchemEdit = BIN_GSCHEM;
  else if( osArg == BIN_EESCHEMA ) m_osSchemEdit = BIN_EESCHEMA;
  else
  {
    std::cout << "Invalid schematic capture / editor program specifier : "
              << wxString( m_ppsArgV[ *piArg ] ).mb_str( ) << '\n';
    return( false );
  }

  return( true );
}
*/
//**************************************************************************************************
// Process command line option -g : Guile procedure used when importing schematic 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_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 ) != wxT('-') )
    {
      // Test the 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 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 );
        }
      }
    }

    // Increment argument pointer
    ( *piArg )++;
  }
  else // Use the default value
    m_osGuileProc = wxT("spice-sdb");

  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 __attribute__ ((unused)) )
{
  Usage( );

  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 ];

  m_eSimEng = eStrToEnumEng( osArg );

  if( m_eSimEng == eSIMR_NONE )
  {
    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 __attribute__ ((unused)) )
{
  wxString  os1;

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

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

  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("          -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("          -e SCHEM  : Specify the schematic capture / editor program to be used\n")
//      << wxT("                      SCHEM = gschem (gEDA / GAF) or eeschema (KiCAD)\n")
      << wxT("          -w VIEWER : Specify the waveform data viewer to be used\n")
      << wxT("                      VIEWER = 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( );
}

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

bool  CmdLinePcr::bClear( void )
{
  m_eSimEng     = eSIMR_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 schematic capture / editor program specifier
//      if( ! bProcOption_w( &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 );

  // At this stage all arguments should have been processed
  if( iArg < m_iArgC )
  {
    os1 << wxT("Too many arguments :");
    for( osArg=m_ppsArgV[ iArg ]; iArg<m_iArgC; osArg=m_ppsArgV[ ++iArg ] )
      os1 << wxT(' ') << osArg;
    std::cout << os1.mb_str( ) << '\n';

    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_eSchemEdit       : " << m_osSchemEdit << '\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';
}

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