/***************************************************************************
 *   Copyright (C) 2012 by Santiago González                               *
 *                                                                         *
 ***( see copyright.txt file at root folder )*******************************/

#include <QtMath>
#include <QPainter>

#include "lm555.h"
#include "simulator.h"
#include "itemlibrary.h"
#include "iopin.h"
#include "e-node.h"

Component* Lm555::construct( QString type, QString id )
{ return new Lm555( type, id );  }

LibraryItem* Lm555::libraryItem()
{
    return new LibraryItem(
        "LM555",
        "Other Logic",
        "ic2.png",
        "Lm555",
        Lm555::construct ); 
}

Lm555::Lm555( QString type, QString id )
     : Component( type, id )
     , eElement( id )
     , m_ePinA( (id+"-ePinA"), 0 )
     , m_ePinB( (id+"-ePinB"), 0 )
     , m_ePinC( (id+"-ePinC"), 0 )
     , m_ePinD( (id+"-ePinD"), 0 )
     , m_resA(  (id+"-resA") )
     , m_resB(  (id+"-resB") )
     , m_resD(  (id+"-resD") )
{
    m_area = QRect( 0, 0, 8*4, 8*5 ); 
    //m_color = QColor( 50, 50, 70 ); 
    
    m_pin.resize( 8 ); 
    
    m_Gnd = new Pin( 180, QPoint(-8, 8*1), id+"-ePin0", 0, this ); 
    m_Gnd->setLabelColor( QColor( 0, 0, 0 ) ); 
    m_Gnd->setLabelText( "Gnd" ); 
    m_pin[0] = m_Gnd; 

    m_trigger = new Pin( 180, QPoint(-8, 8*2), id+"-ePin1", 1, this ); 
    m_trigger->setLabelColor( QColor( 0, 0, 0 ) ); 
    m_trigger->setLabelText( "Trg" ); 
    m_pin[1] = m_trigger; 

    m_output = new IoPin( 180, QPoint(-8, 8*3), id+"-ePin2", 2, this, output ); 
    m_output->setLabelColor( QColor( 0, 0, 0 ) ); 
    m_output->setLabelText( "Out" ); 
    m_output->setOutputImp( 3.5 ); 
    m_output->setOutLowV( 0 ); 
    m_output->setOutHighV( 5-1.3 ); 
    m_output->setOutState( true ); 
    m_pin[2] = m_output; 

    m_Reset = new Pin( 180, QPoint(-8, 8*4), id+"-ePin3", 3, this ); 
    m_Reset->setLabelColor( QColor( 0, 0, 0 ) ); 
    m_Reset->setLabelText( "Rst" ); 
    m_pin[3] = m_Reset; 

    m_cv = new Pin( 0, QPoint(4*8+8, 8*4), id+"-ePin4", 4, this ); 
    m_cv->setLabelColor( QColor( 0, 0, 0 ) ); 
    m_cv->setLabelText( "CV" ); 
    m_pin[4] = m_cv; 

    m_threshold = new Pin( 0, QPoint(4*8+8, 8*3), id+"-ePin5", 5, this ); 
    m_threshold->setLabelColor( QColor( 0, 0, 0 ) ); 
    m_threshold->setLabelText( "Thr" ); 
    m_pin[5] = m_threshold; 

    m_discharge = new Pin( 0, QPoint(4*8+8, 8*2), id+"-ePin6", 6, this ); 
    m_discharge->setLabelColor( QColor( 0, 0, 0 ) ); 
    m_discharge->setLabelText( "Dis" ); 
    m_pin[6] = m_discharge; 

    m_Vcc = new Pin( 0, QPoint(4*8+8, 8*1),id+"-ePin7", 7, this ); 
    m_Vcc->setLabelColor( QColor( 0, 0, 0 ) ); 
    m_Vcc->setLabelText( "Vcc" ); 
    m_pin[7] = m_Vcc; 

    m_thrEnode = nullptr; 
    m_resA.setEpin( 0, m_Vcc ); 
    m_resA.setEpin( 1, &m_ePinA ); 

    m_resB.setEpin( 0, &m_ePinB ); 
    m_resB.setEpin( 1, &m_ePinC ); 

    m_resD.setEpin( 0, &m_ePinD ); 
    m_resD.setEpin( 1, m_discharge ); 

    m_propDelay = 10*1000;  // 10 ns
}
Lm555::~Lm555(){}

void Lm555::initialize()
{
    m_outState = false; 
    m_voltNeg = 0; 
    m_voltPos = 0; 

    if( !Simulator::self()->isRunning() ) return; 
    m_thrEnode = m_cv->getEnode();                // Control Voltage Pin
    if( !m_thrEnode )                            // Not connected: Create threshold eNode
        m_thrEnode = new eNode( m_id+"-threNode" ); 
}

void Lm555::stamp()
{
    m_cv->setEnode( m_thrEnode ); 
    m_ePinA.setEnode( m_thrEnode );    // Set eNode to internal eResistors ePins
    m_ePinB.setEnode( m_thrEnode ); 

    eNode* enod = m_Gnd->getEnode();   // Gnd Pin
    m_ePinC.setEnode( enod );          // Set eNode to internal eResistors ePins
    m_ePinD.setEnode( enod ); 

    m_resA.setResistance( 5000 ); 
    m_resB.setResistance( 10000 ); 
    m_resD.setResistance( 1 ); 

    for( int i=0;  i<8;  ++i )
    {
        if( (i == 2) || (i == 6) ) continue;  // Output or Discharge
        if( m_pin[i]->isConnected() ) m_pin[i]->changeCallBack( this ); //  ->getEnode()->addToNoLinList(this); 
    }
}

void Lm555::voltChanged()
{
    if( !m_Gnd->isConnected() || !m_Vcc->isConnected() ) return; 

    bool changed = false; 
    double voltPos = m_Vcc->getVoltage(); 
    double voltNeg = m_Gnd->getVoltage(); 

    if( qFabs(voltNeg-m_voltNeg) > 1e-3 )
    {
        m_output->setOutLowV( voltNeg ); 
        m_voltNeg = voltNeg; 
        changed = true; 
    }
    if( qFabs(voltPos-m_voltPos) > 1e-3 )
    {
        double voltHight = voltPos - 1.3; 
        if( voltHight < m_voltNeg ) voltHight = m_voltNeg; 
        m_output->setOutHighV( voltHight ); 

        m_voltPos = voltPos; 
        changed = true; 
    }

    double resetVolt = m_Reset->getVoltage() - m_voltNeg;   // Reset Voltage
    double refThre   = m_thrEnode->getVolt();               // Voltage at Threshold Ref node.
    double refTrig   = m_voltNeg + (refThre-m_voltNeg)/2;   // Voltage at Trigger Ref node.

    bool outState = m_outState; 

    if( resetVolt < 0.7 ) outState = false;                    // Reset
    else{
        bool TrigComp = refTrig > m_trigger->getVoltage(); 
        if( TrigComp ) outState = true;                        // Trigger
        else{
            bool ThrComp = refThre < m_threshold->getVoltage(); 
            if( ThrComp ) outState = TrigComp;                 // Threshold
        }
    }

    if( outState != m_outState){
        m_outState = outState; 
        changed = true; 
    }
    if( changed ) Simulator::self()->addEvent( m_propDelay, this ); 
}

void Lm555::runEvent()
{
    m_output->setOutState( m_outState ); 
    m_resD.setResistance( m_outState ? high_imp : 1 ); 
}

void Lm555::paint( QPainter* p, const QStyleOptionGraphicsItem* o, QWidget* w )
{
    Component::paint( p, o, w ); 

    p->drawRoundedRect( m_area, 1, 1); 
    
    //p->setPen( QColor( 170, 170, 150 ) ); 
    p->drawArc( boundingRect().width()/2-6, -4, 8, 8, 0, -2880 /* -16*180 */ ); 

    Component::paintSelected( p ); 
}
