#pragma once

#include <QQuickItem>
#include <QImage>
#include <QUrl>
#include <QSGTexture>
#include <QSGOpacityNode>
#include <QSGImageNode>
#include <QFutureWatcher>
#include <QElapsedTimer>
#include <QThreadPool>
#include <QTimer>
#include <QtQml/qqml.h>
#include <qtmetamacros.h>
#include <atomic>

namespace ZShell::internal {

class WallpaperImage : public QQuickItem {
	Q_OBJECT
	QML_NAMED_ELEMENT(WallpaperImage)
	Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
	Q_PROPERTY(
		QSize screenResolution READ screenResolution WRITE setScreenResolution
			NOTIFY screenResolutionChanged)
	Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged)
	Q_PROPERTY(Status status READ status NOTIFY statusChanged)
	Q_PROPERTY(
		int fadeDuration READ fadeDuration WRITE setFadeDuration NOTIFY
			fadeDurationChanged)

	Q_PROPERTY(qreal actualCropX READ actualCropX NOTIFY actualCropChanged)
	Q_PROPERTY(qreal actualCropY READ actualCropY NOTIFY actualCropChanged)
	Q_PROPERTY(
		qreal actualCropWidth READ actualCropWidth NOTIFY actualCropChanged)
	Q_PROPERTY(
		qreal actualCropHeight READ actualCropHeight NOTIFY actualCropChanged)

	Q_PROPERTY(qreal cropX READ cropX WRITE setCropX NOTIFY cropXChanged)
	Q_PROPERTY(qreal cropY READ cropY WRITE setCropY NOTIFY cropYChanged)
	Q_PROPERTY(
		qreal cropWidth READ cropWidth WRITE setCropWidth NOTIFY
			cropWidthChanged)
	Q_PROPERTY(
		qreal cropHeight READ cropHeight WRITE setCropHeight NOTIFY
			cropHeightChanged)

	public:
	explicit WallpaperImage(QQuickItem* parent = nullptr);
	~WallpaperImage() override;

	enum Status { Null, Ready, Loading, Error };
	Q_ENUM(Status)

	Status status() const { return m_status; }

	QUrl source() const { return m_source; }
	void setSource(const QUrl& source);

	QSize screenResolution() const { return m_screenResolution; }
	void setScreenResolution(const QSize& screenResolution);

	qreal zoom() const { return m_zoom; }
	void setZoom(qreal zoom);

	int fadeDuration() const { return m_fadeDuration; }
	void setFadeDuration(int ms);

	qreal actualCropX() const { return m_actualCropX; }
	qreal actualCropY() const { return m_actualCropY; }
	qreal actualCropWidth() const { return m_actualCropWidth; }
	qreal actualCropHeight() const { return m_actualCropHeight; }

	qreal cropX() const { return m_cropX; }
	void setCropX(qreal x);

	qreal cropY() const { return m_cropY; }
	void setCropY(qreal y);

	qreal cropWidth() const { return m_cropWidth; }
	void setCropWidth(qreal w);

	qreal cropHeight() const { return m_cropHeight; }
	void setCropHeight(qreal h);

	protected:
	QSGNode* updatePaintNode(
		QSGNode* oldNode, UpdatePaintNodeData* updatePaintNodeData) override;

	signals:
	void sourceChanged();
	void screenResolutionChanged();
	void zoomChanged();
	void fadeDurationChanged();
	void actualCropChanged();
	void cropXChanged();
	void cropYChanged();
	void cropWidthChanged();
	void cropHeightChanged();
	void statusChanged();

	private:
	void beginLoad();
	void handleImageLoaded();
	QString getCacheFilePath() const;
	void setStatus(const Status s);
	QRectF computeSourceRect(QSGTexture* texture) const;
	void configureImageNode(QSGImageNode* node, QSGTexture* texture);

	Status m_status = Null;
	QUrl m_source;
	QSize m_screenResolution;
	qreal m_zoom = 1.0;
	int m_fadeDuration = 400;

	qreal m_actualCropX = 0.0;
	qreal m_actualCropY = 0.0;
	qreal m_actualCropWidth = 1.0;
	qreal m_actualCropHeight = 1.0;

	qreal m_cropX = 0.0;
	qreal m_cropY = 0.0;
	qreal m_cropWidth = 1.0;
	qreal m_cropHeight = 1.0;

	QTimer m_loadDebounceTimer;
	QFutureWatcher<QImage> m_imageWatcher;
	std::shared_ptr<std::atomic<quint64>> m_requestGeneration =
		std::make_shared<std::atomic<quint64>>(0);
	static QThreadPool* loadPool();
	QImage m_pendingImage;
	bool m_hasPending = false;

	QSGTexture* m_texture = nullptr;
	QSGTexture* m_previousTexture = nullptr;

	bool m_fading = false;
	QElapsedTimer m_fadeTimer;

	QSGNode* m_rootNode = nullptr;
	QSGOpacityNode* m_newOpacityNode = nullptr;
	QSGOpacityNode* m_oldOpacityNode = nullptr;
	QSGImageNode* m_newImageNode = nullptr;
	QSGImageNode* m_oldImageNode = nullptr;
};

} // namespace ZShell::internal
