// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
// SPDX-License-Identifier: GPL-3.0-or-later

#include "App/Element/GraphicElements/Display14.h"

#include <QPainter>

#include "App/Element/ElementFactory.h"
#include "App/Element/ElementInfo.h"
#include "App/Element/GraphicElements/Display7.h"
#include "App/IO/Serialization.h"
#include "App/IO/SerializationContext.h"
#include "App/IO/VersionInfo.h"
#include "App/Nodes/QNEPort.h"

template<>
struct ElementInfo<Display14> {
    static constexpr ElementConstraints constraints{
        .type = ElementType::Display14,
        .group = ElementGroup::Output,
        .minInputSize = 15,
        .maxInputSize = 15,
        .canChangeAppearance = true,
        .hasColors = true,
        .hasLabel = true,
        .rotatesGraphic = false,
    };
    static_assert(validate(constraints));

    static ElementMetadata metadata()
    {
        auto meta = metadataFromConstraints(constraints);
        meta.pixmapPath = []{ return QStringLiteral(":/Components/Output/Counter/counter_14_on.svg"); };
        meta.titleText = QT_TRANSLATE_NOOP("Display14", "14-SEGMENT DISPLAY");
        meta.translatedName = QT_TRANSLATE_NOOP("Display14", "14-Segment Display");
        meta.trContext = "Display14";
        meta.defaultAppearances = QStringList({
            ":/Components/Output/Counter/counter_14_off.svg",
            ":/Components/Output/Counter/counter_a.svg",
            ":/Components/Output/Counter/counter_b.svg",
            ":/Components/Output/Counter/counter_c.svg",
            ":/Components/Output/Counter/counter_d.svg",
            ":/Components/Output/Counter/counter_e.svg",
            ":/Components/Output/Counter/counter_f.svg",
            ":/Components/Output/Counter/counter_g1.svg",
            ":/Components/Output/Counter/counter_g2.svg",
            ":/Components/Output/Counter/counter_h.svg",
            ":/Components/Output/Counter/counter_j.svg",
            ":/Components/Output/Counter/counter_k.svg",
            ":/Components/Output/Counter/counter_l.svg",
            ":/Components/Output/Counter/counter_m.svg",
            ":/Components/Output/Counter/counter_n.svg",
            ":/Components/Output/Counter/counter_dp.svg",
        });
        return meta;
    }

    static inline const bool registered = []() {
        ElementMetadataRegistry::registerMetadata(metadata());
        ElementFactory::registerCreator(constraints.type, [] { return new Display14(); });
        return true;
    }();
};

Display14::Display14(QGraphicsItem *parent)
    : GraphicElement(ElementType::Display14, parent)
{
    a  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(1));
    b  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(2));
    c  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(3));
    d  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(4));
    e  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(5));
    f  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(6));
    g1 = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(7));
    g2 = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(8));
    h  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(9));
    j  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(10));
    k  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(11));
    l  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(12));
    m  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(13));
    n  = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(14));
    dp = Display7::cachedSegmentColors(m_appearance.defaultAppearances().at(15));

    Display14::updatePortsProperties();
}

void Display14::refresh()
{
    update();
}

void Display14::updatePortsProperties()
{
    // Left-side ports (x=0): G1, F, E, D (outer horizontal/vertical) + G2, H, J (diagonals/center)
    // Right-side ports (x=64): A, B, DP, C + K, L, M, N
    // This two-column layout mirrors the physical dual-inline-package pin arrangement.
    inputPort( 0)->setPos( 0,  -8);    inputPort(0)->setName("G1 (" + tr("middle left horizontal")  + ")");
    inputPort( 1)->setPos( 0,   8);    inputPort(1)->setName("F (" +  tr("upper left vertical")     + ")");
    inputPort( 2)->setPos( 0,  24);    inputPort(2)->setName("E (" +  tr("lower left vertical")     + ")");
    inputPort( 3)->setPos( 0,  40);    inputPort(3)->setName("D (" +  tr("bottom")                  + ")");
    inputPort( 4)->setPos(64,  -8);    inputPort(4)->setName("A (" +  tr("top")                     + ")");
    inputPort( 5)->setPos(64,   8);    inputPort(5)->setName("B (" +  tr("upper right vertical")    + ")");
    inputPort( 6)->setPos(64,  24);    inputPort(6)->setName("DP (" + tr("dot")                     + ")");
    inputPort( 7)->setPos(64,  40);    inputPort(7)->setName("C (" +  tr("lower right vertical")    + ")");
    inputPort( 8)->setPos( 0,  56);    inputPort(8)->setName("G2 (" + tr("middle right horizontal") + ")");
    inputPort( 9)->setPos( 0,  72);    inputPort(9)->setName("H (" +  tr("upper left diagonal")     + ")");
    inputPort(10)->setPos( 0,  88);    inputPort(10)->setName("J (" + tr("upper center vertical")   + ")");
    inputPort(11)->setPos(64,  56);    inputPort(11)->setName("K (" + tr("upper right diagonal")    + ")");
    inputPort(12)->setPos(64,  72);    inputPort(12)->setName("L (" + tr("lower right diagonal")    + ")");
    inputPort(13)->setPos(64,  88);    inputPort(13)->setName("M (" + tr("lower center vertical")   + ")");
    inputPort(14)->setPos(64, 104);    inputPort(14)->setName("N (" + tr("lower left diagonal")    + ")");

    for (auto *port : std::as_const(m_inputPorts)) {
        port->setRequired(false);
        port->setDefaultStatus(Status::Inactive);
    }

    // This override doesn't chain to the base updatePortsProperties(), so re-apply the current
    // rotation/flip orientation to the freshly-positioned ports (keeps flip working on displays).
    rotatePorts();
}

void Display14::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // Base class paints the off-state background; active segments are overlaid additively
    GraphicElement::paint(painter, option, widget);

    if (inputPort(0)->status() == Status::Active)  { painter->drawPixmap(0, 0, g1.at(m_colorNumber)); }
    if (inputPort(1)->status() == Status::Active)  { painter->drawPixmap(0, 0, f.at(m_colorNumber));  }
    if (inputPort(2)->status() == Status::Active)  { painter->drawPixmap(0, 0, e.at(m_colorNumber));  }
    if (inputPort(3)->status() == Status::Active)  { painter->drawPixmap(0, 0, d.at(m_colorNumber));  }
    if (inputPort(4)->status() == Status::Active)  { painter->drawPixmap(0, 0, a.at(m_colorNumber));  }
    if (inputPort(5)->status() == Status::Active)  { painter->drawPixmap(0, 0, b.at(m_colorNumber));  }
    if (inputPort(6)->status() == Status::Active)  { painter->drawPixmap(0, 0, dp.at(m_colorNumber)); }
    if (inputPort(7)->status() == Status::Active)  { painter->drawPixmap(0, 0, c.at(m_colorNumber));  }
    if (inputPort(8)->status() == Status::Active)  { painter->drawPixmap(0, 0, g2.at(m_colorNumber)); }
    if (inputPort(9)->status() == Status::Active)  { painter->drawPixmap(0, 0, h.at(m_colorNumber));  }
    if (inputPort(10)->status() == Status::Active) { painter->drawPixmap(0, 0, j.at(m_colorNumber));  }
    if (inputPort(11)->status() == Status::Active) { painter->drawPixmap(0, 0, k.at(m_colorNumber));  }
    if (inputPort(12)->status() == Status::Active) { painter->drawPixmap(0, 0, l.at(m_colorNumber));  }
    if (inputPort(13)->status() == Status::Active) { painter->drawPixmap(0, 0, m.at(m_colorNumber));  }
    if (inputPort(14)->status() == Status::Active) { painter->drawPixmap(0, 0, n.at(m_colorNumber));  }
}

void Display14::setColor(const QString &color)
{
    m_color = color;
    m_colorNumber = colorNameToIndex(color);
}

QString Display14::color() const
{
    return m_color;
}

void Display14::save(QDataStream &stream) const
{
    GraphicElement::save(stream);

    QMap<QString, QVariant> map;
    map.insert("color", color());

    stream << map;
}

void Display14::load(QDataStream &stream, SerializationContext &context)
{
    GraphicElement::load(stream, context);

    if ((VersionInfo::hasLockState(context.version)) && (!VersionInfo::hasQMapFormat(context.version))) {
        // v3.1–4.0 stored color as a bare QString
        const QString color_ = Serialization::readBoundedString(stream);
        setColor(color_);
    }

    if (VersionInfo::hasQMapFormat(context.version)) {
        // v4.1+ uses a key-value map
        QMap<QString, QVariant> map = Serialization::readBoundedMetadata(stream);

        if (map.contains("color")) {
            setColor(map.value("color").toString());
        }
    }
}
