pragma ComponentBehavior: Bound

import Quickshell
import QtQuick
import ZShell.Components
import qs.Components
import qs.Config
import qs.Modules
import qs.Daemons

LazyListView {
	id: root

	required property Flickable container
	required property Props props
	required property PersistentProperties visibilities

	anchors.left: parent?.left
	anchors.right: parent?.right
	asynchronous: true
	cacheBuffer: 400
	implicitHeight: contentHeight
	readyDelay: 1
	removeDuration: Appearance.anim.durations.normal
	spacing: Appearance.spacing.small
	useCustomViewport: true
	viewport: Qt.rect(0, container.contentY, width, container.height)

	delegate: Component {
		MouseArea {
			id: notif

			readonly property bool closed: notifInner.notifCount === 0
			required property int index
			required property string modelData
			property int startY

			function closeAll(): void {
				clearTimer.start();
			}

			LazyListView.preferredHeight: closed ? 0 : notifInner.nonAnimHeight
			LazyListView.trackViewport: !notifInner.expanded && notifInner.nonAnimHeight < notifInner.implicitHeight
			LazyListView.visibleHeight: notifInner.implicitHeight
			acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
			cursorShape: pressed ? Qt.ClosedHandCursor : undefined
			drag.axis: Drag.XAxis
			drag.target: this
			enabled: !closed
			hoverEnabled: true
			implicitHeight: notifInner.implicitHeight
			opacity: LazyListView.removing || closed || LazyListView.adding ? 0 : 1
			preventStealing: true
			scale: LazyListView.removing || closed ? 0.6 : LazyListView.adding ? 0 : 1

			Behavior on opacity {
				Anim {
				}
			}
			Behavior on scale {
				Anim {
					duration: Appearance.anim.durations.expressiveDefaultSpatial
					easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
				}
			}
			Behavior on x {
				Anim {
					duration: Appearance.anim.durations.expressiveDefaultSpatial
					easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
				}
			}
			Behavior on y {
				enabled: notif.LazyListView.ready

				Anim {
					duration: Appearance.anim.durations.expressiveDefaultSpatial
					easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
				}
			}

			onPositionChanged: event => {
				if (pressed) {
					const diffY = event.y - startY;
					if (Math.abs(diffY) > Config.notifs.expandThreshold)
						notifInner.toggleExpand(diffY > 0);
				}
			}
			onPressed: event => {
				startY = event.y;
				if (event.button === Qt.RightButton)
					notifInner.toggleExpand(!notifInner.expanded);
				else if (event.button === Qt.MiddleButton)
					closeAll();
			}
			onReleased: event => {
				if (Math.abs(x) < width * Config.notifs.clearThreshold)
					x = 0;
				else
					closeAll();
			}

			Timer {
				id: clearTimer

				interval: 15
				repeat: true
				triggeredOnStart: true

				onTriggered: {
					const notifs = NotifServer.notClosed.filter(n => n.appName === notif.modelData);
					if (notifs.length === 0) {
						stop();
						return;
					}

					for (const n of notifs.slice(0, 30))
						n.close();
				}
			}

			NotifGroup {
				id: notifInner

				container: root.container
				modelData: notif.modelData
				props: root.props
				visibilities: root.visibilities
			}
		}
	}
	model: ScriptModel {
		values: {
			const map = new Map();
			for (const n of NotifServer.notClosed)
				map.set(n.appName, null);
			for (const n of NotifServer.list)
				map.set(n.appName, null);
			return [...map.keys()];
		}
	}

	onViewportAdjustNeeded: d => {
		if (contentYAnim.running)
			contentYAnim.complete();
		contentYAnim.to = Math.max(0, container.contentY + d);
		contentYAnim.start();
	}

	Anim {
		id: contentYAnim

		duration: Appearance.anim.durations.expressiveDefaultSpatial
		easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
		property: "contentY"
		target: root.container
	}
}
