#include "GroupGraphicsObject.hpp"
#include "BasicGraphicsScene.hpp"
#include "NodeConnectionInteraction.hpp"
#include "NodeGraphicsObject.hpp"
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QtNodes/DataFlowGraphModel>

using QtNodes::BasicGraphicsScene;
using QtNodes::ConnectionId;
using QtNodes::DataFlowGraphModel;
using QtNodes::GroupGraphicsObject;
using QtNodes::NodeConnectionInteraction;
using QtNodes::NodeGraphicsObject;
using QtNodes::NodeGroup;

IconGraphicsItem::IconGraphicsItem(QGraphicsItem *parent)
    : QGraphicsPixmapItem(parent)
{}

IconGraphicsItem::IconGraphicsItem(const QPixmap &pixmap, QGraphicsItem *parent)
    : QGraphicsPixmapItem(pixmap, parent)
{
    _scaleFactor = _iconSize / pixmap.size().width();
    setScale(_scaleFactor);
}

double IconGraphicsItem::scaleFactor() const
{
    return _scaleFactor;
}

GroupGraphicsObject::GroupGraphicsObject(BasicGraphicsScene &scene, NodeGroup &nodeGroup)
    : _scene(scene)
    , _group(nodeGroup)
    , _possibleChild(nullptr)
    , _locked(false)
{
    setRect(0, 0, _defaultWidth, _defaultHeight);

    _lockedGraphicsItem = new IconGraphicsItem(_lockedIcon, this);
    _unlockedGraphicsItem = new IconGraphicsItem(_unlockedIcon, this);

    _scene.addItem(this);

    setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(QGraphicsItem::ItemIsFocusable, true);
    setFlag(QGraphicsItem::ItemIsSelectable, true);
    setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true);

    _currentFillColor = kUnlockedFillColor;
    _currentBorderColor = kUnselectedBorderColor;

    _borderPen = QPen(_currentBorderColor, 1.0, Qt::PenStyle::DashLine);

    setZValue(-_groupAreaZValue);

    setAcceptHoverEvents(true);
}

GroupGraphicsObject::~GroupGraphicsObject()
{
    _scene.removeItem(this);
}

NodeGroup &GroupGraphicsObject::group()
{
    return _group;
}

NodeGroup const &GroupGraphicsObject::group() const
{
    return _group;
}

QRectF GroupGraphicsObject::boundingRect() const
{
    QRectF ret{};
    for (auto &node : _group.childNodes()) {
        ret |= node->mapRectToScene(node->boundingRect());
    }
    if (_possibleChild) {
        ret |= _possibleChild->mapRectToScene(_possibleChild->boundingRect());
    }
    return mapRectFromScene(ret.marginsAdded(_margins));
}

void GroupGraphicsObject::setFillColor(const QColor &color)
{
    _currentFillColor = color;
    update();
}

void GroupGraphicsObject::setBorderColor(const QColor &color)
{
    _currentBorderColor = color;
    _borderPen.setColor(_currentBorderColor);
}

void GroupGraphicsObject::moveConnections()
{
    for (auto &node : group().childNodes()) {
        node->moveConnections();
    }
}

void GroupGraphicsObject::moveNodes(const QPointF &offset)
{
    for (auto &node : group().childNodes()) {
        auto newPosition = QPointF(node->x() + offset.x(), node->y() + offset.y());
        node->setPos(newPosition);
        node->update();
    }
}

void GroupGraphicsObject::lock(bool locked)
{
    for (auto &node : _group.childNodes()) {
        node->lock(locked);
    }
    _lockedGraphicsItem->setVisible(locked);
    _unlockedGraphicsItem->setVisible(!locked);
    setFillColor(locked ? kLockedFillColor : kUnlockedFillColor);
    _locked = locked;
    setZValue(locked ? _groupAreaZValue : -_groupAreaZValue);
}

bool GroupGraphicsObject::locked() const
{
    return _locked;
}

void GroupGraphicsObject::positionLockedIcon()
{
    _lockedGraphicsItem->setPos(
        boundingRect().topRight()
        + QPointF(-(_roundedBorderRadius + IconGraphicsItem::iconSize()), _roundedBorderRadius));
    _unlockedGraphicsItem->setPos(
        boundingRect().topRight()
        + QPointF(-(_roundedBorderRadius + IconGraphicsItem::iconSize()), _roundedBorderRadius));
}

void GroupGraphicsObject::setHovered(bool hovered)
{
    hovered ? setFillColor(locked() ? kLockedHoverColor : kUnlockedHoverColor)
            : setFillColor(locked() ? kLockedFillColor : kUnlockedFillColor);

    for (auto &node : _group.childNodes()) {
        auto ngo = node->nodeScene()->nodeGraphicsObject(node->nodeId());
        ngo->nodeState().setHovered(hovered);
        node->update();
    }
    update();
}

void GroupGraphicsObject::setPossibleChild(QtNodes::NodeGraphicsObject *possibleChild)
{
    _possibleChild = possibleChild;
}

void GroupGraphicsObject::unsetPossibleChild()
{
    _possibleChild = nullptr;
}

std::vector<std::shared_ptr<ConnectionId>> GroupGraphicsObject::connections() const
{
    return _scene.connectionsWithinGroup(group().id());
}

void GroupGraphicsObject::setPosition(const QPointF &position)
{
    QPointF diffPos = position - scenePos();
    moveNodes(diffPos);
    moveConnections();
}

void GroupGraphicsObject::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    Q_UNUSED(event);
    setHovered(true);
}

void GroupGraphicsObject::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    Q_UNUSED(event);
    setHovered(false);
}

void GroupGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::mouseMoveEvent(event);
    if (event->lastPos() != event->pos()) {
        auto diff = event->pos() - event->lastPos();
        moveNodes(diff);
        moveConnections();
    }
}

void GroupGraphicsObject::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem::mouseDoubleClickEvent(event);
    lock(!locked());
}

void GroupGraphicsObject::paint(QPainter *painter,
                                const QStyleOptionGraphicsItem *option,
                                QWidget *widget)
{
    Q_UNUSED(widget);
    prepareGeometryChange();
    setRect(boundingRect());
    positionLockedIcon();
    painter->setClipRect(option->exposedRect);
    painter->setBrush(_currentFillColor);

    setBorderColor(isSelected() ? kSelectedBorderColor : kUnselectedBorderColor);
    painter->setPen(_borderPen);

    painter->drawRoundedRect(rect(), _roundedBorderRadius, _roundedBorderRadius);
}