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

#include <QtMath>
#include <QPainter>

#include "stepper.h"
#include "simulator.h"
#include "connector.h"
#include "itemlibrary.h"

#include "doubleprop.h"
#include "boolprop.h"
#include "intprop.h"

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

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

LibraryItem* Stepper::libraryItem()
{
    return new LibraryItem(
        tr("Stepper"),
        "Motors",
        "steeper.png",
        "Stepper",
        Stepper::construct );
}

Stepper::Stepper( QString type, QString id )
       : LinkerComponent( type, id )
       , eElement( (id+"-eElement") )
       , m_resA1( (id+"-eEresistorA1") )
       , m_resA2( (id+"-eEresistorA2") )
       , m_resB1( (id+"-eEresistorB1") )
       , m_resB2( (id+"-eEresistorB2") )
       , m_pinA1( 180, QPoint(-72,-32), id+"-PinA1", 0, this )
       , m_pinA2( 180, QPoint(-72, 16), id+"-PinA2", 0, this )
       , m_pinCo( 180, QPoint(-72, 0 ), id+"-PinCo", 0, this )
       , m_pinB1( 180, QPoint(-72,-16), id+"-PinB1", 0, this )
       , m_pinB2( 180, QPoint(-72, 32), id+"-PinB2", 0, this )
       , m_ePinA1Co( (id+"-ePinA1Co"), 0 )
       , m_ePinA2Co( (id+"-ePinA2Co"), 0 )
       , m_ePinB1Co( (id+"-ePinB1Co"), 0 )
       , m_ePinB2Co( (id+"-ePinB2Co"), 0 )
{
    m_graphical = true;
    m_area = QRectF( -64, -50, 114, 100 );
    m_color = QColor( 50, 50, 70 );
    m_bipolar = false;
    m_ang  = 0;
    m_Ppos = 4;
    m_steps = 32;
    m_stpang = 360*8/m_steps;

    m_pin.emplace_back( &m_pinA1 );
    m_pin.emplace_back( &m_pinA2 );
    m_pin.emplace_back( &m_pinCo );
    m_pin.emplace_back( &m_pinB1 );
    m_pin.emplace_back( &m_pinB2 );

    m_pinA1.setLabelText( " A+" );
    m_pinA2.setLabelText( " A-" );
    m_pinCo.setLabelText( " Co" );
    m_pinB1.setLabelText( " B+" );
    m_pinB2.setLabelText( " B-" );
    
    m_resA1.setEpin( 0, &m_pinA1 );
    m_resA1.setEpin( 1, &m_ePinA1Co );
    m_resA2.setEpin( 0, &m_pinA2 );
    m_resA2.setEpin( 1, &m_ePinA2Co );
    m_resB1.setEpin( 0, &m_pinB1 );
    m_resB1.setEpin( 1, &m_ePinB1Co );
    m_resB2.setEpin( 0, &m_pinB2 );
    m_resB2.setEpin( 1, &m_ePinB2Co );
    
    setResistance( 100 );
    setShowId( true );
    setLabelPos(-32,-62, 0);
    setValLabelPos(-14, 36, 0);

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

    addPropGroup( { tr("Main"), {
        new BoolProp<Stepper>( "Bipolar", tr("Bipolar"), ""
                             , this, &Stepper::bipolar, &Stepper::setBipolar ),

        new IntProp <Stepper>("Steps", tr("Steps per Rotation"), ""
                             , this, &Stepper::steps, &Stepper::setSteps ),

        new DoubProp<Stepper>( "Resistance", tr("Resistance"),"Ω"
                             , this, &Stepper::resistance, &Stepper::setResistance )
    },0} );
}
Stepper::~Stepper(){}

void Stepper::stamp()
{
    eNode* enode = m_pinCo.getEnode();// Register for changes callback
    if( enode )
    {
        m_ePinA1Co.setEnode( enode );
        m_ePinA2Co.setEnode( enode );
        m_ePinB1Co.setEnode( enode );
        m_ePinB2Co.setEnode( enode );
    }
    m_pinA1.changeCallBack( this );// Register for changes callback
    m_pinA2.changeCallBack( this );// Register for changes callback
    m_pinB1.changeCallBack( this );// Register for changes callback
    m_pinB2.changeCallBack( this );// Register for changes callback
    m_pinCo.changeCallBack( this );// Register for changes callback
}

void Stepper::updateStep()
{
    if(  m_linkedComp.size() )
    {
        double val = (16.0*360.0-m_ang)*1000/(16.0*360.0);
        for( Component* comp : m_linkedComp ) comp->setLinkedValue( val ); // 0-1000
    }
    if( m_changed )
    {
        m_changed = false;
        m_resA1.setResistance( m_resistance );
        m_resA2.setResistance( m_resistance );
        m_resB1.setResistance( m_resistance );
        m_resB2.setResistance( m_resistance );
    }
    update();
}

void Stepper::voltChanged()
{
    double voltCom = m_pinCo.getVoltage();
    double phaseA = ( m_pinA1.getVoltage()-voltCom )-( m_pinA2.getVoltage()-voltCom );
    double phaseB = ( m_pinB1.getVoltage()-voltCom )-( m_pinB2.getVoltage()-voltCom );
    
    if     ( phaseA > 1 ) phaseA = 1;
    else if( phaseA <-1 ) phaseA =-1;
    else                  phaseA = 0;
    
    if     ( phaseB > 1 ) phaseB = 1;
    else if( phaseB <-1 ) phaseB =-1;
    else                  phaseB = 0;

    int delta = 0;
    if( (qFabs(phaseA)+qFabs(phaseB)) > 0 ) // nosense algoritm.. just works
    {
        int ca = 4; 
        int cb =-1;
        if( phaseA ==-1 ) ca = 0;
        if( phaseA == 0 ) cb = 2;
        if( phaseA == 1 ) cb = 1;
        
        int newPos = ca+cb*phaseB;
        
        delta = newPos-m_Ppos;
        if( delta > 4 ) delta = delta-8;
        if( delta <-4 ) delta = delta+8;
        
        m_Ppos += delta;
    }
    else{                       // avoid keep in half step when no input
        delta = m_Ppos;
        m_Ppos = (m_Ppos/2)*2;
        delta = m_Ppos-delta;
    }
    m_ang  += delta*m_stpang;

    if (m_ang < 0)      m_ang += 360*16;
    if (m_ang > 360*16) m_ang -= 360*16;
    if (m_Ppos < 0)     m_Ppos += 8;
    if (m_Ppos > 7)     m_Ppos -= 8;
}

void Stepper::setSteps( int steps ) //" 4, 8,16,32"
{
    int remainder =  ( steps > m_steps && steps % 4 ) ? 4 : 0;
    m_steps = steps/4;
    m_steps *= 4;
    m_steps += remainder;
    if( m_steps < 4 ) m_steps = 4;
    m_stpang = 360*8/m_steps;
    m_ang  = 0;
    m_Ppos = 4;
    update();
}

void Stepper::setResistance( double res )
{
    if( res < 1e-12 ) res = 1e-12;
    if( m_resistance == res ) return;
    m_resistance = res;
    m_changed = true;
}

void Stepper::setBipolar( bool bi )
{
    m_bipolar = bi;
    m_pinCo.removeConnector();
    m_pinCo.setVisible( !bi && !m_hidden );
}

void Stepper::setHidden( bool hid, bool hidArea, bool hidLabel )
{
    Component::setHidden( hid, hidArea, hidLabel );
    m_pinCo.setVisible( !m_bipolar && !m_hidden );
}

void Stepper::paint( QPainter* p, const QStyleOptionGraphicsItem* o, QWidget* w )
{
    p->setRenderHint( QPainter::Antialiasing );
    Component::paint( p, o, w );

    //p->setBrush( QColor(250, 210, 230) );
    if( !m_hidden ) p->drawRoundedRect(-64,-40, 25, 80, 1, 1 );

    p->setBrush( QColor(50, 70, 100) ); 
    p->drawRoundedRect(-48,-48, 96, 96,1 ,1 );

    p->setPen( QColor(0, 0, 0) );
    p->setBrush( QColor(255, 255, 255) );
    p->drawEllipse(-37,-37, 74, 74 );

    p->setPen ( QColor(255, 255, 255) );
    QRectF rectangle( -42.0,-42.0, 84.0, 84.0 );
    for ( int i = 0; i< 360*16; i += m_stpang*2 )
        p->drawPie( rectangle, i+m_stpang*5/6, m_stpang/3 );

    p->setPen ( QColor(0, 0, 0) );
    p->setBrush( QColor(50, 70, 100) );
    p->drawPie(-33,-33, 66, 66, m_ang-m_stpang*3/4, m_stpang*3/2 );
    p->drawEllipse(-25,-25, 50, 50);

    Component::paintSelected( p );
}
