#include "sidebar.h"

#include <QApplication>
#include <QByteArray>
#include <QDrag>
#include <QMimeData>
#include <QMouseEvent>
#include <QPushButton>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include <QWidget>
#include <cstring>

// ── MIME type for signal drag ─────────────────────────────────────────
static const char* kMimeType = "application/x-signal";

// ── Helper: pack (src_idx, sig_idx) into bytes ────────────────────────
static QByteArray packSignalDrag(int srcIdx, int sigIdx)
{
    QByteArray ba(8, 0);
    std::memcpy(ba.data(),     &srcIdx, 4);
    std::memcpy(ba.data() + 4, &sigIdx, 4);
    return ba;
}

// ── Sidebar ───────────────────────────────────────────────────────────

Sidebar::Sidebar(QWidget* parent)
    : QDockWidget("Signals", parent)
{
    setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
    setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
    setMinimumWidth(180);

    auto* container = new QWidget(this);
    auto* lay = new QVBoxLayout(container);
    lay->setContentsMargins(0, 0, 0, 0);
    lay->setSpacing(0);

    tree_ = new QTreeWidget(container);
    tree_->setHeaderHidden(true);
    // Disable Qt's built-in drag so our custom event-filter drag has sole control.
    // Qt's startDrag() would produce generic MIME data; we need kMimeType instead.
    tree_->setDragEnabled(false);
    tree_->setDragDropMode(QAbstractItemView::NoDragDrop);
    tree_->setSelectionMode(QAbstractItemView::SingleSelection);
    lay->addWidget(tree_);

    auto* addBtn = new QPushButton("+ Add Source", container);
    addBtn->setObjectName("addSourceBtn");
    addBtn->setFlat(true);
    lay->addWidget(addBtn);
    connect(addBtn, &QPushButton::clicked, this, &Sidebar::addSourceRequested);

    setWidget(container);

    // Custom drag: build MIME data ourselves
    connect(tree_, &QTreeWidget::itemPressed,
            [this](QTreeWidgetItem* item, int /*col*/) {
                if (!item || item->parent() == nullptr) return; // top-level = source
                // Walk up to find entry
                for (auto& e : entries_) {
                    int childCount = e.item->childCount();
                    for (int i = 0; i < childCount; ++i) {
                        if (e.item->child(i) == item) {
                            // Start drag on mouse move — intercept via eventFilter or
                            // override mouseMoveEvent.  Here we use a simpler approach:
                            // store pending drag info and start in mouseMoveEvent.
                            // We rely on Qt's built-in drag mechanism triggered by
                            // QTreeWidget::startDrag below.
                            Q_UNUSED(e);
                            return;
                        }
                    }
                }
            });

    // Override startDrag by subclassing is cleaner, but connecting to the
    // model's mimeData is sufficient for QTreeWidget.
    // We override via an event filter that intercepts mouse move after press.
    tree_->viewport()->installEventFilter(this);
}

bool Sidebar::eventFilter(QObject* obj, QEvent* ev)
{
    // We intercept MouseMove while button pressed to start our drag
    static QPoint pressPos;
    static QTreeWidgetItem* pressedItem = nullptr;

    if (obj == tree_->viewport()) {
        if (ev->type() == QEvent::MouseButtonPress) {
            auto* me = static_cast<QMouseEvent*>(ev);
            pressPos = me->pos();
            pressedItem = tree_->itemAt(me->pos());
        } else if (ev->type() == QEvent::MouseMove) {
            auto* me = static_cast<QMouseEvent*>(ev);
            if ((me->buttons() & Qt::LeftButton) && pressedItem &&
                pressedItem->parent() != nullptr &&
                (me->pos() - pressPos).manhattanLength() >
                    QApplication::startDragDistance())
            {
                // Find which signal this is
                for (auto& e : entries_) {
                    for (int i = 0; i < e.item->childCount(); ++i) {
                        if (e.item->child(i) == pressedItem) {
                            auto* drag = new QDrag(tree_);
                            auto* mime = new QMimeData();
                            mime->setData(kMimeType, packSignalDrag(e.srcIdx, i));
                            drag->setMimeData(mime);
                            drag->exec(Qt::CopyAction);
                            pressedItem = nullptr;
                            return true;
                        }
                    }
                }
            }
        } else if (ev->type() == QEvent::MouseButtonRelease) {
            pressedItem = nullptr;
        }
    }
    return QDockWidget::eventFilter(obj, ev);
}

void Sidebar::addSource(std::shared_ptr<Source> src, int srcIdx)
{
    auto* topItem = new QTreeWidgetItem(tree_);
    topItem->setText(0, QString("[%1] %2:%3")
        .arg(QString::fromStdString(src->label()))
        .arg(QString::fromStdString(src->host()))
        .arg(src->port()));
    topItem->setExpanded(true);

    entries_.push_back({std::move(src), srcIdx, topItem});
}

void Sidebar::refresh()
{
    for (auto& e : entries_) {
        // Update connection status
        bool conn = e.src->connected();
        e.item->setText(0,
            QString("%1 %2:%3")
                .arg(conn ? "[+]" : "[-]",
                     QString::fromStdString(e.src->host()),
                     QString::number(e.src->port())));

        if (!conn) continue;

        // Sync children to signal list
        std::lock_guard<std::mutex> lk(e.src->signals_mutex);
        int currentCount = e.item->childCount();
        int newCount = static_cast<int>(e.src->signal_states.size());

        // Add missing children
        for (int i = currentCount; i < newCount; ++i) {
            const auto& sig = e.src->signal_states[i];
            auto* child = new QTreeWidgetItem(e.item);
            child->setText(0, QString::fromStdString(sig.def.name));
            child->setToolTip(0,
                QString("Rate: %1 Hz  Elements: %2  Type: %3")
                    .arg(sig.def.sampling_rate)
                    .arg(sig.def.num_elements)
                    .arg(sig.def.type_code));
        }
        // Update existing children name (config may have changed)
        for (int i = 0; i < std::min(currentCount, newCount); ++i) {
            e.item->child(i)->setText(
                0, QString::fromStdString(e.src->signal_states[i].def.name));
        }
        // Remove excess children
        while (e.item->childCount() > newCount)
            delete e.item->takeChild(e.item->childCount() - 1);
    }
}

void Sidebar::onStartDrag(Qt::DropActions) {}
