//**************************************************************************************************
//                                         SimnNgSpice.cpp                                         *
//                                        -----------------                                        *
// Started     : 2008-05-07                                                                        *
// Last Update : 2018-10-30                                                                        *
// Copyright   : (C) 2008-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 "SimnNgSpice.hpp"

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

SimnNgSpice::SimnNgSpice( void ) : SimnBase( )
{
  m_eSimEng = eSIMR_NGSPICE;

  // Initialize all object attributes
  bClear( );
}

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

SimnNgSpice::~SimnNgSpice( )
{
}

//**************************************************************************************************
// Extract the simulator engine and return true if it's NG-Spice.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  SimnNgSpice::bLoadSimEng( void )
{
  wxString  os1;
  size_t    sz1;

  // Scan the circuit description for simulator type
  for( sz1=0; sz1<NetList::m_osaNetLst.GetCount( ); sz1++ )
  {
    os1 = NetList::m_osaNetLst.Item( sz1 );
    if( os1.IsEmpty( ) ) continue;
    if( os1.Upper( ).Contains( wxT("NG-SPICE") ) ) return( true );
  }

  return( false );
}

//**************************************************************************************************
// Extract all the simulator command lines from the circuit description.
//
// The following commands are currently detected and extracted :
//
//   .OPTIONS
//   .IC
//   .DC
//   .AC
//   .TRANSIENT
//   .PRINT
//
// Return Values :
//   true  - Success
//   false - Failure

bool  SimnNgSpice::bLoadSimCmds( void )
{
  bool      bRtn=false;
  wxString  os1;
  size_t    sz1;

  // Scan the circuit description for simulation commands
  for( sz1=0; sz1<NetList::m_osaNetLst.GetCount( ); sz1++ )
  {
    os1 = NetList::m_osaNetLst.Item( sz1 );

    if( os1.IsEmpty( ) )               continue;
    if( ! os1.StartsWith( wxT(".") ) ) continue;

    os1.MakeUpper( );

    if(      os1.StartsWith( wxT(".OPT") ) ) // OPTIONS command
      m_oCmdOPT.bSetString( NetList::m_osaNetLst.Item( sz1 ) );
    else if( os1.StartsWith( wxT(".DC")  ) ) // DC command
      m_oCmdDC .bSetString( NetList::m_osaNetLst.Item( sz1 ) );
    else if( os1.StartsWith( wxT(".AC")  ) ) // AC command
      m_oCmdAC .bSetString( NetList::m_osaNetLst.Item( sz1 ) );
    else if( os1.StartsWith( wxT(".TR")  ) ) // TRANSIENT command
      m_oCmdTR .bSetString( NetList::m_osaNetLst.Item( sz1 ) );
    else if( os1.StartsWith( wxT(".PR")  ) ) // PRINT command
      m_oCmdPR .bSetString( NetList::m_osaNetLst.Item( sz1 ) );
    else continue;

    bRtn = true;
  }

  // There is no operating point command in NG-Spice, the DC command performs this function
  if( m_oCmdDC.m_osSource.Upper( ) == wxT("TEMP") ) m_oCmdPR.bSetAnaType( eCMD_OP );

  return( bRtn );
}

//**************************************************************************************************
// Extract the source component.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  SimnNgSpice::bLoadSigSrc( void )
{
  // Search for and extract a signal source component
  if( SimnBase::bLoadSigSrc( ) )
  {
    // Set the NG-Spice Independent Source object
    (Component &) m_oCpntIndSrc = m_oCpntSigSrc;
    return( true );
  }

  // Special treatment for a DC Signal Source
  if( eGetAnaType( )==eCMD_DC && m_oCmdDC.m_osSource!=wxT("TEMP") )
  {
    m_oCpntSigSrc.bClear( );
    m_oCpntSigSrc.bSetName( m_oCmdDC.m_osSource );
    return( true );
  }

  return( false );
}

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

bool  SimnNgSpice::bClear( void )
{
  // Clear the command object pointer attributes
  bClrCmds( );

  // Clear the base class
  return( SimnBase::bClear( ) );
}

//**************************************************************************************************
// Clear all simulator command objects.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  SimnNgSpice::bClrCmds( void )
{
  m_oCmdDC .bSetDefaults( );
  m_oCmdAC .bSetDefaults( );
  m_oCmdTR .bSetDefaults( );

  m_oCmdOPT.bSetDefaults( );
  m_oCmdPR .bSetDefaults( );

  m_oCpntIndSrc.bClear( );

  return( true );
}

//**************************************************************************************************
// Clear all test points ie. nodes and components.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  SimnNgSpice::bClrTstPts( void )
{
  m_oCmdPR.m_osaNodes.Empty( );
  m_oCmdPR.m_osaCpnts.Empty( );

  return( true );
}

//**************************************************************************************************
// Do the current simulation settings constitute a valid simulation?
//
// Return Values :
//   true  - Valid
//   false - Not valid

bool  SimnNgSpice::bValidate( void )
{
  // Validate the base class
  SimnBase::bValidate( );

  if( ! m_oCmdOPT.bIsValid( ) )    SetErrMsg( m_oCmdOPT.rosGetErrMsg( ) );
  if( ! m_oCmdPR .bIsValid( ) )    SetErrMsg( m_oCmdPR .rosGetErrMsg( ) );

  switch( eGetAnaType( ) )
  {
    case eCMD_OP :

    case eCMD_DC :
      if( ! m_oCmdDC.bIsValid( ) ) SetErrMsg( m_oCmdDC .rosGetErrMsg( ) );
      break;

    case eCMD_AC :
      if( ! m_oCmdAC.bIsValid( ) ) SetErrMsg( m_oCmdAC .rosGetErrMsg( ) );
      break;

    case eCMD_TR :
      if( ! m_oCmdTR.bIsValid( ) ) SetErrMsg( m_oCmdTR .rosGetErrMsg( ) );
      break;

    default :
      SetErrMsg( wxT("No valid analysis command exists.") );
  }

  return( bIsValid( ) );
}

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

bool  SimnNgSpice::bLoad( void )
{
  // Envoke the base class load operations
  if( ! SimnBase::bLoad( ) ) return( false );

  // Load the independent signal source
  m_oCpntIndSrc = m_oCpntSigSrc;

  return( true );
}

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

bool  SimnNgSpice::bSave( void )
{
  // Envoke the base class save operations
  if( ! SimnBase::bSave( ) ) return( false );

  // Append the simulation command lines to the end of the file
  NetList::m_osaNetLst.Last( ) = wxT("********** NG-Spice Simulation Commands **********");
  NetList::m_osaNetLst.Add( wxT("") );
  NetList::m_osaNetLst.Add( m_oCmdOPT );
  NetList::m_osaNetLst.Add( m_oCmdPR );
  if( m_oCmdDC.bIsValid( ) ) NetList::m_osaNetLst.Add( m_oCmdDC );
  if( m_oCmdAC.bIsValid( ) ) NetList::m_osaNetLst.Add( m_oCmdAC );
  if( m_oCmdTR.bIsValid( ) ) NetList::m_osaNetLst.Add( m_oCmdTR );

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

  return( true );
}

//**************************************************************************************************
// Add a node to the list of test points.
//
// Argument List :
//   rosNode - The name of the node to be added to the list of test points
//
// Return Values :
//   true  - Success
//   false - Failure

bool  SimnNgSpice::bAddTstNode( const wxString & rosName )
{
  // Is the node name valid?
  if( rosName.IsEmpty( ) )                                 return( false );
  // Is the node name already in the test node list?
  if( rosaGetTstNodes( ).Index( rosName ) != wxNOT_FOUND ) return( true );

  // Add the node name to the list of test points
  m_oCmdPR.m_osaNodes.Add( rosName );
  m_oCmdPR.m_osaNodes.Sort( &iStrCmpNode );

  return( true );
}

//**************************************************************************************************
// Add a component to the list of test points.
//
// Argument List :
//   rosName - The name of the component to be added to the list of test points
//
// Return Values :
//   true  - Success
//   false - Failure

bool  SimnNgSpice::bAddTstCpnt( const wxString & rosName )
{
  // Is the component name valid?
  if( rosName.IsEmpty( ) )                                 return( false );
  // Is the component name already in the test component list?
  if( rosaGetTstCpnts( ).Index( rosName ) != wxNOT_FOUND ) return( true );

  // Add the component name to the list of test points
  m_oCmdPR.m_osaCpnts.Add( rosName );
  m_oCmdPR.m_osaCpnts.Sort( &iStrCmpCpnt );

  return( true );
}

//**************************************************************************************************
// Copy the contents of a SimnGnuCap object.
//
// Argument List :
//   roSimn - A reference to a SimnGnuCap object
//
// Return Values :
//   A reference to this object

SimnNgSpice & SimnNgSpice::operator = ( const SimnGnuCap & roSimn )
{
  (SimnBase &) *this = (SimnBase &) roSimn;

  switch( roSimn.eGetAnaType( ) )
  {
    case eCMD_OP :
      m_oCmdDC = roSimn.m_oCmdOP;
      break;

    case eCMD_DC :
      m_oCmdDC = roSimn.m_oCmdDC;
      break;

    case eCMD_AC :
      m_oCmdAC = roSimn.m_oCmdAC;
      break;

    case eCMD_TR :
      m_oCmdTR = roSimn.m_oCmdTR;
      break;

    default :
      break;
  }

  m_oCmdOPT     = roSimn.m_oCmdOPT;
  m_oCmdPR      = roSimn.m_oCmdPR;
  m_oCpntIndSrc = roSimn.m_oCmdGEN;

  return( *this );
}

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

void  SimnNgSpice::Print( const wxString & rosPrefix )
{
  SimnBase    ::Print( rosPrefix + wxT("SimnBase::")     );

  m_oCmdDC     .Print( rosPrefix + wxT("m_oCmdDC.")      );
  m_oCmdAC     .Print( rosPrefix + wxT("m_oCmdAC.")      );
  m_oCmdTR     .Print( rosPrefix + wxT("m_oCmdTR.")      );
  m_oCmdOPT    .Print( rosPrefix + wxT("m_oCmdOPT.")     );
  m_oCmdPR     .Print( rosPrefix + wxT("m_oCmdPR.")      );
  m_oCpntIndSrc.Print( rosPrefix + wxT("m_oCpntIndSrc.") );
}

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