/***************************************************************************
 *   Copyright (C) 2005 by David Saxton                                    *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "probe.h" //HACK: This has to be included before the oscilloscope headers
#include "ecnode.h"
#include "libraryitem.h"
#include "logic.h"
#include "oscilloscope.h"
#include "oscilloscopedata.h"
#include "pin.h"
#include "simulator.h"
#include "voltagesource.h"

#include <KLocalizedString>
#include <QPainter>
#include <QPainterPath>

// BEGIN class Probe
Probe::Probe(ICNDocument *icnDocument, bool newItem, const char *id)
    : Component(icnDocument, newItem, id)
{
    p_probeData = nullptr;
    setSize(-16, -8, 32, 16);

    createProperty("color", Variant::Type::Color);
    property("color")->setCaption(i18n("Color"));
    property("color")->setValue(QColor(Qt::black));
}

Probe::~Probe()
{
    delete p_probeData;
}

void Probe::dataChanged()
{
    m_color = dataColor("color");
    if (p_probeData)
        p_probeData->setColor(m_color);
    setChanged();
}
// END class Probe

// BEGIN class FloatingProbe
FloatingProbe::FloatingProbe(ICNDocument *icnDocument, bool newItem, const char *id)
    : Probe(icnDocument, newItem, id)
{
    p_probeData = m_pFloatingProbeData = static_cast<FloatingProbeData *>(registerProbe(this));
    property("color")->setValue(p_probeData->color());

    createProperty("scaling", Variant::Type::Select);
    property("scaling")->setCaption(i18n("Scaling"));
    QStringMap allowed;
    allowed["Linear"] = i18n("Linear");
    allowed["Logarithmic"] = i18n("Logarithmic");
    property("scaling")->setAllowed(allowed);
    property("scaling")->setValue("Linear");
    property("scaling")->setAdvanced(true);

    createProperty("upper_abs_value", Variant::Type::Double);
    property("upper_abs_value")->setCaption(i18n("Upper Absolute Value"));
    property("upper_abs_value")->setValue(10.0);
    property("upper_abs_value")->setMinValue(0.0);
    property("upper_abs_value")->setUnit("V");
    property("upper_abs_value")->setAdvanced(true);

    createProperty("lower_abs_value", Variant::Type::Double);
    property("lower_abs_value")->setCaption(i18n("Lower Absolute Value"));
    property("lower_abs_value")->setValue(0.1);
    property("lower_abs_value")->setMinValue(0.0);
    property("lower_abs_value")->setUnit("V");
    property("lower_abs_value")->setAdvanced(true);
}

FloatingProbe::~FloatingProbe()
{
}

void FloatingProbe::dataChanged()
{
    Probe::dataChanged();

    if (dataString("scaling") == "Linear")
        m_pFloatingProbeData->setScaling(FloatingProbeData::Linear);
    else
        m_pFloatingProbeData->setScaling(FloatingProbeData::Logarithmic);

    m_pFloatingProbeData->setUpperAbsValue(dataDouble("upper_abs_value"));
    m_pFloatingProbeData->setLowerAbsValue(dataDouble("lower_abs_value"));
}

void FloatingProbe::drawShape(QPainter &p)
{
    initPainter(p);

    int _x = int(x()) - 16;
    int _y = int(y()) - 8;

    p.drawRect(_x, _y, 32, 16);

    QPolygon bezier(4);

    bezier[0] = QPoint(_x + 4, _y + 10);
    bezier[1] = QPoint(_x + 12, _y - 6);
    bezier[2] = QPoint(_x + 20, _y + 24);
    bezier[3] = QPoint(_x + 28, _y + 4);

    p.setPen(QPen(m_color, 2));
    // p.drawCubicBezier(bezier); // 2018.12.07
    QPainterPath path;
    path.moveTo(bezier.at(0));
    ;
    path.cubicTo(bezier.at(1), bezier.at(2), bezier.at(3));
    p.strokePath(path, p.pen());

    deinitPainter(p);
}
// END class FloatingProbe

// BEGIN class VoltageProbe
Item *VoltageProbe::construct(ItemDocument *itemDocument, bool newItem, const char *id)
{
    return new VoltageProbe((ICNDocument *)itemDocument, newItem, id);
}

LibraryItem *VoltageProbe::libraryItem()
{
    return new LibraryItem(QStringList(QString("ec/voltageprobe")), i18n("Voltage Probe"), i18n("Outputs"), "floatingprobe.png", LibraryItem::lit_component, VoltageProbe::construct);
}

VoltageProbe::VoltageProbe(ICNDocument *icnDocument, bool newItem, const char *id)
    : FloatingProbe(icnDocument, newItem, id ? id : "voltageprobe")
{
    m_name = i18n("Voltage Probe");

    init1PinLeft();
    init1PinRight();
    m_pPin1 = m_pNNode[0]->pin();
    m_pPin2 = m_pPNode[0]->pin();
}

VoltageProbe::~VoltageProbe()
{
}

void VoltageProbe::stepNonLogic()
{
    m_pFloatingProbeData->addDataPoint(m_pPin1->voltage() - m_pPin2->voltage());
}
// END class VoltageProbe

// BEGIN class CurrentProbe
Item *CurrentProbe::construct(ItemDocument *itemDocument, bool newItem, const char *id)
{
    return new CurrentProbe((ICNDocument *)itemDocument, newItem, id);
}

LibraryItem *CurrentProbe::libraryItem()
{
    return new LibraryItem(QStringList(QString("ec/currentprobe")), i18n("Current Probe"), i18n("Outputs"), "floatingprobe.png", LibraryItem::lit_component, CurrentProbe::construct);
}

CurrentProbe::CurrentProbe(ICNDocument *icnDocument, bool newItem, const char *id)
    : FloatingProbe(icnDocument, newItem, id ? id : "currentprobe")
{
    m_name = i18n("Current Probe");

    init1PinLeft(0);
    init1PinRight(0);

    m_voltageSource = createVoltageSource(m_pNNode[0], m_pPNode[0], 0.);
}

CurrentProbe::~CurrentProbe()
{
}

void CurrentProbe::stepNonLogic()
{
    m_pFloatingProbeData->addDataPoint(-m_voltageSource->cbranchCurrent(0));
}
// END class CurrentProbe

// BEGIN class LogicProbe
void LogicProbe_logicCallback(void *objV, bool state) {
    LogicProbe *objT = static_cast<LogicProbe*>(objV);
    objT->logicCallback(state);
}

Item *LogicProbe::construct(ItemDocument *itemDocument, bool newItem, const char *id)
{
    return new LogicProbe((ICNDocument *)itemDocument, newItem, id);
}

LibraryItem *LogicProbe::libraryItem()
{
    QStringList ids;
    ids << "ec/probe"
        << "ec/logicprobe";
    return new LibraryItem(ids, i18n("Logic Probe"), i18n("Outputs"), "logicprobe.png", LibraryItem::lit_component, LogicProbe::construct);
}

LogicProbe::LogicProbe(ICNDocument *icnDocument, bool newItem, const char *id)
    : Probe(icnDocument, newItem, id ? id : "probe")
{
    m_name = i18n("Logic Probe");

    init1PinRight();
    m_pIn = createLogicIn(m_pPNode[0]);

    p_probeData = p_logicProbeData = static_cast<LogicProbeData *>(registerProbe(this));
    property("color")->setValue(p_probeData->color());

    m_pSimulator = Simulator::self();
    //m_pIn->setCallback(this, (CallbackPtr)(&LogicProbe::logicCallback));
    m_pIn->setCallback2(LogicProbe_logicCallback, this);
    logicCallback(false);
}

LogicProbe::~LogicProbe()
{
}

void LogicProbe::logicCallback(bool value)
{
    p_logicProbeData->addDataPoint(LogicDataPoint(value, m_pSimulator->time()));
}

void LogicProbe::drawShape(QPainter &p)
{
    initPainter(p);

    int _x = int(x()) - 16;
    int _y = int(y()) - 8;

    p.drawRect(_x, _y, 32, 16);

    p.setPen(QPen(m_color, 2));

    p.drawLine(_x + 4, _y + 11, _x + 6, _y + 11);
    p.drawLine(_x + 6, _y + 11, _x + 6, _y + 4);
    p.drawLine(_x + 6, _y + 4, _x + 10, _y + 4);
    p.drawLine(_x + 10, _y + 4, _x + 10, _y + 11);
    p.drawLine(_x + 10, _y + 11, _x + 16, _y + 11);
    p.drawLine(_x + 16, _y + 11, _x + 16, _y + 4);
    p.drawLine(_x + 16, _y + 4, _x + 23, _y + 4);
    p.drawLine(_x + 23, _y + 4, _x + 23, _y + 11);
    p.drawLine(_x + 23, _y + 11, _x + 28, _y + 11);
    // 	p.drawLine( _x+23, _y+11, _x+26, _y+11 );
    // 	p.drawLine( _x+26, _y+11, _x+26, _y+4 );
    // 	p.drawLine( _x+26, _y+4, _x+28, _y+4 );

    deinitPainter(p);
}
// END class LogicProbe
