/**
 * @file StatsDialog.cpp
 */

#include "StatsDialog.h"
#include "Hub.h"
#include "Theme.h"

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTableWidget>
#include <QHeaderView>
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include <QPainter>
#include <cstdio>

namespace shq {

using StreamHubClient::SourceStats;

/* ── HistogramWidget ─────────────────────────────────────────────────────── */

HistogramWidget::HistogramWidget(QWidget* parent) : QWidget(parent) {
    setMinimumHeight(120);
}

void HistogramWidget::setData(const SourceStats& st) {
    min_ = st.cycleHistMin;
    max_ = st.cycleHistMax;
    valid_ = (max_ > min_);
    for (int i = 0; i < 20; i++) { bins_[i] = st.cycleHist[i]; }
    update();
}

void HistogramWidget::paintEvent(QPaintEvent*) {
    QPainter p(this);
    p.fillRect(rect(), col::mantle());
    if (!valid_) { return; }

    double maxCount = 0.0;
    for (int i = 0; i < 20; i++) { if (bins_[i] > maxCount) { maxCount = bins_[i]; } }
    if (maxCount <= 0.0) { return; }

    const int L = 4, R = 4, T = 4, B = 16;
    QRectF area(L, T, width() - L - R, height() - T - B);
    double bw = area.width() / 20.0;

    p.setPen(Qt::NoPen);
    p.setBrush(col::blue());
    for (int i = 0; i < 20; i++) {
        double h = (bins_[i] / maxCount) * area.height();
        QRectF bar(area.left() + i * bw + 1, area.bottom() - h,
                   bw - 2, h);
        p.drawRect(bar);
    }

    p.setPen(col::subtext());
    QFont f = p.font(); f.setPointSize(7); p.setFont(f);
    p.drawText(QRectF(L, height() - B, area.width() / 2, B),
               Qt::AlignLeft | Qt::AlignVCenter,
               QString::number(min_, 'g', 3) + " ms");
    p.drawText(QRectF(L + area.width() / 2, height() - B, area.width() / 2, B),
               Qt::AlignRight | Qt::AlignVCenter,
               QString::number(max_, 'g', 3) + " ms");
}

/* ── StatsDialog ─────────────────────────────────────────────────────────── */

static const char* kCols[] = {
    "Source", "State", "Cycles Rx", "Lost", "Rate Hz",
    "Frags/cyc", "Bytes/cyc", "Cycle ms (avg±std)", "Cycle ms (min/max)"
};
static const int kNumCols = 9;

StatsDialog::StatsDialog(Hub* hub, QWidget* parent)
    : QDialog(parent), hub_(hub) {
    setWindowTitle("Statistics");
    resize(760, 460);

    auto* lay = new QVBoxLayout(this);

    table_ = new QTableWidget(this);
    table_->setColumnCount(kNumCols);
    QStringList headers;
    for (int i = 0; i < kNumCols; i++) { headers << kCols[i]; }
    table_->setHorizontalHeaderLabels(headers);
    table_->verticalHeader()->setVisible(false);
    table_->setEditTriggers(QAbstractItemView::NoEditTriggers);
    table_->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    lay->addWidget(table_);

    auto* scroll = new QScrollArea(this);
    scroll->setWidgetResizable(true);
    auto* histHost = new QWidget(scroll);
    histLay_ = new QVBoxLayout(histHost);
    scroll->setWidget(histHost);
    lay->addWidget(scroll, 1);

    auto* btnRow = new QHBoxLayout();
    btnRow->addStretch(1);
    auto* refreshBtn = new QPushButton("Refresh", this);
    connect(refreshBtn, &QPushButton::clicked, this, [this]() {
        hub_->sendGetStats();
        refresh();
    });
    btnRow->addWidget(refreshBtn);
    lay->addLayout(btnRow);

    refresh();
}

void StatsDialog::refresh() {
    const auto& sources = hub_->sources();
    table_->setRowCount(static_cast<int>(sources.size()));

    char buf[64];
    for (int r = 0; r < static_cast<int>(sources.size()); r++) {
        const Source& src = sources[r];
        const SourceStats& st = src.stats;
        auto set = [&](int c, const QString& s, const QColor* fg = nullptr) {
            auto* it = new QTableWidgetItem(s);
            if (fg) { it->setForeground(*fg); }
            table_->setItem(r, c, it);
        };
        set(0, QString::fromStdString(src.id));
        QColor stc = (st.state == "connected") ? col::green() : col::red();
        set(1, QString::fromStdString(st.state), &stc);
        set(2, QString::number(static_cast<qulonglong>(st.totalReceived)));
        set(3, QString::number(static_cast<qulonglong>(st.totalLost)));
        snprintf(buf, sizeof(buf), "%.2f \u00b1 %.2f", st.rateHz, st.rateStdHz);
        set(4, buf);
        snprintf(buf, sizeof(buf), "%.1f", st.fragsPerCycle); set(5, buf);
        snprintf(buf, sizeof(buf), "%.0f", st.bytesPerCycle); set(6, buf);
        snprintf(buf, sizeof(buf), "%.3f \u00b1 %.3f", st.cycleAvgMs, st.cycleStdMs);
        set(7, buf);
        snprintf(buf, sizeof(buf), "%.3f / %.3f", st.cycleMinMs, st.cycleMaxMs);
        set(8, buf);
    }

    /* Rebuild histogram blocks. */
    for (auto* w : histBlocks_) { w->deleteLater(); }
    histBlocks_.clear();
    hists_.clear();
    for (const auto& src : sources) {
        if (src.stats.cycleHistMax <= src.stats.cycleHistMin) { continue; }
        auto* block = new QWidget();
        auto* bl = new QVBoxLayout(block);
        bl->setContentsMargins(0, 0, 0, 0);
        bl->addWidget(new QLabel("Cycle time — " +
                                 QString::fromStdString(src.id), block));
        auto* h = new HistogramWidget(block);
        h->setData(src.stats);
        bl->addWidget(h);
        histLay_->addWidget(block);
        histBlocks_.push_back(block);
        hists_.push_back(h);
    }
}

} /* namespace shq */
