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

#include "fulladder.h"
#include "itemlibrary.h"
#include "circuit.h"
#include "iopin.h"

#include "intprop.h"

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

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

LibraryItem* FullAdder::libraryItem()
{
    return new LibraryItem(
        tr("Full Adder"),
        "Arithmetic",
        "2to2.png",
        "FullAdder",
        FullAdder::construct );
}

FullAdder::FullAdder( QString type, QString id)
         : IoComponent( type, id )
         , eElement( id )
{
    m_width  = 3;
    m_height = 4;
    m_bits   = 0;

    m_area = QRect(-8,-(m_height/2)*8, m_width*8, m_height*8 );
    m_inpPin.resize( 1 );
    m_outPin.resize( 1 );

    m_inpPin[0]  = m_ciPin = createPin("IR01Ci", m_id+"-ci");
    m_outPin[0] = m_coPin = createPin("OR03Co", m_id+"-co");
    setBits( 1 );

    addPropGroup( { tr("Main"), {
        new IntProp <FullAdder>("Bits", tr("Size"),"_bits"
                            , this, &FullAdder::bits, &FullAdder::setBits, propNoCopy,"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 } );
}
FullAdder::~FullAdder(){}

void FullAdder::stamp()
{
    IoComponent::initState();
    for( IoPin* pin : m_inpPin ) pin->changeCallBack( this );
}

void FullAdder::voltChanged()
{
    int Ci = m_ciPin->getInpState() ? 1 : 0;

    int A=0, B=0;
    for( int i=0; i<m_bits; ++i )
    {
        if( m_inpPin[1+i]->getInpState()        ) A |= 1<<i;
        if( m_inpPin[1+i+m_bits]->getInpState() ) B |= 1<<i;
    }
    m_nextOutVal = A + B + Ci;

    bool Co = m_nextOutVal & 1<<m_bits; // Move Carry out to first bit: m_outPin[0] = m_coPin
    m_nextOutVal <<= 1;
    if( Co ) m_nextOutVal |= 1;

    scheduleOutPuts( this );
}

void FullAdder::setBits( int b )
{
    if( b < 1 ) b = 1;
    if( b == m_bits ) return;

    setNumInps( 1+b*2, "I", 0, 10 ); // Set a label to get Pin 1 full cell below edge
    setNumOuts( 1+b  , "S", 0, 10 );

    for( int i=0; i<b; ++i )
    {
        QString iStr = ( b > 1 ) ? QString::number(i) : "";

        m_inpPin[1+i]->setY( m_area.y()+i*8+8 );
        m_inpPin[1+i]->setLabelText("A"+iStr );
        m_inpPin[1+b+i]->setLabelText("B"+iStr );
        m_outPin[1+i]->setLabelText("S"+iStr );
    }
    m_coPin->setY( m_area.y()+b*8+16 );
    m_bits = b;
    Circuit::self()->update();
}

Pin* FullAdder::getPin( QString pinName )
{
    if( pinName == "in0" ) return m_inpPin[1];
    if( pinName == "in1" ) return m_inpPin[2];
    if( pinName == "in2" ) return m_ciPin;
    if( pinName == "out0") return m_outPin[1];
    if( pinName == "out1") return m_coPin;
    /// TODO: find pins from number in pinName
    return nullptr;
}
