import QtQuick
import Quickshell
import M3Shapes
import ZShell.Config
import qs.Services

MaterialShape {
	id: root

	property bool animated: true
	property real cRotation
	property bool containsIcon
	property real dampingRatio: 0.6
	property real lRotation
	property int morphAnimRotation: 60
	property real morphScale: 0.14
	property alias rotateAnimDuration: rotateAnim.duration
	property int shapeIndex
	property list<int> shapes: {
		if (containsIcon)
			return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
		return [MaterialShape.SoftBurst, MaterialShape.Cookie9Sided, MaterialShape.Pentagon, MaterialShape.Pill, MaterialShape.Sunny, MaterialShape.Cookie4Sided, MaterialShape.Oval];
	}
	readonly property real springDuration: {
		const wn = Math.sqrt(stiffness);
		const r = -dampingRatio * wn;
		const c = 1 / Math.sqrt(1 - dampingRatio * dampingRatio);
		return Math.log(visibilityThreshold / c) / r;
	}
	readonly property real springMaxVelocity: {
		const wn = Math.sqrt(stiffness);
		const factor = Math.exp(-z * Math.acos(z) / Math.sqrt(1 - z * z));
		return wn * factor;
	}
	property bool springSettled: true
	property real stiffness: 180
	property real thisLRotation
	property real visibilityThreshold: 0.075

	function spring(t: real): var {
		const wn = Math.sqrt(stiffness);
		const za = dampingRatio * wn;

		const wd = wn * Math.sqrt(1 - dampingRatio * dampingRatio);
		const r = za / wd;
		const pos = 1 - Math.exp(-za * t) * (Math.cos(wd * t) + r * Math.sin(wd * t));
		const vel = Math.exp(-za * t) * (wn * wn / wd) * Math.sin(wd * t);

		return [pos, vel];
	}

	color: Colors.palette.m3primary
	implicitSize: 38
	toShape: shapes[0]

	RotationAnimation on cRotation {
		id: rotateAnim

		duration: 4666
		easing.type: Easing.Linear
		from: 0
		loops: Animation.Infinite
		running: root.animated
		to: 360
	}
	Behavior on color {
		CAnim {
		}
	}

	ElapsedTimer {
		id: timer
	}

	FrameAnimation {
		running: root.animated && !root.springSettled

		onTriggered: {
			const t = timer.elapsed();

			if (t >= root.springDuration) {
				root.springSettled = true;
			} else {
				const [pos, vel] = root.spring(t);
				root.morphProgress = Math.min(1, pos); // Overshooting the morph looks weird
				root.thisLRotation = pos * root.morphAnimRotation;
				root.scale = 1 + vel * root.morphScale / root.springMaxVelocity;
			}
		}
	}

	Timer {
		interval: 650
		repeat: true
		running: root.animated
		triggeredOnStart: true

		onTriggered: {
			root.beginBatchUpdate();
			root.fromShape = root.toShape;
			root.shapeIndex = (root.shapeIndex + 1) % root.shapes.length;
			root.toShape = root.shapes[root.shapeIndex];
			root.morphProgress = 0;

			root.rotation = root.rotation;
			root.lRotation = (root.lRotation + root.thisLRotation) % 360;
			root.thisLRotation = 0;
			root.rotation = Qt.binding(() => root.cRotation + root.lRotation + root.thisLRotation);

			root.springSettled = false;
			timer.restart();
			root.endBatchUpdate();
		}
	}
}
