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

#include "latchd.h"
#include "itemlibrary.h"
#include "circuitwidget.h"
#include "simulator.h"
#include "circuit.h"
#include "iopin.h"

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

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

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

LibraryItem* LatchD::libraryItem()
{
    return new LibraryItem(
        tr( "Latch" ),
        "Memory",
        "subc.png",
        "LatchD",
        LatchD::construct ); 
}

LatchD::LatchD( QString type, QString id )
      : LogicComponent( type, id )
{
    m_width  = 4; 
    m_height = 10; 
    m_area = QRect(-(m_width*8/2),-(m_height*8/2), m_width*8, m_height*8 ); 
    
    m_useReset = false; 
    m_tristate = true; 
    createOePin("IR00OE", id+"-Pin_outEnable"); 

    m_clkPin = new IoPin( 180, QPoint(-24, 0 ), m_id+"-Pin_clock", 0, this, input ); 
    m_clkPin->setLabelColor( QColor( 0, 0, 0 ) ); 

    m_resetPin = new IoPin( 180, QPoint(-24, 8 ), m_id+"-Pin_reset", 0, this, input ); 
    setupPin( m_resetPin,"L03RST"); 
    m_resetPin->setVisible( false ); 
    m_otherPin.emplace_back( m_resetPin ); 

    m_channels = 0; 
    setChannels( 8 ); 
    setTriggerStr("Enable"); 

    addPropGroup( { tr("Main"), {
        new IntProp <LatchD>("Channels", tr("Size"),"_bits"
                            , this, &LatchD::channels, &LatchD::setChannels, propNoCopy,"uint" ),

        new StrProp <LatchD>("Trigger", tr("Trigger Type"), LogicComponent::m_triggerList
                            , this, &LatchD::triggerStr, &LatchD::setTriggerStr, propNoCopy,"enum" ),

        new BoolProp<LatchD>("UseRS", tr("Reset Pin"),""
                            , this, &LatchD::pinReset, &LatchD::setPinReset, propNoCopy ),
    }, groupNoCopy } ); 

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

    addPropGroup( { tr("Inputs"),
        IoComponent::inputProps()
        +QList<ComProperty*>({
            new BoolProp<LatchD>("Invert_Inputs", tr("Invert Inputs"),""
                                , this, &LatchD::invertInps, &LatchD::setInvertInps,propNoCopy )
        })
    ,0 } ); 

    addPropGroup( { tr("Outputs"),
          IoComponent::outputProps()
        + IoComponent::outputType()
        +QList<ComProperty*>({
            new BoolProp<LatchD>("Tristate", tr("Tristate"),""
                                 , this, &LatchD::tristate, &LatchD::setTristate ),
        })
    ,0 } ); 

    addPropGroup( { tr("Timing"), IoComponent::edgeProps(),0 } ); 
}
LatchD::~LatchD(){}

void LatchD::stamp()
{
    if( m_trigger != Clock )
    { for( uint i=0;  i<m_inpPin.size();  ++i ) m_inpPin[i]->changeCallBack( this );  }

    m_resetPin->changeCallBack( this ); 
    LogicComponent::stamp(); 
}

void LatchD::voltChanged()
{
    if( m_tristate ) updateOutEnabled(); 
    updateClock(); 

    if( m_useReset && m_resetPin->getInpState() ) m_nextOutVal = 0; 
    else if( m_clkState == Clock_Allow )
    {
        m_nextOutVal = 0; 
        for( uint i=0;  i<m_outPin.size();  ++i )
            if( m_inpPin[i]->getInpState() ) m_nextOutVal |= 1<<i; 
    }
    scheduleOutPuts( this ); 
}

void LatchD::setChannels( int channels )
{
    if( channels == m_channels ) return; 
    if( channels < 1 ) return; 
    
    setNumInps( channels, "D"); 
    setNumOuts( channels ); 
    m_channels = channels; 

    m_height = channels+2; 
    int y = -(m_height/2)*8+8; 

    for( int i=0;  i<channels;  i++ )
    {
        m_inpPin[i]->setY( y+i*8 ); 
        m_outPin[i]->setY( y+i*8 ); 
    }
    updateSize(); 
}

void LatchD::setTristate( bool t )
{
    LogicComponent::setTristate( t ); 
    updateSize(); 
}

void LatchD::setTrigger( trigger_t trigger )
{
    eClockedDevice::setTrigger( trigger ); 
    updateSize(); 
}

void LatchD::setPinReset( bool r )
{
    m_useReset = r; 
    m_resetPin->setVisible( r ); 
    if( !r ) m_resetPin->removeConnector(); 
    updateSize(); 
}

void LatchD::updateSize()
{
    int height = m_height-1; 
    int l = m_useReset ? 1 : 0; 
    if( m_trigger != None ) l++; 
    int d = m_tristate ? 1 : 0; 
    height = (l > d) ? height+l : height+d; 

    int y = -(m_height/2)*8; 
    m_area = QRect( -(m_width/2)*8, y, m_width*8, height*8 ); 
    y += m_channels*8+8; 
    m_clkPin->setY( y ); 
    m_oePin->setY( y ); 
    if( m_trigger != None ) y += 8; 
    m_resetPin->setY( y ); 
    Circuit::self()->update(); 
}
