/***************************************************************************
 *   Copyright (C) 2006 by William Hillerby - william.hillerby@ntlworld.com*
 *                                                                         *
 *   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 "ledbargraphdisplay.h"
#include "colorutils.h"
#include "led.h"
#include "libraryitem.h"
#include "simulator.h"

#include <KLocalizedString>

#include <QDebug>
#include <QPainter>
#include <QStyle>

LEDPart::LEDPart(Component *pParent, const QString &strPNode, const QString &strNNode)
{
    m_pParent = pParent;

    m_strPNode = strPNode;
    m_strNNode = strNNode;

    m_pDiode = pParent->createDiode(pParent->ecNodeWithID(strPNode), pParent->ecNodeWithID(strNNode));

    avg_brightness = 255;
    lastUpdatePeriod = 1.;
    last_brightness = 255;
    r = g = b = 0;
}

LEDPart::~LEDPart()
{
    m_pParent->removeNode(m_strPNode);
    m_pParent->removeNode(m_strNNode);
    m_pParent->removeElement(m_pDiode, false);
}

void LEDPart::setDiodeSettings(const DiodeSettings &ds)
{
    m_pDiode->setDiodeSettings(ds);
}

void LEDPart::setColor(const QColor &color)
{
    r = color.red() / (double)0x100;
    g = color.green() / (double)0x100;
    b = color.blue() / (double)0x100;
}

void LEDPart::step()
{
    avg_brightness += LED::brightness(m_pDiode->current()) * LINEAR_UPDATE_PERIOD;
    lastUpdatePeriod += LINEAR_UPDATE_PERIOD;
}

void LEDPart::draw(QPainter &p, int x, int y, int w, int h)
{
    uint _b;
    if (lastUpdatePeriod == 0.)
        _b = last_brightness;
    else {
        _b = (uint)(avg_brightness / lastUpdatePeriod);
        last_brightness = _b;
    }

    avg_brightness = 0.;
    lastUpdatePeriod = 0.;

    p.setBrush(QColor(uint(255 - (255 - _b) * (1 - r)), uint(255 - (255 - _b) * (1 - g)), uint(255 - (255 - _b) * (1 - b))));
    p.drawRect(x, y, w, h);
}

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

LibraryItem *LEDBarGraphDisplay::libraryItem()
{
    return new LibraryItem(QStringList(QString("ec/bar_graph_display")), i18n("Bar Graph Display"), i18n("Outputs"), "bar_graph_display.png", LibraryItem::lit_component, LEDBarGraphDisplay::construct);
}

LEDBarGraphDisplay::LEDBarGraphDisplay(ICNDocument *icnDocument, bool newItem, const QString &id)
    : Component(icnDocument, newItem, (!id.isEmpty()) ? id : "bar_graph_display")
{
    m_name = i18n("Bar Graph Display");
    m_bDynamicContent = true;

    m_numRows = 0;

    for (unsigned i = 0; i < max_LED_rows; i++)
        m_LEDParts[i] = nullptr;

    // Create a Row property.
    createProperty("rows", Variant::Type::Int);
    property("rows")->setCaption(i18n("Rows"));
    property("rows")->setMinValue(1);
    property("rows")->setMaxValue(max_LED_rows);
    property("rows")->setValue(7);

    createProperty("color", Variant::Type::Color);
    property("color")->setCaption(i18n("Color"));
    property("color")->setColorScheme(ColorUtils::LED);

    DiodeSettings ds;

    createProperty("I_S", Variant::Type::Double);
    property("I_S")->setCaption("Saturation Current");
    property("I_S")->setUnit("A");
    property("I_S")->setMinValue(1e-20);
    property("I_S")->setMaxValue(1e-0);
    property("I_S")->setValue(ds.I_S);
    property("I_S")->setAdvanced(true);

    createProperty("N", Variant::Type::Double);
    property("N")->setCaption(i18n("Emission Coefficient"));
    property("N")->setMinValue(1e0);
    property("N")->setMaxValue(1e1);
    property("N")->setValue(ds.N);
    property("N")->setAdvanced(true);

    createProperty("V_B", Variant::Type::Double);
    property("V_B")->setCaption(i18n("Breakdown Voltage"));
    property("V_B")->setUnit("V");
    property("V_B")->setMinAbsValue(1e-5);
    property("V_B")->setMaxValue(1e10);
    property("V_B")->setValue(ds.V_B);
    property("V_B")->setAdvanced(true);
}

LEDBarGraphDisplay::~LEDBarGraphDisplay()
{
}

void LEDBarGraphDisplay::dataChanged()
{
    DiodeSettings ds;
    QColor color = dataColor("color");

    ds.I_S = dataDouble("I_S");
    ds.V_B = dataDouble("V_B");
    ds.N = dataDouble("N");

    initPins();

    // Update each diode in array with new diode setting as they are acting individually.
    for (unsigned i = 0; i < m_numRows; i++) {
        m_LEDParts[i]->setDiodeSettings(ds);
        m_LEDParts[i]->setColor(color);
    }
}

void LEDBarGraphDisplay::initPins()
{
    unsigned int numRows = dataInt("rows");

    if (numRows == m_numRows)
        return;

    if (numRows > max_LED_rows)
        numRows = max_LED_rows;
    if (numRows < 1)
        numRows = 1;

    // Create a list of named pins.
    // A default setup looks like:
    //       -------
    // p_0--|1    14|--n_0
    // p_1--|2    13|--n_1
    // p_2--|3    12|--n_2
    // p_3--|4    11|--n_3
    // p_4--|5    10|--n_4
    // p_5--|6     9|--n_5
    // p_6--|7     8|--n_6
    //		 -------
    //
    // And this is the scheme used to create the nodes and diodes.
    //

    // Create the positive & negative pin names in an anticlockwise fashion
    // as shown in the pin schematic above.
    QStringList pins;
    for (unsigned i = 0; i < numRows; i++)
        pins += QString("p_" + QString::number(i));

    for (int i = numRows - 1; i >= 0; i--)
        pins += QString("n_" + QString::number(i));

    // Set the size of the component *BEFORE* initDIP() is called
    // as initDIP() uses this data to initialise the pin positions.
    setSize(-16, -(numRows + 1) * 8, 32, (numRows + 1) * 16, true);

    // Create the nodes.
    initDIP(pins);

    // Create or remove LED parts
    if (numRows > m_numRows) {
        // Create the extra LED parts required.
        for (unsigned i = m_numRows; i < numRows; i++)
            m_LEDParts[i] = new LEDPart((Component *)this, QString("p_" + QString::number(i)), QString("n_" + QString::number(i)));
    } else {
        // Remove excess LED parts.
        for (unsigned i = numRows; i < m_numRows; i++) {
            delete m_LEDParts[i];
            m_LEDParts[i] = nullptr;
        }
    }

    m_numRows = numRows;
}

void LEDBarGraphDisplay::stepNonLogic()
{
    // Update LED brightnesses.
    for (unsigned i = 0; i < m_numRows; i++)
        m_LEDParts[i]->step();
}

void LEDBarGraphDisplay::drawShape(QPainter &p)
{
    Component::drawShape(p);
    initPainter(p);

    // Init _x and _y to top left hand corner of component.
    int _x = (int)(x() + offsetX());
    int _y = (int)(y() + offsetY());

    // Draw LED elements, passing in a position for each.
    for (unsigned i = 0; i < m_numRows; i++)
        m_LEDParts[i]->draw(p, _x + 4, _y + (i * 16) + 10, 24, 12);

    deinitPainter(p);
}
