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

#include "piccomparator.h"
#include "datautils.h"
#include "e_mcu.h"
#include "mcupin.h"
#include "mcuvref.h"

PicComp* PicComp::createComparator( eMcu* mcu, QString name, int type ) // Static
{
    switch( type ){
        case 01: return new PicComp01( mcu, name );
        case 02: return new PicComp02( mcu, name );
        case 03: return new PicComp03( mcu, name );
        case 11: return new PicComp11( mcu, name );
        case 12: return new PicComp12( mcu, name );
        default: return nullptr;
    }
}

PicComp::PicComp( eMcu* mcu, QString name )
       : McuComp( mcu, name )
{
}
PicComp::~PicComp(){}

void PicComp::setup()
{
    McuVref* vref = m_mcu->vrefModule();
    if( vref ) vref->addCallBack( this, true ); // Vref Module will update vref if changed.
}

void PicComp::initialize()
{
    McuComp::initialize();
    m_pinP = nullptr;
    m_pinN = nullptr;
    m_pinOut = nullptr;
    m_enabled = false;
}

void PicComp::voltChanged()
{
    if( !m_enabled ) return;

    double vRef = m_fixVref ? m_vref : m_pinP->getVoltage();
    bool compOut = vRef > m_pinN->getVoltage() ;
    if( m_inv ) compOut = !compOut;

    if( m_compOut != compOut )
    {
        m_compOut = compOut;
        m_interrupt->raise();
        if( m_pinOut ) m_pinOut->setOutState( m_compOut );

        if( m_compOut ) setRegBits( m_COUT );
        else            clearRegBits( m_COUT );
    }
}

void PicComp::connect( McuPin* pinN, McuPin* pinP, McuPin* pinOut )
{
    if( pinN != m_pinN )
    {
        if( m_pinN ) m_pinN->changeCallBack( this, false );
        if( pinN )   pinN->changeCallBack( this, true );
        m_pinN = pinN;
    }
    if( pinP != m_pinP )
    {
        if( m_pinP ) m_pinP->changeCallBack( this, false );
        if( pinP )   pinP->changeCallBack( this, true );
        m_pinP = pinP;
    }
    if( pinOut != m_pinOut )
    {
        if( m_pinOut ) m_pinOut->controlPin( false, false );
        if( pinOut )   pinOut->controlPin( true, false );

        m_pinOut = pinOut;
    }
    if( m_pinN ) voltChanged(); // Update Comparator state
}

//-------------------------------------------------------------
// Type 0: 16f627 comparators  --------------------------------

PicComp0::PicComp0( eMcu* mcu, QString name )
        : PicComp( mcu, name )
{
}
PicComp0::~PicComp0(){}

void PicComp0::setup()
{
    PicComp::setup();

    m_CM  = getRegBits("CM0,CM1,CM2", m_mcu );
    m_CIS = getRegBits("CIS", m_mcu );
}

void PicComp0::configureA( uint8_t newCMCON )
{
    m_cis = getRegBitsBool( newCMCON, m_CIS );
    m_inv = getRegBitsBool( newCMCON, m_CINV );

    uint8_t mode = getRegBitsVal( newCMCON, m_CM );
    if( mode != m_mode )
    {
        m_mode = mode;
        m_fixVref = false;
        m_enabled = true;
        setMode( mode );
    }
    voltChanged();
}

//-------------------------------------------------------------
// Type 01: 16f627 comparator 1 -------------------------------

PicComp01::PicComp01( eMcu* mcu, QString name )
         : PicComp0( mcu, name )
{
}
PicComp01::~PicComp01(){}

void PicComp01::setup()
{
    PicComp0::setup();

    m_CINV = getRegBits("C1INV", m_mcu );
    m_COUT = getRegBits("C1OUT", m_mcu );
}

void PicComp01::setMode( uint8_t mode )
{
    McuPin* pinN = m_cis ? m_pins[1] : m_pins[0];

    switch ( mode ) {
        case 0: m_enabled = false; connect( m_pins[0], m_pins[1], nullptr   ); break;
        case 1:                    connect( pinN     , m_pins[2], nullptr   ); break;
        case 2: m_fixVref = true;  connect( pinN     , nullptr  , nullptr   ); break;
        case 3:                    connect( m_pins[0], m_pins[2], nullptr   ); break;
        case 4:                    connect( m_pins[0], m_pins[1], nullptr   ); break;
        case 5: m_enabled = false; connect( nullptr  , nullptr  , nullptr   ); break;
        case 6:                    connect( m_pins[0], m_pins[2], m_pins[1] ); break;
        case 7: m_enabled = false; connect( nullptr  , nullptr  , nullptr   ); break;
    }
}

//-------------------------------------------------------------
// Type 02: 16f627 comparator 2 -------------------------------

PicComp02::PicComp02( eMcu* mcu, QString name )
         : PicComp0( mcu, name )
{
}
PicComp02::~PicComp02(){}

void PicComp02::setup()
{
    PicComp0::setup();

    m_CINV = getRegBits("C2INV", m_mcu );
    m_COUT = getRegBits("C2OUT", m_mcu );
}

void PicComp02::setMode( uint8_t mode )
{
    McuPin* pinN = m_cis ? m_pins[1] : m_pins[0];

    switch ( mode ) {
        case 0: m_enabled = false;
        case 1:                    connect( m_pins[0], m_pins[1], nullptr      ); break;
        case 2: m_fixVref = true;  connect( pinN     , nullptr     , nullptr      ); break;
        case 3:                                                                // Fallthrough
        case 4:                                                                // Fallthrough
        case 5:                    connect( m_pins[0], m_pins[1], m_pins[2] ); break;
        case 6:                    connect( m_pins[0], m_pins[1], nullptr      ); break;
        case 7: m_enabled = false; connect( nullptr     , nullptr     , nullptr      ); break;
    }
}

//-------------------------------------------------------------
// Type 01: 16f629 comparator ---------------------------------

PicComp03::PicComp03( eMcu* mcu, QString name )
         : PicComp0( mcu, name )
{
}
PicComp03::~PicComp03(){}

void PicComp03::setup()
{
    PicComp0::setup();

    m_CINV = getRegBits("CINV", m_mcu );
    m_COUT = getRegBits("COUT", m_mcu );
}

void PicComp03::setMode( uint8_t mode )
{
    McuPin* pinO = nullptr;
    McuPin* pinN = m_cis ? m_pins[1] : m_pins[0];

    switch ( mode ) {
        case 0: m_enabled = false; connect( m_pins[0], m_pins[1], nullptr      ); break;
        case 1:                    connect( m_pins[0], m_pins[1], m_pins[2] ); break;
        case 2:                    connect( m_pins[0], m_pins[1], nullptr      ); break;
        case 3: m_fixVref = true;  connect( m_pins[0], nullptr     , m_pins[2] ); break;
        case 4: m_fixVref = true;  connect( m_pins[0], nullptr     , nullptr      ); break;
        case 5:                                             pinO = m_pins[2];  // Fallthrough
        case 6: m_fixVref = true;  connect( pinN     , nullptr     , pinO      ); break;
        case 7: m_enabled = false; connect( nullptr     , nullptr     , nullptr      ); break;
    }
}

//-------------------------------------------------------------
// Type 1: 16f88x comparators  --------------------------------

PicComp1::PicComp1( eMcu* mcu, QString name )
        : PicComp( mcu, name )
{
}
PicComp1::~PicComp1(){}

void PicComp1::setup()
{
    PicComp::setup();

    QString n = m_name.right(1); // name="Comp01" => n="1"

    m_CxON = getRegBits("C"+n+"ON", m_mcu );
    m_COUT = getRegBits("C"+n+"OUT", m_mcu );
    m_CxOE = getRegBits("C"+n+"OE", m_mcu );
    m_CINV = getRegBits("C"+n+"POL", m_mcu );
    m_CxR  = getRegBits("C"+n+"R", m_mcu );
    m_CxCH = getRegBits("C"+n+"CH0,C"+n+"CH1", m_mcu );
}

void PicComp1::configureA( uint8_t newCMxCON0 )
{
    if( newCMxCON0 == m_mode ) return;
    m_mode = newCMxCON0;

    m_enabled = getRegBitsBool( newCMxCON0, m_CxON );
    if( !m_enabled )
    {
        connect( nullptr, nullptr, nullptr );
        return;
    }
    bool OE = getRegBitsBool( newCMxCON0, m_CxOE );
    McuPin* pinOut = OE ? m_pins[5] : nullptr;

    m_inv     = getRegBitsBool( newCMxCON0, m_CINV );
    m_fixVref = getRegBitsBool( newCMxCON0, m_CxR );

    uint8_t channel = getRegBitsVal( newCMxCON0, m_CxCH );
    connect( m_pins[channel], m_pins[4], pinOut );
}

//-------------------------------------------------------------
// Type 11: 16f88x comparator 1 -------------------------------

PicComp11::PicComp11( eMcu* mcu, QString name )
         : PicComp1( mcu, name )
{

}
PicComp11::~PicComp11(){}

void PicComp11::configureC( uint8_t newSRCON )
{

}

//-------------------------------------------------------------
// Type 12: 16f88x comparator 2 -------------------------------

PicComp12::PicComp12( eMcu* mcu, QString name )
         : PicComp1( mcu, name )
{

}
PicComp12::~PicComp12(){}

void PicComp12::configureB( uint8_t newCM2CON1 )
{

}

void PicComp12::configureC( uint8_t newSRCON )
{

}

//-------------------------------------------------------------
// Type 20: 16f1826 comparators  ------------------------------

PicComp20::PicComp20( eMcu* mcu, QString name )
         : PicComp( mcu, name )
{

}
PicComp20::~PicComp20(){}

void PicComp20::configureA( uint8_t newCMxCON0 )
{

}

void PicComp20::configureB( uint8_t newCMxCON1 )
{

}
