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

#include <QPainter>
#include <QtMath>

#include "lamp.h"
#include "itemlibrary.h"
#include "simulator.h"
#include "pin.h"

#include "doubleprop.h"

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

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

LibraryItem* Lamp::libraryItem()
{
    return new LibraryItem(
        tr("Incandescent lamp"),
        "Other Outputs",
        "lamp.png",
        "Lamp",
        Lamp::construct); 
}

Lamp::Lamp( QString type, QString id )
    : Comp2Pin( type, id )
    , eResistor( id )
{
    m_area = QRect(-10,-10, 20, 20 ); 
    m_graphical = true; 

    m_maxCurrent = 0.2; 
    setResistance( 5 ); 

    m_ePin[0] = m_pin[0]; 
    m_ePin[1] = m_pin[1]; 

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

    addPropGroup( { tr("Main"), {
        new DoubProp<Lamp>("Resistance", tr("Resistance"), "Ω"
                          , this, &Lamp::resistance, &Lamp::setResSafe ),

        new DoubProp<Lamp>("MaxCurrent", tr("Max Current"), "A"
                          , this, &Lamp::maxCurrent, &Lamp::setMaxCurrent ),
    },0 } ); 
}
Lamp::~Lamp(){}

void Lamp::initialize()
{
    m_crashed = false; 
    m_warning = false; 
    m_prevStep    = 0; 
    m_avgCurrent  = 0.; 
    m_intensity   = 25; 
    m_brightness  = 0; 
    m_totalCurrent = 0; 
    m_lastPeriod   = 0; 

    m_current = 0; 
    m_lastCurrent = 0; 
    update(); 
}

void Lamp::stamp()
{
    m_ePin[0]->changeCallBack( this ); 
    m_ePin[1]->changeCallBack( this ); 

    eResistor::stamp(); 
}

void Lamp::updateStep()
{
    uint32_t intensity = m_intensity; 
    updateBright(); 

    if( overCurrent() > 1.5 )
    {
        m_warning = true; 
        m_crashed = overCurrent() > 2; 
        update(); 
    }else{
        if( m_warning ) update(); 
        m_warning = false; 
        m_crashed = false; 
    }
    if( intensity != m_intensity ) update(); 
}

void Lamp::voltChanged()
{
    m_current = qFabs( m_ePin[0]->getVoltage()-m_ePin[1]->getVoltage() )*m_admit; 
    updateVI(); 
}

void Lamp::updateVI()
{
    const uint64_t step = Simulator::self()->circTime(); 
    uint64_t period = (step-m_prevStep); 
    m_prevStep = step; 
    m_lastPeriod += period; 

    if( m_lastCurrent > 0 ) m_totalCurrent += m_lastCurrent*period; 
    m_lastCurrent = m_current; 
}

void Lamp::updateBright()
{
    if( !Simulator::self()->isRunning() )
    {
        m_totalCurrent = 0; 
        m_lastPeriod = 0; 
        m_intensity = 25; 
        return; 
    }
    updateVI(); 

    uint64_t psPF = Simulator::self()->simPsPF(); ; 

    if( m_lastPeriod > psPF/2 ) // Update 2 times per frame
    {
        m_avgCurrent = m_totalCurrent/m_lastPeriod; 
        m_brightness = pow( m_avgCurrent/m_maxCurrent, 1.0/2.0 ); 

        m_totalCurrent  = 0; 
        m_lastPeriod = 0; 
        m_intensity  = uint32_t(m_brightness*255)+25; 
}   }

void Lamp::paint( QPainter* p, const QStyleOptionGraphicsItem* o, QWidget* w )
{
    Component::paint( p, o, w ); 

    QColor color; 

    if( m_warning/*m_current > m_maxCurrent*1.2*/ ) // Led overcurrent
    {
        p->setBrush( QColor( 255, 150, 0 ) ); 
        color = QColor( Qt::red ); 
    }
    if( m_crashed )  // Led extreme overcurrent
    {
        p->setBrush( Qt::white ); 
        color = QColor( Qt::white ); 
    }else{
        if( m_intensity > 25 )
        {
            m_intensity += 15;        // Set a Minimun Bright
            if( m_intensity > 255 ) m_intensity = 255; 
            color = QColor( m_intensity, m_intensity, m_intensity*0.85 ); 
        }
        else color = QColor( 25, 25, 100 ); 
    }
    p->setBrush( color ); 
    p->drawEllipse(-8,-8, 16, 16 ); 

    p->drawLine(-5,-5, 5, 5 ); 
    p->drawLine(-5, 5, 5,-5 ); 

    Component::paintSelected( p ); 
}
