//**************************************************************************************************
//                                      CpntNgSpiceIndSrc.cpp                                      *
//                                     -----------------------                                     *
// Started     : 2008-06-12                                                                        *
// Last Update : 2018-10-03                                                                        *
// 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 "CpntNgSpiceIndSrc.hpp"

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

CpntNgSpiceIndSrc::CpntNgSpiceIndSrc( void ) : Component( )
{
  bClear( );
}

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

CpntNgSpiceIndSrc::~CpntNgSpiceIndSrc( )
{
}

//**************************************************************************************************
// Parse the value part of the component definition string.
//
// Eg. : Vin 3 0 SIN(0 1 100Meg 1ns 1E10)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CpntNgSpiceIndSrc::bParseValue( void )
{
  wxStringTokenizer  ostk1;
  size_t             szSin, szPulse;
  size_t             sz1, sz2;

  ClrValues( );

  // Find the beginning of the SIN and/or PULSE sections
  szSin   = Component::m_osValue.Upper( ).Find( wxT("SIN(") );
  szPulse = Component::m_osValue.Upper( ).Find( wxT("PULSE(") );
  if( szSin==wxString::npos && szPulse==wxString::npos ) return( false );

  // Extract the sinusoid function parameter values
  if( szSin != wxString::npos )
  {
    sz1 = Component::m_osValue.find( wxT('('), szSin );
    sz2 = Component::m_osValue.find( wxT(')'), szSin );
    if( sz1==wxString::npos || sz2==wxString::npos )     return( false );

    ostk1.SetString( Component::m_osValue.substr( sz1+1, sz2-sz1-1 ) );
    if( ostk1.CountTokens( ) != 5 )                      return( false );

    m_osSinOffset = ostk1.GetNextToken( );
    m_osSinAmp    = ostk1.GetNextToken( );
    m_osSinFreq   = ostk1.GetNextToken( );
    m_osSinDelay  = ostk1.GetNextToken( );
    m_osSinDamp   = ostk1.GetNextToken( );
  }

  // Extract the pulse function parameter values
  if( szPulse != wxString::npos )
  {
    sz1 = Component::m_osValue.find( wxT('('), szPulse );
    sz2 = Component::m_osValue.find( wxT(')'), szPulse );
    if( sz1==wxString::npos || sz2==wxString::npos )     return( false );

    ostk1.SetString( Component::m_osValue.substr( sz1+1, sz2-sz1-1 ) );
    if( ostk1.CountTokens( ) != 7 )                      return( false );

    m_osPulInitV  = ostk1.GetNextToken( );
    m_osPulValue  = ostk1.GetNextToken( );
    m_osPulDelay  = ostk1.GetNextToken( );
    m_osPulRise   = ostk1.GetNextToken( );
    m_osPulFall   = ostk1.GetNextToken( );
    m_osPulWidth  = ostk1.GetNextToken( );
    m_osPulPeriod = ostk1.GetNextToken( );
  }

  return( true );
}

//**************************************************************************************************
// Format the value part of the command string.
//
// Eg. : Vin 3 0 SIN(0 1 100Meg 1ns 1E10)
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CpntNgSpiceIndSrc::bFormatValue( void )
{
  float  f1;
  bool   b1;

  m_osValue.Empty( );

  f1 = 0.0;
  b1 = CnvtType::bStrToFlt( m_osSinAmp, &f1 );
  if( b1==true && f1!=0.0 )
  { // Create the sinusoid function section
    m_osValue << wxT("SIN( ");
    m_osValue << m_osSinOffset << wxT(' ');
    m_osValue << m_osSinAmp    << wxT(' ');
    m_osValue << m_osSinFreq   << wxT(' ');
    m_osValue << m_osSinDelay  << wxT(' ');
    m_osValue << m_osSinDamp   << wxT(' ');
    m_osValue << wxT(')');
  }

  f1 = 0.0;
  b1 = CnvtType::bStrToFlt( m_osPulValue, &f1 );
  if( b1==true && f1!=0.0 )
  { // Create the pulse function section
    if( ! m_osValue.IsEmpty( ) ) m_osValue << wxT(' ');
    m_osValue << wxT("PULSE( ");
    m_osValue << m_osPulInitV  << wxT(' ');
    m_osValue << m_osPulValue  << wxT(' ');
    m_osValue << m_osPulDelay  << wxT(' ');
    m_osValue << m_osPulRise   << wxT(' ');
    m_osValue << m_osPulFall   << wxT(' ');
    m_osValue << m_osPulWidth  << wxT(' ');
    m_osValue << m_osPulPeriod << wxT(' ');
    m_osValue << wxT(')');
  }

  if( m_osValue.IsEmpty( ) ) m_osValue = wxT("0.0");

  return( true );
}

//**************************************************************************************************
// Check that the object attributes are valid.
//
// Return Values :
//   true  - Success
//   false - Failure

bool  CpntNgSpiceIndSrc::bValidate( void )
{
  double  dfSinOffset, dfSinAmp, dfSinFreq, dfSinDelay, dfSinDamp;
  double  dfPulInitial, dfPulMax, dfPulDelay, dfPulRise, dfPulWidth, dfPulFall,
          dfPulPeriod;

  // Check the validity of the base component
  if( ! Component::bValidate( ) )                return( false );

  // Check that the component type is valid
  if( m_eType!=eCPNT_IVS && m_eType!=eCPNT_ICS ) return( false );

  // Check the sinusoidal source values
  dfSinAmp    = 0.0;
  dfSinOffset = 0.0;
  dfSinFreq   = 0.0;
  dfSinDelay  = 0.0;
  dfSinDamp   = 0.0;
  if( ! CnvtType::bStrToFlt( m_osSinOffset, &dfSinOffset ) )
    bSetErrMsg( wxT("sinusoid offset value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osSinAmp   , &dfSinAmp    ) )
    bSetErrMsg( wxT("sinusoid amplitude value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osSinFreq  , &dfSinFreq   ) )
    bSetErrMsg( wxT("sinusoid frequency value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osSinDelay , &dfSinDelay  ) )
    bSetErrMsg( wxT("sinusoid delay value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osSinDamp  , &dfSinDamp   ) )
    bSetErrMsg( wxT("sinusoid damping factor is invalid") );

  // Check overall sinusoidal source logic
  if( dfSinFreq>0.0 && dfSinAmp <=0.0 )
    bSetErrMsg( wxT("sinusoid amplitude value must be greater than zero") );
  if( dfSinAmp >0.0 && dfSinFreq<=0.0 )
    bSetErrMsg( wxT("sinusoid frequency must be greater than zero") );

  // Check the pulse source values
  dfPulInitial = 0.0;
  dfPulMax     = 0.0;
  dfPulDelay   = 0.0;
  dfPulRise    = 0.0;
  dfPulWidth   = 0.0;
  dfPulFall    = 0.0;
  dfPulPeriod  = 0.0;
  if( ! CnvtType::bStrToFlt( m_osPulInitV , &dfPulInitial ) )
    bSetErrMsg( wxT("initial pulse value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulValue , &dfPulMax     ) )
    bSetErrMsg( wxT("maximum pulse value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulDelay , &dfPulDelay   ) )
    bSetErrMsg( wxT("pulse delay value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulRise  , &dfPulRise    ) )
    bSetErrMsg( wxT("pulse rise time value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulWidth , &dfPulWidth   ) )
    bSetErrMsg( wxT("pulse width value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulFall  , &dfPulFall    ) )
    bSetErrMsg( wxT("pulse fall time value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulPeriod, &dfPulPeriod  ) )
    bSetErrMsg( wxT("pulse period value is invalid") );

  // Check the pulse time values
  if( dfPulPeriod < dfPulRise )
    bSetErrMsg( wxT("pulse rise time is greater than the period") );
  if( dfPulPeriod < dfPulWidth )
    bSetErrMsg( wxT("pulse width is greater than the period") );
  if( dfPulPeriod < dfPulFall )
    bSetErrMsg( wxT("pulse fall time is greater than the period") );
  if( dfPulPeriod < dfPulRise+dfPulWidth+dfPulFall )
    bSetErrMsg( wxT("pulse period is less then the width") );

  // Check overall pulse source logic
  if( dfPulPeriod!=0.0 && (dfPulInitial==0.0 && dfPulMax==0.0) )
    bSetErrMsg( wxT("pulse timing has been defined but no levels") );
  if( dfPulPeriod==0.0 && (dfPulInitial!=0.0 || dfPulMax!=0.0) )
    bSetErrMsg( wxT("pulse levels have been defined but no timings") );

  return( bIsValid( ) );
}

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

void  CpntNgSpiceIndSrc::ClrValues( void )
{
  m_osSinOffset = NGS_SINOFFSET;
  m_osSinAmp    = NGS_SINAMP;
  m_osSinFreq   = NGS_SINFREQ;
  m_osSinDelay  = NGS_SINDELAY;
  m_osSinDamp   = NGS_SINDAMP;

  m_osPulInitV  = NGS_PULINITV;
  m_osPulValue  = NGS_PULVALUE;
  m_osPulDelay  = NGS_PULDELAY;
  m_osPulRise   = NGS_PULRISE;
  m_osPulWidth  = NGS_PULWIDTH;
  m_osPulFall   = NGS_PULFALL;
  m_osPulPeriod = NGS_PULPERIOD;
}

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

bool  CpntNgSpiceIndSrc::bClear( void )
{
  Component::bClear( );

  ClrValues( );

  return( true );
}

//**************************************************************************************************
// Copy and digest the contents of a Component object.
//
// Argument List :
//   roCpnt - A reference to a Component object
//
// Return Values :
//   A reference to this object

CpntNgSpiceIndSrc & CpntNgSpiceIndSrc::operator = ( const Component & roCpnt )
{
  // Copy the component contents
  (Component &) *this = roCpnt;

  // Parse the value part of the component definition
  bParseValue( );

  return( *this );
}

//**************************************************************************************************
// Copy the contents of an CmdGnuCapGEN object.
//
// Argument List :
//   roCmdGEN - A reference to a CmdGnuCapGEN object
//
// Return Values :
//   A reference to this object

CpntNgSpiceIndSrc & CpntNgSpiceIndSrc::operator = ( const CmdGnuCapGEN & roCmdGEN )
{
  double  df1, df2;
  int     i1;

  m_osSinOffset  = roCmdGEN.m_osOffset;
  m_osSinAmp     = roCmdGEN.m_osAmplitude;

  m_osSinFreq    = roCmdGEN.m_osSinFreq;
  if( CnvtType::bStrToFlt( roCmdGEN.m_osSinPhase, &df1 ) &&
      CnvtType::bStrToFlt( roCmdGEN.m_osSinFreq,  &df2 ) )
  {
    if( CnvtType::bIsEqual( df1, 0.0 ) ) m_osSinDelay = wxT("0.0m");
    else
    {
      i1 = CnvtType::iGetFltRes( );
      CnvtType::bSetFltRes( 2 );
      CnvtType::bFltToStrEng( df1 / (360.0 * df2), m_osSinDelay );
      CnvtType::bSetFltRes( i1 );
      m_osSinDelay.Trim( false );
    }
  }
  else
    m_osSinDelay = wxT("0.0m");
  m_osSinDamp    = wxT("0.0");

  m_osPulInitV  = roCmdGEN.m_osPulInitV;
  m_osPulValue  = roCmdGEN.m_osPulMax;
  m_osPulDelay  = roCmdGEN.m_osPulDelay;
  m_osPulRise   = roCmdGEN.m_osPulRise;
  m_osPulWidth  = roCmdGEN.m_osPulWidth;
  m_osPulFall   = roCmdGEN.m_osPulFall;
  m_osPulPeriod = roCmdGEN.m_osPulPeriod;

  bFormat( );

  return( *this );
}

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

void  CpntNgSpiceIndSrc::Print( const wxString & rosPrefix )
{
  Component::Print( rosPrefix + wxT("Component::") );

  std::cout << rosPrefix.mb_str( ) << "m_osSinOffset   : " << m_osSinOffset.mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osSinAmp      : " << m_osSinAmp   .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osSinFreq     : " << m_osSinFreq  .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osSinDelay    : " << m_osSinDelay .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osSinDamp     : " << m_osSinDamp  .mb_str( ) << '\n';

  std::cout << rosPrefix.mb_str( ) << "m_osPulseInitV  : " << m_osPulInitV .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulseValue  : " << m_osPulValue .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulseDelay  : " << m_osPulDelay .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulseRise   : " << m_osPulRise  .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulseWidth  : " << m_osPulWidth .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulseFall   : " << m_osPulFall  .mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulsePeriod : " << m_osPulPeriod.mb_str( ) << '\n';
}

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