pragma ComponentBehavior: Bound

import ZShell
import Quickshell
import Quickshell.Wayland
import QtQuick
import QtQuick.Effects
import qs.Components
import ZShell.Config
import qs.Helpers.Picker
import qs.Paths
import qs.Services

Item {
	id: root

	property list<var> clients: {
		const mon = Hypr.monitorFor(screen);
		if (!mon)
			return [];

		const special = mon.lastIpcObject.specialWorkspace;
		const wsId = special.name ? special.id : mon.activeWorkspace.id;

		return Hypr.toplevels.values.filter(c => c.workspace?.id === wsId).sort((a, b) => {
			const ac = a.lastIpcObject;
			const bc = b.lastIpcObject;
			return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating);
		});
	}
	readonly property int cornerRadius: Hypr.options.decoration.rounding
	property real ex: screen.width
	property real ey: screen.height
	property color inkColor: "#ff3b30"
	required property LazyLoader loader
	property string mode: "select"
	property bool onClient
	property real realBorderWidth: 2
	property real realRounding: onClient ? (Hypr.options.decoration.rounding ?? 0) : 0
	property real rsx: Math.min(sx, ex)
	property real rsy: Math.min(sy, ey)
	readonly property real scaleRatio: Hypr.monitorFor(screen).scale
	required property ShellScreen screen
	property real sh: Math.abs(sy - ey)
	readonly property color shadowColor: Hypr.options.decoration.shadow.color
	readonly property bool shadowEnabled: Hypr.options.decoration.shadow.enabled
	readonly property var shadowOffset: Hypr.options.decoration.shadow.offset
	readonly property int shadowRange: Hypr.options.decoration.shadow.range
	readonly property int shadowRenderPower: Hypr.options.decoration.shadow.render_power
	property real ssx
	property real ssy
	property real sw: Math.abs(sx - ex)
	property real sx: 0
	property real sy: 0
	property string tool: "move"

	function cancel(): void {
		annotations.clearAll();
		closeAnim.start();
	}

	function checkClientRects(x: real, y: real): void {
		for (const client of clients) {
			if (!client)
				continue;

			let {
				at: [cx, cy],
				size: [cw, ch]
			} = client.lastIpcObject;
			cx -= screen.x;
			cy -= screen.y;
			if (cx <= x && cy <= y && cx + cw >= x && cy + ch >= y) {
				onClient = true;
				sx = cx;
				sy = cy;
				ex = cx + cw;
				ey = cy + ch;
				break;
			}
		}
	}

	function enterEditMode(): void {
		mode = "edit";
		tool = "move";
	}

	function save(copyToClipboard: bool): void {
		const rounding = root.cornerRadius > 0;
		const shadow_blur = root.shadowRange / root.shadowRenderPower;
		const r = Math.floor(root.shadowColor.r * 256);
		const g = Math.floor(root.shadowColor.g * 256);
		const b = Math.floor(root.shadowColor.b * 256);
		const a = Math.floor(root.shadowColor.a * 256);
		const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`);
		const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : [];
		const postCmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : null;

		overlay.visible = border.visible = false;
		handles.visible = false;
		toolbar.visible = false;

		ZShellIo.saveItem(captureLayer, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => {
			const finalize = finalPath => {
				if (copyToClipboard) {
					Quickshell.execDetached(["sh", "-c", `wl-copy --type image/png < '${finalPath.replace(/'/g, `'\\''`)}'`]);
				}
				if (!copyToClipboard || Config.screenshot.saveOnCopy) {
					const dest = `${Paths.screenshots}/zshell-${Date.now()}.png`;
					Quickshell.execDetached(["sh", "-c", `mkdir -p "$(dirname '${dest}')" && cp '${path.replace(/'/g, `'\\''`)}' '${dest}'`]);
				}
			};

			if (postCmd) {
				Quickshell.execDetached(postCmd.concat([path]));
				finalize(path);
			} else {
				finalize(path);
			}
		});
		closeAnim.start();
	}

	anchors.fill: parent
	focus: true
	opacity: 0

	Behavior on rsx {
		enabled: !selectArea.pressed && root.mode === "select"

		ExAnim {}
	}
	Behavior on rsy {
		enabled: !selectArea.pressed && root.mode === "select"

		ExAnim {}
	}
	Behavior on sh {
		enabled: !selectArea.pressed && root.mode === "select"

		ExAnim {}
	}
	Behavior on sw {
		enabled: !selectArea.pressed && root.mode === "select"

		ExAnim {}
	}
	Behavior on opacity {
		Anim {
			duration: 300
		}
	}

	Component.onCompleted: {
		Hypr.extras.refreshOptions();
		screencopy.active = true;

		opacity = 1;

		const c = clients[0];
		if (c) {
			const cx = c.lastIpcObject.at[0] - screen.x;
			const cy = c.lastIpcObject.at[1] - screen.y;
			onClient = true;
			sx = cx;
			sy = cy;
			ex = cx + c.lastIpcObject.size[0];
			ey = cy + c.lastIpcObject.size[1];
		} else {
			sx = screen.width / 2 - 100;
			sy = screen.height / 2 - 100;
			ex = screen.width / 2 + 100;
			ey = screen.height / 2 + 100;
		}
	}
	Keys.onEscapePressed: root.mode === "edit" ? cancel() : closeAnim.start()
	Keys.onPressed: e => {
		if ((e.key == Qt.Key_C) && (e.modifiers & Qt.ControlModifier)) {
			save(true);
			e.accepted = true;
		} else if ((e.key == Qt.Key_S) && (e.modifiers & Qt.ControlModifier)) {
			save(false);
			e.accepted = true;
		}
	}
	onClientsChanged: {
		if (root.mode === "select")
			checkClientRects(selectArea.mouseX, selectArea.mouseY);
	}

	MouseArea {
		id: selectArea

		anchors.fill: parent
		cursorShape: Qt.CrossCursor
		enabled: root.mode === "select"
		hoverEnabled: true
		visible: root.mode === "select"

		onPositionChanged: event => {
			const x = event.x;
			const y = event.y;

			if (pressed) {
				root.onClient = false;
				root.sx = root.ssx;
				root.sy = root.ssy;
				root.ex = x;
				root.ey = y;
			} else {
				root.checkClientRects(x, y);
			}
		}
		onPressed: event => {
			root.ssx = event.x;
			root.ssy = event.y;
		}
		onReleased: root.enterEditMode()
	}

	SequentialAnimation {
		id: closeAnim

		PropertyAction {
			property: "closing"
			target: root.loader
			value: true
		}

		ParallelAnimation {
			Anim {
				duration: 300
				property: "opacity"
				target: root
				to: 0
			}

			ExAnim {
				properties: "rsx,rsy"
				target: root
				to: 0
			}

			ExAnim {
				property: "sw"
				target: root
				to: root.screen.width
			}

			ExAnim {
				property: "sh"
				target: root
				to: root.screen.height
			}
		}

		PropertyAction {
			property: "activeAsync"
			target: root.loader
			value: false
		}
	}

	Item {
		id: captureLayer

		anchors.fill: parent

		Loader {
			id: screencopy

			active: false
			anchors.fill: parent
			asynchronous: true

			sourceComponent: ScreencopyView {
				captureSource: root.screen
				paintCursor: false
			}
		}

		AnnotationCanvas {
			id: annotations

			anchors.fill: parent
			inkColor: root.inkColor
			layer.enabled: true
			tool: root.mode === "edit" && root.tool !== "move" ? root.tool : "none"

			layer.effect: MultiEffect {
				maskEnabled: true
				maskSource: selectionWrapper
				maskSpreadAtMin: 1
				maskThresholdMin: 0.5
			}
		}
	}

	Rectangle {
		id: overlay

		anchors.fill: parent
		color: "white"
		layer.enabled: true
		opacity: 0.3
		radius: root.realRounding
		visible: screencopy.item?.hasContent ?? false

		layer.effect: MultiEffect {
			maskEnabled: true
			maskInverted: true
			maskSource: selectionWrapper
			maskSpreadAtMin: 1
			maskThresholdMin: 0.5
		}
	}

	Item {
		id: selectionWrapper

		anchors.fill: parent
		layer.enabled: true
		visible: false

		Rectangle {
			id: selectionRect

			implicitHeight: root.sh
			implicitWidth: root.sw
			radius: root.realRounding
			x: root.rsx
			y: root.rsy
		}
	}

	Rectangle {
		id: border

		border.color: Colors.palette.m3primary
		border.width: root.realBorderWidth
		color: "transparent"
		implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
		implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
		radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
		visible: screencopy.item?.hasContent ?? false
		x: selectionRect.x - root.realBorderWidth
		y: selectionRect.y - root.realBorderWidth

		Behavior on border.color {
			Anim {}
		}
	}

	SelectionHandles {
		id: handles

		selH: root.sh
		selW: root.sw
		selX: root.rsx
		selY: root.rsy
		visible: root.mode === "edit" && root.tool === "move"

		onBoundsChanged: (x, y, w, h) => {
			root.sx = x;
			root.sy = y;
			root.ex = x + w;
			root.ey = y + h;
		}
		onMoveBy: (dx, dy) => {
			const w = root.sw, h = root.sh;
			let nx = root.rsx + dx, ny = root.rsy + dy;
			nx = Math.max(0, Math.min(nx, root.screen.width - w));
			ny = Math.max(0, Math.min(ny, root.screen.height - h));
			root.sx = nx;
			root.sy = ny;
			root.ex = nx + w;
			root.ey = ny + h;
		}
	}

	AnnotationToolbar {
		id: toolbar

		inkColor: root.inkColor
		tool: root.tool
		visible: root.mode === "edit"
		x: Math.max(8, Math.min(root.rsx + root.sw / 2 - width / 2, root.screen.width - width - 8))
		y: (root.rsy + root.sh + height + Tokens.padding.small <= root.screen.height) ? root.rsy + root.sh + Tokens.padding.small : (root.rsy - height - Tokens.padding.small <= 0) ? root.rsy + root.sh - height - Tokens.padding.small : root.rsy - height - Tokens.padding.small

		onCancelRequested: root.cancel()
		onClearRequested: annotations.clearAll()
		onColorSelected: c => root.inkColor = c
		onCopyRequested: root.save(true)
		onSaveRequested: root.save(false)
		onToolSelected: t => root.tool = t
		onUndoRequested: annotations.undo()
	}

	component ExAnim: Anim {}
}
