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

#include "circuitwidget.h"
#include <QtMath>
#include <QPainter>
#include <QMenu>

#include "probe.h"
#include "connector.h"
#include "connectorline.h"
#include "circuit.h"
#include "simulator.h"
#include "itemlibrary.h"
#include "label.h"
#include "iopin.h"

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

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

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

LibraryItem* Probe::libraryItem()
{
    return new LibraryItem(
        tr("Probe"),
        "Meters",
        "probe.png",
        "Probe",
        Probe::construct );
}

Probe::Probe( QString type, QString id )
     : Component( type, id )
     , eElement( id )
{
    setZValue( 200 );
    m_area = QRect(-12,-8, 20, 16 );
    m_graphical = true;
    m_pauseState = false;

    m_voltTrig = 2.5;
    m_voltIn = 0;

    m_pin.resize(1); // Create Input Pin
    m_pin[0] = m_inputPin = new IoPin( 180, QPoint(-22,0), id+"-inpin", 0, this, undef_mode );
    m_inputPin->setBoundingRect( QRect(-1, -1, 2, 2) );
    m_inputPin->setImpedance( 1e9 );

    setValLabelPos( 16, 0, 45 ); // x, y, rot
    setShowVal( true );
    setLabelPos( 16,-16, 45 );
    setRotation( rotation() - 45 );
    m_voltIn = -1; // Force update
    setVolt( 0 );

    setSmall( false );

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

    addPropGroup( { tr("Main"), {
        new BoolProp<Probe>("ShowVolt" , tr("Show Voltage"), ""
                           , this, &Probe::showVal, &Probe::setShowVal, propNoCopy ),

        new DoubProp<Probe>("Threshold", tr("Threshold"), "V"
                           , this, &Probe::threshold, &Probe::setThreshold ),

        new BoolProp<Probe>("Small", tr("Small size"), ""
                           , this, &Probe::isSmall, &Probe::setSmall ),

        new BoolProp<Probe>("Pause", "", ""
                           , this, &Probe::pauseState, &Probe::setPauseState, propHidden )
    }, 0 } );
}
Probe::~Probe(){}

void Probe::stamp()
{
    m_state = false;
    m_inputPin->changeCallBack( this, m_pauseState );
}

void Probe::updateStep()
{
    if( !Simulator::self()->isRunning() ) { setVolt( 0.0 ); return; }

    if( m_inputPin->isConnected() )// Voltage from connected pin
    {
         setVolt( m_inputPin->getVoltage() );
         return;
    }
    QList<QGraphicsItem*> list = m_inputPin->collidingItems(); // Voltage from connector or Pin behind inputPin
    if( list.isEmpty() ) { setVolt( 0.0 ); return; }

    for( QGraphicsItem* it : list )
    {
        if( it->type() == UserType+3 )                    // Pin found
        {
            Pin* pin =  qgraphicsitem_cast<Pin*>( it );
            setVolt( pin->getVoltage() );
            break;
        }else if( it->type() == UserType+2 )        // ConnectorLine
        {
            ConnectorLine* line =  qgraphicsitem_cast<ConnectorLine*>( it );
            Connector* con = line->connector();
            setVolt( con->getVoltage() );
            break;
}   }   }

void Probe::voltChanged()
{
    if( !m_pauseState ) return;

    bool state = m_inputPin->getInpState();
    if( m_state == state ) return;
    m_state = state;
    CircuitWidget::self()->pauseCirc();
}

void Probe::setVolt( double volt )
{
    if( m_voltIn == volt ) return;
    m_voltIn = volt;
    update();       // Repaint

    if( !m_showVal ) return;
    if( qFabs(volt) < 0.01 ) volt = 0;
    
    float v = ( volt > 0 ) ? 0.5 : -0.5;
    v = float(int( v+volt*100 ))/100;
    setValLabelText( QString("%1 V").arg(v) );
}

void Probe::rotateCW()  { rotateAngle( 45 ); }
void Probe::rotateCCW() { rotateAngle(-45 );}

void Probe::rotateAngle( double a )
{
    Component::rotateAngle( a );
    m_idLabel->rotateAngle(-a );
    m_valLabel->rotateAngle(-a );
    updtPinSize();
}

void Probe::setAngle( double angle )
{
    Component::setAngle( angle );
    updtPinSize();
}

void Probe::setSmall( bool s )
{
    m_small = s;

    QFont font = m_idLabel->font();

    if( s ){
        m_area = QRect(-16, -4, 8, 8 );
        font.setPixelSize( 7 );
    }else{
        m_area = QRect(-12,-8, 20, 16 );
        font.setPixelSize( 8 );
    }
    m_idLabel->setFont( font );

    updtPinSize();
}

void Probe::updtPinSize()
{
    int length = 0;
    int an = (int)rotation()%90;

    if( m_small ) length = an ? 6 : 8;
    else          length = an ? 14 : 16;
    m_inputPin->setLength( length );

    int xPos = an? -22 : -24;
    m_inputPin->setPos( xPos, 0 );
    m_inputPin->isMoved();

    Circuit::self()->update();
}

void Probe::slotBreakpoint()
{
    m_pauseState = !m_pauseState;

    m_inputPin->changeCallBack( this, m_pauseState );

    update();
}

void Probe::contextMenu( QGraphicsSceneContextMenuEvent* event, QMenu* menu )
{
    if( m_inputPin->connector() )
    {
        QString iconStr = m_pauseState ? ":/nobreakpoint.png" : ":/breakpoint.png";
        QAction* breakAction = menu->addAction( QIcon( iconStr ),tr("Pause at state change") );
        QObject::connect( breakAction, &QAction::triggered, [=](){ slotBreakpoint(); } );
    }
    menu->addSeparator();
    Component::contextMenu( event, menu );
}

QPainterPath Probe::shape() const
{
    QPainterPath path;
    path.addEllipse( m_area );
    return path;
}
void Probe::paint( QPainter* p, const QStyleOptionGraphicsItem* o, QWidget* w )
{
    Component::paint( p, o, w );

    if      ( m_voltIn > m_voltTrig)  p->setBrush( QColor( 255, 166, 0 ) );
    else if ( m_voltIn < -m_voltTrig) p->setBrush( QColor( 0, 100, 255 ) );
    else                              p->setBrush( QColor( 230, 230, 255 ) );

    if( m_pauseState )
    {
        QPen pen = p->pen();
        pen.setWidthF( 2.5 );
        pen.setColor( QColor( 255, 0, 0 ));
        p->setPen(pen);
    }
    if( m_small ) p->drawEllipse( m_area );
    else          p->drawEllipse( QRect(-8,-8, 16, 16 ) );

    Component::paintSelected( p );
}
