/**
 * @file MainWindow.cpp
 */

#include "MainWindow.h"
#include "PlotGrid.h"
#include "SourceSidebar.h"
#include "TriggerBar.h"
#include "HistoryBar.h"
#include "StatsDialog.h"
#include "Theme.h"

#include <QToolBar>
#include <QToolButton>
#include <QComboBox>
#include <QLineEdit>
#include <QLabel>
#include <QTimer>
#include <QDockWidget>
#include <QMenu>
#include <QAction>
#include <QDialog>
#include <QFormLayout>
#include <QDialogButtonBox>
#include <QIntValidator>
#include <cmath>

namespace shq {

MainWindow::MainWindow(const QString& host, uint16_t port, QWidget* parent)
    : QMainWindow(parent) {
    setWindowTitle("StreamHub Qt Client");
    resize(1280, 800);

    /* ── Central plot grid ── */
    grid_ = new PlotGrid(&hub_, &gv_, this);
    setCentralWidget(grid_);

    /* ── Source sidebar dock ── */
    sidebar_ = new SourceSidebar(&hub_, this);
    sideDock_ = new QDockWidget("Sources", this);
    sideDock_->setWidget(sidebar_);
    sideDock_->setFeatures(QDockWidget::DockWidgetMovable |
                           QDockWidget::DockWidgetClosable);
    addDockWidget(Qt::LeftDockWidgetArea, sideDock_);
    connect(sidebar_, &SourceSidebar::addSourceRequested,
            this, &MainWindow::openAddSourceDialog);

    /* ── Trigger bar (hidden until toggled) ── */
    trigBar_ = new TriggerBar(&hub_, this);
    auto* trigTb = new QToolBar("Trigger", this);
    trigTb->setMovable(false);
    trigTb->addWidget(trigBar_);
    addToolBarBreak();
    addToolBar(Qt::TopToolBarArea, trigTb);
    trigTb->setVisible(false);

    /* ── History bar (hidden until toggled) ── */
    histBar_ = new HistoryBar(&hub_, grid_, this);
    auto* histTb = new QToolBar("History", this);
    histTb->setMovable(false);
    histTb->addWidget(histBar_);
    addToolBarBreak();
    addToolBar(Qt::TopToolBarArea, histTb);
    histTb->setVisible(false);
    connect(histBar_, &HistoryBar::liveRequested, this, [this, histTb]() {
        histTb->setVisible(false);
        histBtn_->setChecked(false);
    });

    /* Keep the QToolBar pointers reachable from slots. */
    trigBtn_ = nullptr;  /* set in buildToolbar */
    buildToolbar();
    /* Wire trigger/history toggle buttons to their bars. */
    connect(trigBtn_, &QToolButton::toggled, trigTb, &QToolBar::setVisible);
    connect(histBtn_, &QToolButton::toggled, this, [this, histTb](bool on) {
        if (on) {
            const auto& hi = hub_.historyInfo();
            if (hi.enabled && !hi.signals.empty()) {
                histTb->setVisible(true);
                histBar_->showAll();
            } else {
                histBtn_->setChecked(false);
            }
        } else {
            histTb->setVisible(false);
            grid_->goLive();
        }
    });

    /* ── Hub signal wiring ── */
    connect(&hub_, &Hub::connectedChanged,     this, &MainWindow::onConnectedChanged);
    connect(&hub_, &Hub::sourcesChanged,       this, &MainWindow::onSourcesChanged);
    connect(&hub_, &Hub::configChanged,        this, &MainWindow::onConfigChanged);
    connect(&hub_, &Hub::statsChanged,         this, &MainWindow::onStatsChanged);
    connect(&hub_, &Hub::triggerStateChanged,  this, &MainWindow::onTriggerStateChanged);
    connect(&hub_, &Hub::historyInfoChanged,   this, &MainWindow::onHistoryInfoChanged);

    /* ── 60 Hz repaint / refresh ── */
    timer_ = new QTimer(this);
    timer_->setInterval(16);
    connect(timer_, &QTimer::timeout, this, &MainWindow::onTick);
    timer_->start();

    hostEdit_->setText(host);
    portEdit_->setText(QString::number(port));
    hub_.start(host, port);
    onConnectedChanged(false);
}

/* ── Toolbar construction ────────────────────────────────────────────────── */

void MainWindow::buildToolbar() {
    auto* tb = new QToolBar("Main", this);
    tb->setMovable(false);
    addToolBar(Qt::TopToolBarArea, tb);

    /* Sidebar toggle. */
    auto* sideBtn = new QToolButton(this);
    sideBtn->setText("\u2630");   /* ≡ */
    sideBtn->setCheckable(true);
    sideBtn->setChecked(true);
    sideBtn->setToolTip("Toggle sidebar");
    connect(sideBtn, &QToolButton::toggled, sideDock_, &QDockWidget::setVisible);
    connect(sideDock_, &QDockWidget::visibilityChanged, sideBtn, &QToolButton::setChecked);
    tb->addWidget(sideBtn);

    /* Layout picker. */
    auto* layoutBtn = new QToolButton(this);
    layoutBtn->setText("Layout");
    layoutBtn->setPopupMode(QToolButton::InstantPopup);
    auto* layMenu = new QMenu(layoutBtn);
    const char* const* names = layoutNames();
    for (int i = 0; i < static_cast<int>(PlotLayout::kCount); i++) {
        QAction* a = layMenu->addAction(QString::fromUtf8(names[i]));
        PlotLayout l = static_cast<PlotLayout>(i);
        connect(a, &QAction::triggered, this, [this, l]() {
            grid_->setLayout(l);
        });
    }
    layoutBtn->setMenu(layMenu);
    tb->addWidget(layoutBtn);

    /* Pause. */
    pauseBtn_ = new QToolButton(this);
    pauseBtn_->setText("Pause");
    pauseBtn_->setCheckable(true);
    connect(pauseBtn_, &QToolButton::clicked, this, &MainWindow::togglePause);
    tb->addWidget(pauseBtn_);

    /* Cursors. */
    cursorBtn_ = new QToolButton(this);
    cursorBtn_->setText("Cursors");
    cursorBtn_->setCheckable(true);
    cursorBtn_->setToolTip("Cursors A/B");
    connect(cursorBtn_, &QToolButton::toggled, this, [this](bool on) {
        gv_.cursorsOn = on;
    });
    tb->addWidget(cursorBtn_);

    /* Trigger toggle. */
    trigBtn_ = new QToolButton(this);
    trigBtn_->setText("Trigger");
    trigBtn_->setCheckable(true);
    connect(trigBtn_, &QToolButton::toggled, this, [this](bool on) {
        gv_.trigView = on;
    });
    tb->addWidget(trigBtn_);

    /* History toggle. */
    histBtn_ = new QToolButton(this);
    histBtn_->setText("History");
    histBtn_->setCheckable(true);
    histBtn_->setEnabled(false);
    tb->addWidget(histBtn_);

    /* Window presets. */
    tb->addWidget(new QLabel(" Win ", this));
    winCombo_ = new QComboBox(this);
    winCombo_->setEditable(true);
    static const double kWin[]  = {1, 5, 10, 30, 60};
    static const char*  kWinL[] = {"1 s", "5 s", "10 s", "30 s", "60 s"};
    for (int i = 0; i < 5; i++) { winCombo_->addItem(kWinL[i], kWin[i]); }
    winCombo_->setCurrentIndex(2);   /* 10 s */
    connect(winCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
            this, [this](int i) {
        if (i >= 0) { gv_.windowSec = winCombo_->itemData(i).toDouble(); }
    });
    connect(winCombo_->lineEdit(), &QLineEdit::editingFinished, this, [this]() {
        double v = winCombo_->currentText().split(' ').first().toDouble();
        if (v >= 1e-4 && v <= 3600.0) { gv_.windowSec = v; }
    });
    tb->addWidget(winCombo_);

    /* Spacer pushes the rest to the right. */
    auto* spacer = new QWidget(this);
    spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
    tb->addWidget(spacer);

    /* Stats. */
    auto* statsBtn = new QToolButton(this);
    statsBtn->setText("Stats");
    connect(statsBtn, &QToolButton::clicked, this, [this]() {
        if (!stats_) { stats_ = new StatsDialog(&hub_, this); }
        hub_.sendGetStats();
        stats_->refresh();
        stats_->show();
        stats_->raise();
    });
    tb->addWidget(statsBtn);

    /* Connection. */
    tb->addWidget(new QLabel(" Host ", this));
    hostEdit_ = new QLineEdit(this);
    hostEdit_->setMaximumWidth(120);
    tb->addWidget(hostEdit_);
    portEdit_ = new QLineEdit(this);
    portEdit_->setMaximumWidth(60);
    portEdit_->setValidator(new QIntValidator(1, 65535, this));
    tb->addWidget(portEdit_);
    auto* connBtn = new QToolButton(this);
    connBtn->setText("Connect");
    connect(connBtn, &QToolButton::clicked, this, &MainWindow::doConnect);
    tb->addWidget(connBtn);

    ledLbl_ = new QLabel(" \u25cf Disconnected ", this);
    tb->addWidget(ledLbl_);
}

/* ── Slots ───────────────────────────────────────────────────────────────── */

void MainWindow::togglePause() {
    gv_.paused = pauseBtn_->isChecked();
    pauseBtn_->setText(gv_.paused ? "Resume" : "Pause");
    grid_->onPauseChanged();
}

void MainWindow::toggleHistory() { /* handled inline via histBtn_ toggle */ }

void MainWindow::doConnect() {
    QString host = hostEdit_->text().trimmed();
    uint16_t port = static_cast<uint16_t>(portEdit_->text().toUInt());
    if (host.isEmpty() || port == 0) { return; }
    hub_.reconnect(host, port);
}

void MainWindow::onConnectedChanged(bool connected) {
    ledLbl_->setText(connected ? " \u25cf Connected " : " \u25cf Disconnected ");
    ledLbl_->setStyleSheet(connected
        ? "color:#a6e3a1; font-weight:bold;"
        : "color:#f38ba8; font-weight:bold;");
}

void MainWindow::onSourcesChanged() {
    sidebar_->refresh();
    trigBar_->refreshSignals();
    grid_->onModelChanged();
}

void MainWindow::onConfigChanged(const QString&) {
    sidebar_->refresh();
    trigBar_->refreshSignals();
    grid_->onModelChanged();
}

void MainWindow::onStatsChanged() {
    if (stats_ && stats_->isVisible()) { stats_->refresh(); }
}

void MainWindow::onTriggerStateChanged() {
    trigBar_->onTriggerStateChanged();
}

void MainWindow::onHistoryInfoChanged() {
    const auto& hi = hub_.historyInfo();
    histBtn_->setEnabled(hi.enabled && !hi.signals.empty());
}

void MainWindow::onTick() {
    grid_->tick();
    if (histBtn_->isChecked()) { histBar_->updateReadout(); }
}

/* ── Add-source dialog ───────────────────────────────────────────────────── */

void MainWindow::openAddSourceDialog() {
    QDialog dlg(this);
    dlg.setWindowTitle("Add Source");
    auto* form = new QFormLayout(&dlg);

    auto* label = new QLineEdit(&dlg);
    auto* host  = new QLineEdit("127.0.0.1", &dlg);
    auto* port  = new QLineEdit("44500", &dlg);
    port->setValidator(new QIntValidator(1, 65535, &dlg));
    auto* mcast = new QLineEdit(&dlg);
    auto* dport = new QLineEdit("0", &dlg);
    dport->setValidator(new QIntValidator(0, 65535, &dlg));

    form->addRow("Label", label);
    form->addRow("Host", host);
    form->addRow("Port", port);
    form->addRow("Multicast", mcast);
    form->addRow("Data Port", dport);

    auto* box = new QDialogButtonBox(
        QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dlg);
    form->addRow(box);
    connect(box, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
    connect(box, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);

    if (dlg.exec() != QDialog::Accepted) { return; }
    QString h = host->text().trimmed();
    int p = port->text().toInt();
    if (h.isEmpty() || p <= 0 || p >= 65536) { return; }
    std::string addr = h.toStdString() + ":" + std::to_string(p);
    int dp = dport->text().toInt();
    hub_.sendAddSource(label->text().toStdString(), addr,
                       mcast->text().toStdString(),
                       static_cast<uint16_t>((dp > 0 && dp < 65536) ? dp : 0));
}

} /* namespace shq */
