#pragma once

#include <QQuickItem>
#include <QQuickWindow>
#include <QWheelEvent>
#include <QCoreApplication>
#include <QPointer>
#include <QDebug>

namespace ZShell::components {

class WheelInverter : public QQuickItem {
	Q_OBJECT
	QML_ELEMENT

	Q_PROPERTY(
		QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged)

	public:
	using QQuickItem::QQuickItem;

	QQuickItem* target() const { return m_target; }

	void setTarget(QQuickItem* target) {
		if (m_target == target) return;

		if (m_target) m_target->removeEventFilter(this);

		m_target = target;

		if (m_target) m_target->installEventFilter(this);

		emit targetChanged();
	}

	protected:
	class InvertedWheelEvent : public QWheelEvent {
		public:
		using QWheelEvent::QWheelEvent;

		bool invertedByWheelInverter = true;
	};

	bool eventFilter(QObject* watched, QEvent* event) override {
		if (watched != m_target || event->type() != QEvent::Wheel)
			return QQuickItem::eventFilter(watched, event);

		auto* wheel = static_cast<QWheelEvent*>(event);

		const bool inverted = dynamic_cast<InvertedWheelEvent*>(wheel) !=
							  nullptr;

		if (inverted) return false;

		auto* window = m_target ? m_target->window() : nullptr;
		if (!window) {
			qInfo() << "[WheelInverter] no window";
			return false;
		}

		const QPointF windowPos =
			window->mapFromGlobal(wheel->globalPosition());

		auto* invertedEvent = new InvertedWheelEvent(
			windowPos,
			wheel->globalPosition(),
			-wheel->pixelDelta(),
			-wheel->angleDelta(),
			wheel->buttons(),
			wheel->modifiers(),
			wheel->phase(),
			wheel->inverted(),
			wheel->source(),
			wheel->pointingDevice());

		invertedEvent->setTimestamp(wheel->timestamp());

		QCoreApplication::postEvent(window, invertedEvent);

		return true;
	}

	signals:
	void targetChanged();

	private:
	QPointer<QQuickItem> m_target;
};

} // namespace ZShell::components
