//**************************************************************************************************
//                                         NbkNgSpice.cpp                                          *
//                                        ----------------                                         *
// Started     : 2004-05-08                                                                        *
// Last Update : 2018-10-17                                                                        *
// 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 "NbkNgSpice.hpp"

//**************************************************************************************************
// Implement an event table.

wxBEGIN_EVENT_TABLE( NbkNgSpice, NbkSimEngBase )

  EVT_NOTEBOOK_PAGE_CHANGED( -1,          NbkNgSpice::OnPageChangd )

  EVT_BUTTON( PnlAnaBase::ID_BTN_OPTIONS, NbkNgSpice::OnBtnOptions )

wxEND_EVENT_TABLE( )

//**************************************************************************************************
// Constructor.
//
// Arguments :
//   poParent - The parent window
//   oWinID   - The display object ID
//   roPosn   - The notebook position
//   roSize   - The notebook size

NbkNgSpice::NbkNgSpice( wxWindow * poParent, wxWindowID oWinID )
                      : NbkSimEngBase( poParent, oWinID ), m_oDlgCfgOPT( poParent )
{
  // Set the simulator engine type specifier
  m_eSimEng = eSIMR_NGSPICE;

  // Create the various display objects
  m_poPnlNgSpiceOP = new PnlNgSpiceOP( this );
  m_poPnlNgSpiceDC = new PnlNgSpiceDC( this );
  m_poPnlNgSpiceAC = new PnlNgSpiceAC( this );
  m_poPnlNgSpiceTR = new PnlNgSpiceTR( this );

  // Add the display objects to the note book
  AddPage( m_poPnlNgSpiceOP, wxT( " Quiescent  "     ) );
  AddPage( m_poPnlNgSpiceDC, wxT( "       DC       " ) );
  AddPage( m_poPnlNgSpiceAC, wxT( "       AC       " ) );
  AddPage( m_poPnlNgSpiceTR, wxT( "  Transient  "    ) );

  // Specify the default page to be displayed
  SetSelection( 0 );
}

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

NbkNgSpice::~NbkNgSpice( )
{
}

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

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

  // Clear the base class
  if( ! NbkSimEngBase   ::bClear( ) ) bRtn = false;

  if( ! m_poPnlNgSpiceOP->bClear( ) ) bRtn = false;
  if( ! m_poPnlNgSpiceDC->bClear( ) ) bRtn = false;
  if( ! m_poPnlNgSpiceAC->bClear( ) ) bRtn = false;
  if( ! m_poPnlNgSpiceTR->bClear( ) ) bRtn = false;

  if( ! m_oDlgCfgOPT     .bClear( ) ) bRtn = false;

  return( bRtn );
}

//**************************************************************************************************
// Load information from a simulation object.
//
// Argument List :
//   roSimn - The simulation object
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NbkNgSpice::bLoad( const SimnBase & roSimn )
{
  bool    bRtn=true;
  size_t  sz1;

  if( roSimn.eGetSimEng( ) != eSIMR_NGSPICE )            return( false );

  SimnNgSpice & roSimnNgs = (SimnNgSpice &) roSimn;

  if( ! bSetPage( roSimnNgs.eGetAnaType( ) ) )           bRtn = false;

  sz1 = 0;
  if( m_poPnlNgSpiceOP->bLoad( roSimnNgs ) ) sz1++;
  if( m_poPnlNgSpiceDC->bLoad( roSimnNgs ) ) sz1++;
  if( m_poPnlNgSpiceAC->bLoad( roSimnNgs ) ) sz1++;
  if( m_poPnlNgSpiceTR->bLoad( roSimnNgs ) ) sz1++;
  if( sz1 == 0 )                                         bRtn = false;

  if( ! m_oDlgCfgOPT.bSetValues( roSimnNgs.m_oCmdOPT ) ) bRtn = false;

  return( bRtn );
}

//**************************************************************************************************
// Save information to a simulation object.
//
// Argument List :
//   roSimultn - The simulation object
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NbkNgSpice::bSave( SimnBase & roSimn )
{
  wxString  os1;
  int       i1;

  // Only proceed if this is an NG-Spice simulation object
  if( roSimn.eGetSimEng( ) != eSIMR_NGSPICE ) return( false );

  SimnNgSpice & roSimnNgs = (SimnNgSpice &) roSimn;

  // Clear the error string
  NbkSimEngBase::m_osErrMsg.Empty( );

  // Create the analysis command/s
  switch( GetSelection( ) )
  {
    case 0 : // OP analysis
      if( ! m_poPnlNgSpiceOP->bSave( roSimnNgs ) )
        SetErrMsg( m_poPnlNgSpiceOP->rosGetErrMsg( ) );
      break;

    case 1 : // DC analysis
      if( ! m_poPnlNgSpiceDC->bSave( roSimnNgs ) )
        SetErrMsg( m_poPnlNgSpiceDC->rosGetErrMsg( ) );
      break;

    case 2 : // AC analysis
      if( ! m_poPnlNgSpiceAC->bSave( roSimnNgs ) )
        SetErrMsg( m_poPnlNgSpiceAC->rosGetErrMsg( ) );
      break;

    case 3 : // TR analysis
      if( ! m_poPnlNgSpiceTR->bSave( roSimnNgs ) )
        SetErrMsg( m_poPnlNgSpiceTR->rosGetErrMsg( ) );
      break;

    default :                                 return( false );
  }
  if( ! bIsOk( ) )                            return( false );

  // Transfer the values in OPTIONS config. dialogue to OPTIONS command object
  m_oDlgCfgOPT.bGetValues( roSimnNgs.m_oCmdOPT );

  // Set the results output page width : 40 chars. for the index and the
  // independent variable plus 16 chars. multiplied by the number of dependent
  // variables.
  i1 = 40 + 16 * roSimnNgs.m_oCmdPR.iGetParamCnt( );
  CnvtType::bIntToStr( i1, os1 );
  os1.Trim( false );
  roSimnNgs.m_oCmdOPT.m_osWIDTH = os1;

  // Create the OPTIONS command
  roSimnNgs.m_oCmdOPT.bFormat( );
  if( ! roSimnNgs.m_oCmdOPT.bIsValid( ) )
  {
    os1 = wxT("OPTIONS command fault :\n\n  ");
    SetErrMsg( os1 + roSimnNgs.m_oCmdOPT.rosGetErrMsg( ) );
    return( false );
  }

  return( true );
}

//**************************************************************************************************
// Set the page to be displayed.
//
// Argument List :
//   eAnalysis - The enumerated analysis type specifier
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NbkNgSpice::bSetPage( eTypeCmd eAnalysis )
{
  int  iPage;

  switch( eAnalysis )
  {
    case eCMD_OP   : iPage = 0; break;
    case eCMD_DC   : iPage = 1; break;
    case eCMD_AC   : iPage = 2; break;
    case eCMD_TR   : iPage = 3; break;
    default        : return( false );
  }

  SetSelection( iPage ); // Specify the page to be displayed

  return( true );
}

//**************************************************************************************************
// Set the page to be displayed.
//
// Argument List :
//   rosAnalysis - The two letter analysis type specifier (case ignored)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  NbkNgSpice::bSetPage( const wxString & rosAnalysis )
{
  eTypeCmd  eAnalysis=eCMD_NONE;
  wxString  os1;

  os1 = rosAnalysis.Upper( );

  if( os1.Length( ) == 2 )
  {
    if(      os1 == wxT("OP") ) eAnalysis = eCMD_OP;
    else if( os1 == wxT("DC") ) eAnalysis = eCMD_DC;
    else if( os1 == wxT("AC") ) eAnalysis = eCMD_AC;
    else if( os1 == wxT("TR") ) eAnalysis = eCMD_TR;
  }

  return( bSetPage( eAnalysis ) );
}

//**************************************************************************************************
// Get the two letter page specifier.
//
// Return Values :
//   Success - The two letter analysis type specifier (lower case)
//   Failure - An empty string

const wxString & NbkNgSpice::rosGetPage( void )
{
  static  wxString  osAnalysis;

  switch( eGetPage( ) )
  {
    case eCMD_OP : osAnalysis = wxT("op"); break;
    case eCMD_DC : osAnalysis = wxT("dc"); break;
    case eCMD_AC : osAnalysis = wxT("ac"); break;
    case eCMD_TR : osAnalysis = wxT("tr"); break;
    default      : osAnalysis.Empty( );
  }

  return( osAnalysis );
}

//**************************************************************************************************
// Get the enumerated page specifier.
//
// Return Values :
//   Success - The enumerated analysis specifier
//   Failure - eCMD_NONE

eTypeCmd  NbkNgSpice::eGetPage( void )
{
  eTypeCmd  eAnalysis;

  switch( GetSelection( ) )
  {
    case  0 : eAnalysis = eCMD_OP; break;
    case  1 : eAnalysis = eCMD_DC; break;
    case  2 : eAnalysis = eCMD_AC; break;
    case  3 : eAnalysis = eCMD_TR; break;
    default : eAnalysis = eCMD_NONE;
  }

  return( eAnalysis );
}

//**************************************************************************************************
//                                         Event Handlers                                          *
//**************************************************************************************************
// This event is generated when a notebook page has just changed.
//
// Argument List:
//   roEvtNbk - An object holding information about the event

void  NbkNgSpice::OnPageChangd( wxNotebookEvent & roEvtNbk )
{
  PnlAnaBase * poPnlOld=NULL;
  PnlAnaBase * poPnlNew=NULL;
  wxString     os1;
  int          i1;

  // Get pointers to the old and new (current) analysis panels
  i1 = roEvtNbk.GetOldSelection( );
  if( i1 != wxNOT_FOUND ) poPnlOld = static_cast< PnlAnaBase * >( GetPage( (size_t) i1 ) );
  poPnlNew = static_cast< PnlAnaBase * >( GetCurrentPage( ) );

  // Synchronize the ambient temperature values between the different analysis pages
  if( g_oConfig.bGetSyncTemps( ) )
  {
    // Transfer the ambient temperature from the previous page to the OPTIONS dialog
    if( poPnlOld != NULL )
    {
      if( poPnlOld->m_oPnlTemp.GetParent( ) != NULL )
      {
        os1 = poPnlOld->m_oPnlTemp.rosGetValue( );
        m_oDlgCfgOPT.bSetValue( DlgNgSpiceCfgOPT::ID_PNL_TEMP, os1 );
      }
    }

    // Transfer the ambient temperature from the OPTIONS dialog to current page
    if( poPnlNew->m_oPnlTemp.GetParent( ) != NULL )
    {
      os1 = m_oDlgCfgOPT.rosGetValue( DlgNgSpiceCfgOPT::ID_PNL_TEMP );
      poPnlNew->m_oPnlTemp.bSetValue( os1 );
    }
  }

  // Record the signal source value from the old panel
  if( poPnlOld != NULL )
  {
    if( poPnlOld->m_oChoSrcName.GetSelection( ) > 0 )
    {
      m_oCpntSigSrc.bSetName( poPnlOld->m_oChoSrcName.GetStringSelection( ) );
      if( poPnlOld->m_oPnlSrcLvl.GetParent( ) != NULL )
      {
        os1 = poPnlOld->m_oPnlSrcLvl.rosGetValue( );
        m_oCpntSigSrc.bSetValue( os1 );
      }
      else if( poPnlOld->eGetAnalysType( ) == eCMD_TR )
      {
        os1 = m_poPnlNgSpiceTR->m_oDlgCfgSrc.rosGetValue( DlgNgSpiceCfgSrc::ID_PNL_SIN_AMP );
        m_oCpntSigSrc.bSetValue( os1 );
      }
    }
    else m_oCpntSigSrc.bClear( );
  }

  // Synchronize the signal sources between the old and new analysis pages
  if( g_oConfig.bGetSyncSigSrcs( ) )
  {
    os1 = m_oCpntSigSrc.rosGetName( );

    if( poPnlNew->m_oChoSrcName.SetStringSelection( os1 ) )
    { // Set the signal source value if it's been defined
      if( poPnlNew->m_oPnlSrcLvl.GetParent( ) != NULL )
      {
        poPnlNew->m_oPnlSrcLvl.bSetUnitsType( m_oCpntSigSrc.eGetUnitsType( ) );
        poPnlNew->m_oPnlSrcLvl.bSetValue( m_oCpntSigSrc.rosGetValue( ) );
      }
      else if( poPnlNew->eGetAnalysType( ) == eCMD_TR )
      {
        poPnlNew->m_oPnlSrcLvl.bSetUnitsType( m_oCpntSigSrc.eGetUnitsType( ) );
        os1 = m_oCpntSigSrc.rosGetValue( );
        m_poPnlNgSpiceTR->m_oDlgCfgSrc.bSetValue( DlgNgSpiceCfgSrc::ID_PNL_SIN_AMP, os1 );
      }
    }
    else
    { // Couldn't set the signal source name so clear it
      poPnlNew->m_oChoSrcName.SetSelection( 0 );
      if( poPnlNew->m_oPnlSrcLvl.GetParent( ) != NULL )
      {
        poPnlNew->m_oPnlSrcLvl.bSetUnitsType( eUNITS_NONE );
        poPnlNew->m_oPnlSrcLvl.bSetValue( 0.0 );
      }
    }

    // Update the signal parameter units
    if( poPnlNew->eGetAnalysType( ) == eCMD_DC )
      m_poPnlNgSpiceDC->OnSrcName( (wxCommandEvent &) roEvtNbk );
  }

  // Allow additional event handlers to be called
  roEvtNbk.Skip( );
}

//**************************************************************************************************
// Setup .OPTIONS command button event handler.
//
// Argument List :
//   roEvtCmd - An object holding information about the event

void  NbkNgSpice::OnBtnOptions( wxCommandEvent & roEvtCmd )
{
  PnlAnaBase * poPnlAna;
  wxString     os1;
  int          i1;

  // Get a pointer to the currently displayed analysis page
  poPnlAna = static_cast< PnlAnaBase * >( GetCurrentPage( ) );
  if( poPnlAna == NULL ) return;

  // Set the temperature in the OPTIONS dialog
  if( poPnlAna->m_oPnlTemp.GetParent( ) != NULL )
  {
    os1 = poPnlAna->m_oPnlTemp.rosGetValue( );
    m_oDlgCfgOPT.bSetValue( DlgNgSpiceCfgOPT::ID_PNL_TEMP, os1 );
  }

  // Display the OPTIONS dialog
  m_oDlgCfgOPT.CenterOnParent( );
  i1 = m_oDlgCfgOPT.ShowModal( );
  if( i1 != wxID_OK )    return;

  // Set the temperature in the current analysis panel
  if( poPnlAna->m_oPnlTemp.GetParent( ) != NULL )
  {
    os1 = m_oDlgCfgOPT.rosGetValue( DlgNgSpiceCfgOPT::ID_PNL_TEMP );
    poPnlAna->m_oPnlTemp.bSetValue( os1 );
  }
}

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