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

#include "counter.h"
#include "itemlibrary.h"
#include "iopin.h"

#include "intprop.h"
#include "boolprop.h"

#define tr(str) simulideTr("Counter",str)

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

LibraryItem* Counter::libraryItem()
{
    return new LibraryItem(
        tr("Simple Counter"),
        "Arithmetic",
        "2to1.png",
        "Counter",
        Counter::construct );
}

Counter::Counter( QString type, QString id)
       : LogicComponent( type, id )
{
    m_TopValue = 1;
    m_width  = 3;
    m_height = 3;

    init({         // Inputs:
            "IL01>",
            "IL02R",
            "IU01S",
                   // Outputs:
            "OR01Q"
        });

    m_clkPin = m_inpPin[0];     // eClockedDevice
    m_rstPin = m_inpPin[1];
    m_setPin = m_inpPin[2];

    //setSrInv( true );            // Invert Reset Pin
    m_rstPin->setInverted( true );
    m_setPin->setInverted( true );
    useSetPin( false );          // Don't use Set Pin

    addPropGroup( { tr("Main"), {
        new BoolProp<Counter>("Pin_SET", tr("Use Set Pin"),""
                                , this, &Counter::pinSet,&Counter::useSetPin, propNoCopy ),

        //new BoolProp<Counter>("Clock_Inverted", tr("Clock Inverted"),""
        //                        , this, &Counter::clockInv, &Counter::setClockInv ),

        //new BoolProp<Counter>("Reset_Inverted", tr("Set/Reset Inverted"),""
        //                        , this, &Counter::srInv, &Counter::setSrInv ),

        new IntProp <Counter>("Max_Value", tr("Count to"),""
                                , this, &Counter::maxVal, &Counter::setMaxVal,0,"uint" ),
    },groupNoCopy} );

    appendPropGroup( tr("Main"), IoComponent::familyProps() );

    addPropGroup( { tr("Inputs"), IoComponent::inputProps(),0 } );
    addPropGroup( { tr("Outputs"), IoComponent::outputProps(),0 } );
    addPropGroup( { tr("Timing"), IoComponent::edgeProps(),0 } );
}
Counter::~Counter(){}

bool Counter::propNotFound( QString prop, QString val )
{
    if( prop =="Clock_Inverted" ) // Old circuits
    {
        m_clkPin->setInverted( val == "true" );
    }
    else if( prop =="Reset_Inverted" ) // Old circuits
    {
        bool invert = (val == "true");

        m_setPin->setInverted( invert );
        m_rstPin->setInverted( invert );
    }
    else return false;

    return true;
}


void Counter::stamp()
{
    m_Counter = 0;
    m_rstPin->changeCallBack( this );
    m_setPin->changeCallBack( this );
    LogicComponent::stamp();
}

void Counter::voltChanged()
{
    updateClock();
    bool clkRising = ( m_clkState == Clock_Rising );

    if( m_rstPin->getInpState() ) // Reset
    {
       m_Counter = 0;
       m_nextOutVal = 0;
    }
    else if( m_pinSet && m_setPin->getInpState() ) // Set
    {
       m_Counter = m_TopValue;
       m_nextOutVal = 1;
    }
    else if( clkRising )
    {
        m_Counter++;

        if(      m_Counter == m_TopValue ) m_nextOutVal = 1;
        else if( m_Counter >  m_TopValue )
        {
            m_Counter = 0;
            m_nextOutVal = 0;
    }   }
    IoComponent::scheduleOutPuts( this );
}

/*void Counter::setSrInv( bool inv )
{
    m_resetInv = inv;
    m_rstPin->setInverted( inv );
    m_setPin->setInverted( inv );
}*/

void Counter::useSetPin( bool set )
{
    m_pinSet = set;
    if( !set ) m_setPin->removeConnector();
    m_setPin->setVisible( set );
}
