// SPDX-License-Identifier: LGPL-2.1-or-later
//
// SPDX-FileCopyrightText: 2010 Bastian Holst <bastianholst@gmx.de>
//

// Self
#include "WidgetGraphicsItem.h"
#include "WidgetGraphicsItem_p.h"

// Marble
#include "MarbleDebug.h"
#include "MarbleWidget.h"

// Qt
#include <QApplication>
#include <QMouseEvent>
#include <QPainter>
#include <QWidget>

using namespace Marble;

WidgetGraphicsItemPrivate::WidgetGraphicsItemPrivate(WidgetGraphicsItem *widgetGraphicsItem, MarbleGraphicsItem *parent)
    : ScreenGraphicsItemPrivate(widgetGraphicsItem, parent)
    , m_widget(nullptr)
    , m_marbleWidget(nullptr)
    , m_activeWidget(nullptr)
{
    // nothing to do
}

WidgetGraphicsItemPrivate::~WidgetGraphicsItemPrivate()
{
    delete m_widget;
}

WidgetGraphicsItem::WidgetGraphicsItem(MarbleGraphicsItem *parent)
    : ScreenGraphicsItem(new WidgetGraphicsItemPrivate(this, parent))
{
}

WidgetGraphicsItem::~WidgetGraphicsItem() = default;

void WidgetGraphicsItem::setWidget(QWidget *widget)
{
    Q_D(WidgetGraphicsItem);
    d->m_widget = widget;

    QSize size = widget->sizeHint().expandedTo(widget->size());
    size = size.expandedTo(widget->minimumSize());
    size = size.boundedTo(widget->maximumSize());
    setSize(size);
    widget->resize(size);
}

QWidget *WidgetGraphicsItem::widget() const
{
    Q_D(const WidgetGraphicsItem);
    return d->m_widget;
}

void WidgetGraphicsItem::paint(QPainter *painter)
{
    Q_D(WidgetGraphicsItem);
    if (d->m_widget == nullptr)
        return;

    // Paint widget without a background
    d->m_widget->render(painter, QPoint(0, 0), QRegion(), QWidget::RenderFlags(QWidget::DrawChildren));
}

bool WidgetGraphicsItem::eventFilter(QObject *object, QEvent *e)
{
    Q_D(WidgetGraphicsItem);
    if (!visible() || d->m_widget == nullptr) {
        return false;
    }

    auto widget = dynamic_cast<MarbleWidget *>(object);
    if (!widget) {
        return ScreenGraphicsItem::eventFilter(object, e);
    }

    if (d->m_marbleWidget != widget) {
        // Delayed initialization
        d->m_marbleWidget = widget;
    }

    Q_ASSERT(d->m_marbleWidget);

    if (e->type() == QEvent::MouseButtonDblClick || e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonPress
        || e->type() == QEvent::MouseButtonRelease) {
        // Mouse events are forwarded to the underlying widget
        auto event = static_cast<QMouseEvent *>(e);

        const QList<QPointF> widgetPositions = absolutePositions();
        QRectF widgetItemRect;
        QPoint shiftedPos;
        QList<QPointF>::ConstIterator it = widgetPositions.begin();
        bool foundRightPosition = false;
        for (; !foundRightPosition && it != widgetPositions.end(); ++it) {
            widgetItemRect = QRectF(*it, size());

            if (widgetItemRect.contains(event->pos())) {
                foundRightPosition = true;
                shiftedPos = event->pos() - widgetItemRect.topLeft().toPoint();
            }
        }

        if (foundRightPosition) {
            QWidget *child = d->m_widget->childAt(shiftedPos);

            if (d->m_activeWidget && d->m_activeWidget != child) {
                QEvent leaveEvent(QEvent::Leave);
                QApplication::sendEvent(d->m_activeWidget, &leaveEvent);
            }

            if (child && d->m_activeWidget != child) {
                QEvent enterEvent(QEvent::Enter);
                QApplication::sendEvent(child, &enterEvent);
            }
            d->m_activeWidget = child;

            if (child) {
                shiftedPos -= child->pos(); // transform to children's coordinates
                QMouseEvent shiftedEvent = QMouseEvent(e->type(), shiftedPos, event->globalPos(), event->button(), event->buttons(), event->modifiers());
                if (QApplication::sendEvent(child, &shiftedEvent)) {
                    d->m_marbleWidget->setCursor(d->m_widget->cursor());
                    return true;
                }
            }
        } else {
            if (d->m_activeWidget) {
                QEvent leaveEvent(QEvent::Leave);
                QApplication::sendEvent(d->m_activeWidget, &leaveEvent);
                d->m_activeWidget = nullptr;
            }
        }
    }

    return ScreenGraphicsItem::eventFilter(object, e);
}
