//**************************************************************************************************
//                                        CpntNgsIndSrc.cpp                                        *
//                                       -------------------                                       *
// Started     : 2008-06-12                                                                        *
// Last Update : 2014-12-15                                                                        *
// Copyright   : (C) 2008-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 "CpntNgsIndSrc.hpp"

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

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

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

CpntNgsIndSrc::~CpntNgsIndSrc( )
{
}

//**************************************************************************************************
// 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  CpntNgsIndSrc::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_osPulInitial = ostk1.GetNextToken( );
    m_osPulMax     = 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  CpntNgsIndSrc::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_osPulMax, &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_osPulInitial << wxT(' ');
    m_osValue << m_osPulMax     << 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  CpntNgsIndSrc::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_osPulInitial, &dfPulInitial ) )
    bSetErrMsg( wxT("initial pulse value is invalid") );
  if( ! CnvtType::bStrToFlt( m_osPulMax,     &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 the pulse level values
  if( dfPulInitial > dfPulMax )
    bSetErrMsg( wxT("initial pulse value is greater than the maximum value") );

  // 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  CpntNgsIndSrc::ClrValues( void )
{
  m_osSinOffset  = NGS_SINOFFSET;
  m_osSinAmp     = NGS_SINAMP;
  m_osSinFreq    = NGS_SINFREQ;
  m_osSinDelay   = NGS_SINDELAY;
  m_osSinDamp    = NGS_SINDAMP;

  m_osPulInitial = NGS_PULINITIAL;
  m_osPulMax     = NGS_PULMAX;
  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  CpntNgsIndSrc::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

CpntNgsIndSrc & CpntNgsIndSrc::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

CpntNgsIndSrc & CpntNgsIndSrc::operator = ( const CmdGnuCapGEN & roCmdGEN )
{
  double  df1, df2;

  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 ) )
    CnvtType::bFltToStr( df1 / (360.0 * df2), m_osSinDelay );
  else
    m_osSinDelay = wxT("0.0");
  m_osSinDamp    = wxT("0.0");

  m_osPulInitial = roCmdGEN.m_osPulInitial;
  m_osPulMax     = 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  CpntNgsIndSrc::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_osPulInitial.mb_str( ) << '\n';
  std::cout << rosPrefix.mb_str( ) << "m_osPulseMaxV   : " << m_osPulMax    .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';
}

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