#include "patchboardscene.h"
using Xybrid::UI::PatchboardScene;

#include <cmath>

#include <QDebug>
#include <QScrollBar>
#include <QGraphicsItem>
#include <QMainWindow>
#include <QMenu>
#include <QTimer>

#include <QGraphicsSceneContextMenuEvent>

#include "data/graph.h"
using namespace Xybrid::Data;
#include "config/pluginregistry.h"
using namespace Xybrid::Config;

#include "ui/patchboard/nodeobject.h"

#include "config/colorscheme.h"

PatchboardScene::PatchboardScene(QGraphicsView* parent, const std::shared_ptr<Xybrid::Data::Graph>& g) : QGraphicsScene(parent) {
    graph = g;
    view = parent;

    connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, this, &PatchboardScene::queueResize);
    connect(view->verticalScrollBar(), &QScrollBar::valueChanged, this, &PatchboardScene::queueResize);
    connect(view->horizontalScrollBar(), &QScrollBar::rangeChanged, this, &PatchboardScene::queueResize);
    connect(view->verticalScrollBar(), &QScrollBar::rangeChanged, this, &PatchboardScene::queueResize);
    connect(this, &QGraphicsScene::changed, this, &PatchboardScene::queueResize);

    refresh();
}

void PatchboardScene::drawBackground(QPainter* painter, const QRectF& rect) {
    painter->setBrush(QBrush(Config::colorScheme.patternBg));
    painter->setPen(QPen(Qt::PenStyle::NoPen));
    painter->drawRect(rect);

    const constexpr int step = 32; // grid size
    painter->setPen(QPen(QColor(127, 127, 127, 63), 1));
    for (auto y = std::floor(rect.top() / step) * step + .5; y < rect.bottom(); y += step)
        painter->drawLine(QPointF(rect.left(), y), QPointF(rect.right(), y));
    for (auto x = std::floor(rect.left() / step) * step + .5; x < rect.right(); x += step)
        painter->drawLine(QPointF(x, rect.top()), QPointF(x, rect.bottom()));
}

void PatchboardScene::contextMenuEvent(QGraphicsSceneContextMenuEvent* e) {
    // only override if nothing inside picks it up
    QGraphicsScene::contextMenuEvent(e);
    if (e->isAccepted()) return;

    auto p = e->scenePos();
    auto* m = new QMenu();
    PluginRegistry::populatePluginMenu(m->addMenu("Add..."), [this, p](std::shared_ptr<Node> n) {
        n->x = static_cast<int>(p.x());
        n->y = static_cast<int>(p.y());
        n->parentTo(graph);

        addItem(new NodeObject(n));
    }, graph.get());

    m->setAttribute(Qt::WA_DeleteOnClose);
    m->popup(e->screenPos());
}

void PatchboardScene::queueResize() {
    if (!resizeQueued) {
        resizeQueued = true;
        QTimer::singleShot(1, this, &PatchboardScene::autoSetSize);
    }
}

void PatchboardScene::autoSetSize() {
    if (view->scene() != this) return;
    auto vrect = view->mapToScene(view->viewport()->visibleRegion().boundingRect()).boundingRect();
    auto rect = itemsBoundingRect()
                .united(vrect)
                .united(QRectF(0, 0, 1, 1));
    rect.setTopLeft(QPointF(0, 0));
    setSceneRect(rect);
    graph->viewX = static_cast<int>(vrect.left());
    graph->viewY = static_cast<int>(vrect.top());
    resizeQueued = false;
}

void PatchboardScene::refresh() {
    // build scene from graph
    clear();

    for (auto n : graph->children) {
        auto* o = new NodeObject(n);
        addItem(o);
    }
}
