#pragma once

#include "configobject.hpp"
#include "anim.hpp"

#include <QVariantList>
#include <limits>
#include <qobject.h>
#include <qqmlengine.h>
#include <qqmlintegration.h>
#include <qqmlregistration.h>
#include <qtypes.h>

namespace ZShell::config {

class AppearanceRounding : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CFG_PROPERTY(qreal, extraSmall, 4)
	CFG_PROPERTY(qreal, full, std::numeric_limits<int>::max())
	CFG_PROPERTY(qreal, large, 16)
	CFG_PROPERTY(qreal, largeIncreased, 20)
	CFG_PROPERTY(qreal, extraLarge, 28)
	CFG_PROPERTY(qreal, extraLargeIncreased, 32)
	CFG_PROPERTY(qreal, extraExtraLarge, 48)
	CFG_PROPERTY(qreal, medium, 12)
	CFG_PROPERTY(qreal, scale, 1)
	CFG_PROPERTY(qreal, small, 8)

	public:
	explicit AppearanceRounding(QObject* parent = nullptr)
		: ConfigObject(parent) {}
};

class AppearancePadding : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CFG_PROPERTY(int, extraSmall, 4)
	CFG_PROPERTY(int, smaller, 5)
	CFG_PROPERTY(int, small, 8)
	CFG_PROPERTY(int, medium, 12)
	CFG_PROPERTY(int, large, 16)
	CFG_PROPERTY(int, largeIncreased, 20)
	CFG_PROPERTY(int, extraLarge, 28)
	CFG_PROPERTY(int, extraLargeIncreased, 32)
	CFG_PROPERTY(int, extraExtraLarge, 48)

	public:
	explicit AppearancePadding(QObject* parent = nullptr)
		: ConfigObject(parent) {}
};

class AppearanceSpacing : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CFG_PROPERTY(int, extraSmall, 4)
	CFG_PROPERTY(int, small, 8)
	CFG_PROPERTY(int, medium, 12)
	CFG_PROPERTY(int, large, 16)
	CFG_PROPERTY(int, largeIncreased, 20)
	CFG_PROPERTY(int, extraLarge, 28)
	CFG_PROPERTY(int, extraLargeIncreased, 32)
	CFG_PROPERTY(int, extraExtraLarge, 48)

	public:
	explicit AppearanceSpacing(QObject* parent = nullptr)
		: ConfigObject(parent) {}
};

class FontSizeTokens : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CFG_PROPERTY(int, small, 11)
	CFG_PROPERTY(int, smaller, 12)
	CFG_PROPERTY(int, normal, 13)
	CFG_PROPERTY(int, larger, 15)
	CFG_PROPERTY(int, large, 18)
	CFG_PROPERTY(int, extraLarge, 28)

	public:
	explicit FontSizeTokens(QObject* parent = nullptr) : ConfigObject(parent) {}
};

class FontTokens : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CONFIG_SUBOBJECT(FontSizeTokens, size)

	public:
	explicit FontTokens(QObject* parent = nullptr)
		: ConfigObject(parent), m_size(new FontSizeTokens(this)) {}
};

class AnimCurves : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CFG_PROPERTY(QList<qreal>, emphasized)
	CFG_PROPERTY(QList<qreal>, emphasizedAccel)
	CFG_PROPERTY(QList<qreal>, emphasizedDecel)
	CFG_PROPERTY(QList<qreal>, standard)
	CFG_PROPERTY(QList<qreal>, standardAccel)
	CFG_PROPERTY(QList<qreal>, standardDecel)
	CFG_PROPERTY(QList<qreal>, expressiveFastSpatial)
	CFG_PROPERTY(QList<qreal>, expressiveDefaultSpatial)
	CFG_PROPERTY(QList<qreal>, expressiveSlowSpatial)
	CFG_PROPERTY(QList<qreal>, expressiveFastEffects)
	CFG_PROPERTY(QList<qreal>, expressiveDefaultEffects)
	CFG_PROPERTY(QList<qreal>, expressiveSlowEffects)

	public:
	explicit AnimCurves(QObject* parent = nullptr)
		: ConfigObject(parent)
		, m_emphasized(
			  {0.05,
			   0,
			   2.0 / 15.0,
			   0.06,
			   1.0 / 6.0,
			   0.4,
			   5.0 / 24.0,
			   0.82,
			   0.25,
			   1,
			   1,
			   1})
		, m_emphasizedAccel({0.3, 0, 0.8, 0.15, 1, 1})
		, m_emphasizedDecel({0.05, 0.7, 0.1, 1, 1, 1})
		, m_standard({0.2, 0, 0, 1, 1, 1})
		, m_standardAccel({0.3, 0, 1, 1, 1, 1})
		, m_standardDecel({0, 0, 0, 1, 1, 1})
		, m_expressiveFastSpatial({0.42, 1.67, 0.21, 0.9, 1, 1})
		, m_expressiveDefaultSpatial({0.38, 1.21, 0.22, 1, 1, 1})
		, m_expressiveSlowSpatial({0.39, 1.29, 0.35, 0.98, 1, 1})
		, m_expressiveFastEffects({0.31, 0.94, 0.34, 1, 1, 1})
		, m_expressiveDefaultEffects({0.34, 0.8, 0.34, 1, 1, 1})
		, m_expressiveSlowEffects({0.34, 0.88, 0.34, 1, 1, 1}) {}
};

class AnimDurations : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CFG_PROPERTY(int, small, 200)
	CFG_PROPERTY(int, scale, 1)
	CFG_PROPERTY(int, normal, 400)
	CFG_PROPERTY(int, large, 600)
	CFG_PROPERTY(int, extraLarge, 1000)
	CFG_PROPERTY(int, expressiveFastSpatial, 350)
	CFG_PROPERTY(int, expressiveDefaultSpatial, 500)
	CFG_PROPERTY(int, expressiveSlowSpatial, 650)
	CFG_PROPERTY(int, expressiveFastEffects, 150)
	CFG_PROPERTY(int, expressiveDefaultEffects, 200)
	CFG_PROPERTY(int, expressiveSlowEffects, 300)

	public:
	explicit AnimDurations(QObject* parent = nullptr) : ConfigObject(parent) {}
};

class AnimTokens : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CONFIG_SUBOBJECT(AnimDurations, durations)

	Q_PROPERTY(QEasingCurve emphasized READ emphasized NOTIFY curvesChanged)
	Q_PROPERTY(
		QEasingCurve emphasizedAccel READ emphasizedAccel NOTIFY curvesChanged)
	Q_PROPERTY(
		QEasingCurve emphasizedDecel READ emphasizedDecel NOTIFY curvesChanged)
	Q_PROPERTY(QEasingCurve standard READ standard NOTIFY curvesChanged)
	Q_PROPERTY(
		QEasingCurve standardAccel READ standardAccel NOTIFY curvesChanged)
	Q_PROPERTY(
		QEasingCurve standardDecel READ standardDecel NOTIFY curvesChanged)
	Q_PROPERTY(
		QEasingCurve expressiveFastSpatial READ expressiveFastSpatial NOTIFY
			curvesChanged)
	Q_PROPERTY(
		QEasingCurve expressiveDefaultSpatial READ expressiveDefaultSpatial
			NOTIFY curvesChanged)
	Q_PROPERTY(
		QEasingCurve expressiveSlowSpatial READ expressiveSlowSpatial NOTIFY
			curvesChanged)
	Q_PROPERTY(
		QEasingCurve expressiveFastEffects READ expressiveFastEffects NOTIFY
			curvesChanged)
	Q_PROPERTY(
		QEasingCurve expressiveDefaultEffects READ expressiveDefaultEffects
			NOTIFY curvesChanged)
	Q_PROPERTY(
		QEasingCurve expressiveSlowEffects READ expressiveSlowEffects NOTIFY
			curvesChanged)

	public:
	explicit AnimTokens(QObject* parent = nullptr)
		: ConfigObject(parent)
		, m_curves(new AnimCurves(this))
		, m_durations(new AnimDurations(this))
		, m_easing(new AnimEasing(this)) {
		connect(
			m_easing,
			&AnimEasing::curvesChanged,
			this,
			&AnimTokens::curvesChanged);
		m_easing->bindCurves(m_curves);
	}

	[[nodiscard]] QEasingCurve emphasized() const {
		return m_easing->emphasized();
	}
	[[nodiscard]] QEasingCurve emphasizedAccel() const {
		return m_easing->emphasizedAccel();
	}
	[[nodiscard]] QEasingCurve emphasizedDecel() const {
		return m_easing->emphasizedDecel();
	}
	[[nodiscard]] QEasingCurve standard() const { return m_easing->standard(); }
	[[nodiscard]] QEasingCurve standardAccel() const {
		return m_easing->standardAccel();
	}
	[[nodiscard]] QEasingCurve standardDecel() const {
		return m_easing->standardDecel();
	}
	[[nodiscard]] QEasingCurve expressiveFastSpatial() const {
		return m_easing->expressiveFastSpatial();
	}
	[[nodiscard]] QEasingCurve expressiveDefaultSpatial() const {
		return m_easing->expressiveDefaultSpatial();
	}
	[[nodiscard]] QEasingCurve expressiveSlowSpatial() const {
		return m_easing->expressiveSlowSpatial();
	}
	[[nodiscard]] QEasingCurve expressiveFastEffects() const {
		return m_easing->expressiveFastEffects();
	}
	[[nodiscard]] QEasingCurve expressiveDefaultEffects() const {
		return m_easing->expressiveDefaultEffects();
	}
	[[nodiscard]] QEasingCurve expressiveSlowEffects() const {
		return m_easing->expressiveSlowEffects();
	}

	signals:
	void curvesChanged();

	private:
	AnimCurves* m_curves;
	AnimEasing* m_easing;
};

class BarTokens : public ConfigObject {
	Q_OBJECT
	QML_ANONYMOUS

	CFG_PROPERTY(int, innerSize, 28)

	public:
	explicit BarTokens(QObject* parent = nullptr) : ConfigObject(parent) {}
};

class Tokens : public ConfigObject {
	Q_OBJECT
	QML_ELEMENT
	QML_SINGLETON

	CONFIG_SUBOBJECT(AppearanceRounding, rounding)
	CONFIG_SUBOBJECT(AppearancePadding, padding)
	CONFIG_SUBOBJECT(AppearanceSpacing, spacing)
	CONFIG_SUBOBJECT(FontTokens, font)
	CONFIG_SUBOBJECT(AnimTokens, anim)
	CONFIG_SUBOBJECT(BarTokens, bar)

	public:
	explicit Tokens(QObject* parent = nullptr)
		: ConfigObject(parent)
		, m_rounding(new AppearanceRounding(this))
		, m_padding(new AppearancePadding(this))
		, m_spacing(new AppearanceSpacing(this))
		, m_font(new FontTokens(this))
		, m_anim(new AnimTokens(this))
		, m_bar(new BarTokens(this)) {}

	static Tokens* create(QQmlEngine*, QJSEngine*) { return new Tokens(); }
};

} // namespace ZShell::config
