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

#include "avrcomparator.h"
#include "datautils.h"
#include "regwatcher.h"
#include "e_mcu.h"
#include "mcupin.h"

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

void AvrComp::setup()
{
    m_ACD  = getRegBits("ACD" , m_mcu ); 
    m_ACBG = getRegBits("ACBG", m_mcu ); 
    m_ACO  = getRegBits("ACO" , m_mcu ); 
    m_ACI  = getRegBits("ACI" , m_mcu ); 
    m_ACIE = getRegBits("ACIE", m_mcu ); 
    m_ACIC = getRegBits("ACIC", m_mcu ); 
    m_ACIS = getRegBits("ACIS0,ACIS1", m_mcu ); 

    m_AIN0D = getRegBits("AIN0D", m_mcu ); 
    m_AIN1D = getRegBits("AIN1D", m_mcu ); 

    watchRegNames("ACSR", R_READ, this, &AvrComp::readACO, m_mcu );  // Trigger a compare when ACO or ACI is read (ACSR)
}

void AvrComp::initialize()
{
    m_acie = false; 
    m_acoe = false; 
    m_compOut = false; 

    m_pinP = m_pins[0]; 
    m_pinN = m_pins[1]; 
}

void AvrComp::voltChanged()
{
    compare(); 
}

void AvrComp::configureA( uint8_t newACSR ) // ACSR is being written
{
    m_enabled = !getRegBitsBool( newACSR, m_ACD ); 

    m_acie = getRegBitsBool( newACSR, m_ACIE );   // Enable interrupt
    changeCallbacks(); 

    m_fixVref = getRegBitsVal( newACSR, m_ACBG ); 

    /// TODO: ACIC: Analog Comparator Input Capture Enable

    m_mode = getRegBitsVal( newACSR, m_ACIS ); 

    if( !m_enabled ) m_mcu->m_regOverride = newACSR & ~m_ACO.mask;  // Clear ACO
}

void AvrComp::configureB( uint8_t newAIND ) // AIN0D,AIN1D being written
{
    /// TODO: Disable Digital Input buffer.
    /// The corresponding PIN Register bit will always read as zero when this bit is set
}

void AvrComp::configureC( uint8_t newACOE ) // mega328PB
{
    if( m_pins.size() < 3 ) return; 

    if( newACOE ) m_pinOut = m_pins[2]; 
    else          m_pinOut = nullptr; 
    m_pins[2]->controlPin( newACOE, newACOE ); 

    m_acoe = newACOE; 
    changeCallbacks(); 
}

void AvrComp::readACO( uint8_t )
{
    if( !m_enabled ) return; 
    compare(); 
    m_mcu->m_regOverride = *m_ACO.reg;  // Clear ACO
}

void AvrComp::compare( uint8_t ) //
{
    if( !m_enabled ) return; 

    double vRef = m_fixVref ? 1.1 : m_pinP->getVoltage(); 
    bool compOut = vRef > m_pinN->getVoltage() ; 
    bool rising = !m_compOut && compOut; 

    if( m_compOut != compOut )
    {
        if( compOut ) setRegBits( m_ACO ); 
        else          clearRegBits( m_ACO ); 

        switch( m_mode ){
            case 0: m_interrupt->raise();                break;  // Comparator Interrupt on Output Toggle.
            case 1:                                     break;  // Reserved
            case 2: if( !rising ) m_interrupt->raise();  break;  // Comparator Interrupt on Falling Output Edge.
            case 3: if(  rising ) m_interrupt->raise();         // Comparator Interrupt on Rising  Output Edge.
        }
        m_compOut = compOut; 
        if( m_pinOut ) m_pinOut->scheduleState( compOut, 0 ); 
    }
}

void AvrComp::setPinN( McuPin* pin )
{
    if( !pin ) pin = m_pins[1]; 

    m_pinN->changeCallBack( this, false ); 
    m_pinN = pin; 

    changeCallbacks(); 
    compare(); 
}

void AvrComp::changeCallbacks()
{
    m_pinP->changeCallBack( this, m_enabled && (m_acie || m_acoe) ); 
    m_pinN->changeCallBack( this, m_enabled && (m_acie || m_acoe) ); 
}
