pragma ComponentBehavior: Bound

import QtQuick
import Quickshell
import ZShell.Config
import qs.Components
import qs.Services
import qs.Modules.Launcher.Items
import qs.Modules.Launcher.Services

CustomListView {
	id: root

	readonly property string displayState: stateForText(displayText)
	property string displayText
	readonly property string requestedState: stateForText(search.text)
	required property SearchBar search
	required property PersistentProperties visibilities

	function resultsForText(text: string): var {
		switch (stateForText(text)) {
		case "actions":
			return Actions.query(text);
		case "calc":
			return [0];
		case "variant":
			return SchemeVariants.query(text);
		case "files":
			return Files.query(text);
		default:
			return Apps.search(text);
		}
	}

	function stateForText(text: string): string {
		const prefix = Config.launcher.actionPrefix;
		if (text.startsWith(prefix)) {
			for (const action of ["calc", "scheme", "variant", "files"])
				if (text.startsWith(`${prefix}${action} `))
					return action;

			return "actions";
		}

		return "apps";
	}

	function syncDisplayText(): void {
		if (visibilities.launcher && requestedState === displayState)
			displayText = search.text;
	}

	highlightFollowsCurrentItem: false
	highlightRangeMode: ListView.ApplyRange
	implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
	orientation: Qt.Vertical
	preferredHighlightBegin: 0
	preferredHighlightEnd: height
	spacing: Tokens.spacing.small
	state: visibilities.launcher ? requestedState : displayState
	verticalLayoutDirection: ListView.BottomToTop

	CustomScrollBar.vertical: CustomScrollBar {
		flickable: root
	}
	add: Transition {
		enabled: !root.state

		Anim {
			from: 0
			property: "opacity"
			to: 1
			type: Anim.DefaultEffects
		}
	}
	addDisplaced: Transition {
		Anim {
			property: "y"
			type: Anim.StandardSmall
		}

		Anim {
			property: "opacity"
			to: 1
			type: Anim.DefaultEffects
		}
	}
	displaced: Transition {
		Anim {
			property: "y"
		}

		Anim {
			property: "opacity"
			to: 1
			type: Anim.DefaultEffects
		}
	}
	highlight: CustomRect {
		color: Colors.palette.m3onSurface
		implicitHeight: root.currentItem?.implicitHeight ?? 0
		implicitWidth: root.width
		opacity: 0.08
		radius: Tokens.rounding.medium
		y: root.currentItem?.y ?? 0

		Behavior on y {
			Anim {
				duration: Tokens.anim.durations.small
				easing: Tokens.anim.expressiveDefaultEffects
			}
		}
	}
	model: ScriptModel {
		values: root.resultsForText(root.displayText)

		onValuesChanged: root.currentIndex = 0
	}
	move: Transition {
		Anim {
			property: "y"
		}

		Anim {
			property: "opacity"
			to: 1
			type: Anim.DefaultEffects
		}
	}
	remove: Transition {
		enabled: !root.state

		Anim {
			from: 1
			property: "opacity"
			to: 0
			type: Anim.DefaultEffects
		}
	}
	states: [
		State {
			name: "apps"

			PropertyChanges {
				root.delegate: appItem
			}
		},
		State {
			name: "actions"

			PropertyChanges {
				root.delegate: actionItem
			}
		},
		State {
			name: "calc"

			PropertyChanges {
				root.delegate: calcItem
			}
		},
		State {
			name: "variant"

			PropertyChanges {
				root.delegate: variantItem
			}
		},
		State {
			name: "files"

			PropertyChanges {
				root.delegate: filesItem
			}
		}
	]
	transitions: Transition {
		SequentialAnimation {
			ParallelAnimation {
				Anim {
					duration: Tokens.anim.durations.small
					easing: Tokens.anim.standardAccel
					from: 1
					property: "opacity"
					target: root
					to: 0
				}

				Anim {
					duration: Tokens.anim.durations.small
					easing: Tokens.anim.standardAccel
					from: 1
					property: "scale"
					target: root
					to: 0.9
				}
			}

			PropertyAction {
				property: "delegate"
				target: root
				value: null
			}

			ScriptAction {
				script: root.displayText = root.search.text
			}

			PropertyAction {
				property: "delegate"
				target: root
			}

			ParallelAnimation {
				Anim {
					duration: Tokens.anim.durations.small
					easing: Tokens.anim.standardDecel
					from: 0
					property: "opacity"
					target: root
					to: 1
				}

				Anim {
					duration: Tokens.anim.durations.small
					easing: Tokens.anim.standardDecel
					from: 0.9
					property: "scale"
					target: root
					to: 1
				}
			}

			PropertyAction {
				property: "enabled"
				targets: [root.add, root.remove]
				value: true
			}
		}
	}

	Component.onCompleted: displayText = search.text

	Component {
		id: appItem

		AppItem {
			visibilities: root.visibilities
		}
	}

	Component {
		id: actionItem

		ActionItem {
			list: root
		}
	}

	Component {
		id: calcItem

		CalcItem {
			list: root
		}
	}

	Component {
		id: variantItem

		VariantItem {
			list: root
		}
	}

	Component {
		id: filesItem

		FilesItem {
			list: root
		}
	}

	Connections {
		function onTextChanged() {
			root.syncDisplayText();
		}

		target: root.search
	}

	Connections {
		function onLauncherChanged() {
			root.syncDisplayText();
		}

		target: root.visibilities
	}
}
