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

#include <math.h>

#include <QGraphicsProxyWidget>
#include <QPainter>

#include "ky040.h"
#include "customdial.h"
#include "custombutton.h"
#include "iopin.h"
#include "simulator.h"
#include "circuit.h"
#include "circuitwidget.h"
#include "itemlibrary.h"

#include "intprop.h"

#define WIDTH 40
#define HEIGHT 56
#define GAP 0
#define DIAL_SIZE 36
#define RESOLUTION 10
#define VIN 5

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

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

LibraryItem* KY040::libraryItem()
{
    return new LibraryItem(
        tr("Rotary Encoder (relative)"),
        "Peripherals",
        "ky-040.png",
        "KY040",
        KY040::construct); 
}

KY040::KY040( QString type, QString id )
     : Component( type, id )
     , eElement( id )
{
    m_graphical = true; 
    m_changed = false; 
    m_area = QRect( -WIDTH/2, -HEIGHT/2 + GAP, WIDTH, HEIGHT ); 

    m_dial = new CustomDial(); 
    m_dial->setFixedSize( DIAL_SIZE, DIAL_SIZE ); 
    m_dial->setWrapping( true ); 
    m_dial->setMinimum( 1 ); 
    m_dial->setValue( 1 ); 
    m_dial->setSingleStep( 1 ); 
    m_dial->setNotchTarget( 10 ); 
    setSteps( 20 ); 
    
    m_proxy = Circuit::self()->addWidget( m_dial ); 
    m_proxy->setParentItem( this ); 
    m_proxy->setPos( QPoint(-WIDTH/2+(WIDTH-DIAL_SIZE)/2, -HEIGHT/2+(WIDTH-DIAL_SIZE)/2+GAP) ); 
    
    m_button = new CustomButton(); 
    m_button->setMaximumSize( 10, 10 ); 
    m_button->setGeometry(-10,-10, 10, 10 ); 
    
    m_proxy_button = Circuit::self()->addWidget( m_button ); 
    m_proxy_button->setParentItem( this ); 
    m_proxy_button->setPos( QPoint(8, HEIGHT/2-13) ); 

    m_pin.resize(3); 

    m_pin[0] = m_pinA = new IoPin( 270, QPoint(4,36), id+"-clk", 0, this, output ); 
    m_pinA->setOutHighV( VIN ); 
    m_pinA->setLabelText( "CLK" ); 

    m_pin[1] = m_pinB = new IoPin( 270, QPoint(-4,36), id+"-dt", 0, this, output ); 
    m_pinB->setOutHighV( VIN ); 
    m_pinB->setLabelText( "DT" ); 

    m_pin[2] = m_sw = new IoPin( 270, QPoint(-12,36), id+"-sw", 0, this, output ); 
    m_sw->setOutHighV( VIN ); 
    m_sw->setOutputImp( 2000 ); 
    m_sw->setLabelText( "SW" ); 

    setAngle( 90 ); 
    setLabelPos(-34, 20,-90 ); 

    Simulator::self()->addToUpdateList( this ); 

    QObject::connect( m_button, &QToolButton::pressed , [=](){ onbuttonchanged();  } ); 
    QObject::connect( m_button, &QToolButton::released, [=](){ onbuttonchanged();  } ); 

    addPropGroup( { tr("Main"), {
    new IntProp<KY040>("Steps", tr("Steps per Rotation"), ""
                      , this, &KY040::steps, &KY040::setSteps,0,"uint" )
    }, groupNoCopy} ); 
}
KY040::~KY040(){}

void KY040::stamp()
{
    m_dial->setValue( 1 ); 
    m_prevDialVal = 1; 

    m_posA = 5; 
    m_posB = 0; 

    m_stateA = false; 
    m_stateB = false; 

    m_pinA->setOutState( false ); 
    m_pinB->setOutState( false ); 
    m_sw->setOutState( true ); 
}

void KY040::updateStep()
{
    Simulator::self()->cancelEvents( this ); 

    int val = m_dial->value(); 
    if( m_prevDialVal != val )
    {
        int max = m_dial->maximum() ; 
        int bot = max/4; 
        int top = max-bot; 

        if     ( m_prevDialVal > top && val < bot ) m_prevDialVal -= max; 
        else if( m_prevDialVal < bot && val > top ) m_prevDialVal += max; 
        int dialDelta = val - m_prevDialVal; 
        m_prevDialVal = val; 

        if( dialDelta > 0 ) m_delta = 1; // Clockwise
        else                m_delta = -1; 

        uint64_t spf = Simulator::self()->psPerFrame(); 
        m_stepDelta = spf/fabs(dialDelta); 

        //qDebug()<<"\n" << dialDelta << m_stepDelta/1e6; 

        Simulator::self()->addEvent( m_stepDelta, this ); 
    }

    if( !m_changed ) return; 
    bool closed = m_button->isDown(); 
    m_sw->setOutputImp( closed ? 1e-3 : 2000 ); 
    m_sw->setOutState( !closed ); 
    m_changed = false; 
}

void KY040::runEvent()
{
    m_posA += m_delta; 
    if     ( m_posA > 19 ) m_posA = 0; 
    else if( m_posA < 0  ) m_posA = 19; 
    m_posB += m_delta; 
    if     ( m_posB > 19 ) m_posB = 0; 
    else if( m_posB < 0  ) m_posB = 19; 

    bool stateA = m_posA > 9; 
    bool stateB = m_posB > 9; 

    if( m_stateA != stateA )
    {
        m_stateA = stateA; 
        m_pinA->setOutState( stateA ); 
    }
    if( m_stateB != stateB )
    {
        m_stateB = stateB; 
        m_pinB->setOutState( stateB ); 
    }
    Simulator::self()->addEvent( m_stepDelta, this ); 
}

void KY040::onbuttonchanged()
{
    m_changed = true; 
    update(); 
}

void KY040::setSteps( int s )
{
    if( Simulator::self()->isRunning() ) CircuitWidget::self()->powerCircOff(); 
    m_steps = s; 
    //m_deltaS = 5; 
    m_dial->setMaximum( s*2*RESOLUTION ); 
}

void KY040::setLinkedValue( double v, int )
{
    double val = v*m_steps*2*RESOLUTION/1000; 
    m_dial->setValue( val ); 
    m_changed = true; 
}

void KY040::paint( QPainter* p, const QStyleOptionGraphicsItem* o, QWidget* w )
{
    Component::paint( p, o, w ); 
    
    p->setBrush( QColor( 58, 60, 52 ) ); 
    p->drawRoundedRect( m_area, 2, 2 ); 

    Component::paintSelected( p ); 
}
