#include <Graphics/ConnectionBox.h>

#include <utility>

#include <QtGui/QDrag>
#include <QtWidgets/QApplication>

#include <Graphics/GraphicsBox.h>
#include <Graphics/GraphicsScene.h>

const QColor ConnectionBox::defaultColor = Qt::white;

ConnectionBox::ConnectionBox(GraphicsBox* parent, Type type)
    : QGraphicsRectItem(-8, -8, 16, 16, parent)
    , _type(type)

{
    this->setBrush(ConnectionBox::defaultColor);
    this->setAcceptedMouseButtons(Qt::LeftButton);
}

void ConnectionBox::mousePressEvent(QGraphicsSceneMouseEvent* event) {
    this->setCursor(Qt::UpArrowCursor);

    Q_ASSERT(!this->tempLine);
    this->tempLine = this->scene()->addLine({this->scenePos(), this->scenePos()});
    this->tempLine->setZValue(2);
}

void ConnectionBox::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
    Q_ASSERT(this->tempLine);
    QLineF line = this->tempLine->line();
    line.setP2(event->scenePos());
    this->tempLine->setLine(line);

    static ConnectionBox* other = nullptr;
    if (other)
    {
        other->setColor(ConnectionBox::defaultColor);
    }

    other = nullptr;
    for (auto* item: this->scene()->items(event->scenePos()))
    {
        if (auto* cb = dynamic_cast<ConnectionBox*>(item))
        {
            other = cb;
            break;
        }
    }

    if (other && other->getType() != this->getType())
    {
        other->setColor(Qt::yellow);
    }
}

void ConnectionBox::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
    this->setCursor(Qt::ArrowCursor);

    Q_ASSERT(this->tempLine);
    delete this->tempLine;
    this->tempLine = nullptr;

    auto* other = dynamic_cast<ConnectionBox*>(this->scene()->itemAt(event->scenePos(), {}));
    if (other && other->getType() != this->getType())
    {
        this->setColor(ConnectionBox::defaultColor);
        other->setColor(ConnectionBox::defaultColor);
        ConnectionBox::connect(this, other);
    }
}

void ConnectionBox::connect(ConnectionBox* a, ConnectionBox* b) {
    Q_ASSERT(a->getType() != b->getType());
    auto* origin = a;
    auto* target = b;
    if (origin->getType() == ConnectionBox::Type::Input)
        std::swap(origin, target);

    origin->connection = target->connection = std::make_shared<Connection>(origin, target);
}

void ConnectionBox::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
    QMenu menu;
    QAction* a = menu.addAction("&Disconnect");
    QObject::connect(a, SIGNAL(triggered()), this, SLOT(on_Disconnect()));
    menu.exec(event->screenPos());
    event->accept();
}

void ConnectionBox::on_Disconnect() {
    auto* origin = this->connection->getOriginConnectionBox();
    auto* target = this->connection->getTargetConnectionBox();
    origin->getWrapperBox()->removeOutput();
    target->getWrapperBox()->removeInput(origin->getWrapperBox(), nullptr);

    origin->connection.reset();
    target->connection.reset();
}

void ConnectionBox::setColor(QColor color) {
    QBrush brush = this->brush();
    brush.setColor(color);
    this->setBrush(brush);
}

WrapperBox* ConnectionBox::getWrapperBox() const {
    return dynamic_cast<GraphicsBox*>(this->parentObject())->getWrapperBox();
}