pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Templates
import qs.Helpers
import ZShell.Config

ScrollBar {
	id: root

	required property Flickable flickable
	readonly property bool isHorizontal: flickable && flickable.ScrollBar.horizontal === root
	readonly property real axisSize: isHorizontal ? flickable.width : flickable.height
	readonly property real axisContentSize: isHorizontal ? flickable.contentWidth : flickable.contentHeight
	readonly property real axisContentPos: isHorizontal ? (reversed ? flickable.contentX : (flickable.contentX - flickable.originX)) : (reversed ? flickable.contentY : (flickable.contentY - flickable.originY))
	readonly property real axisLength: isHorizontal ? root.width : root.height
	readonly property real effectiveSize: Math.max(nonAnimHeight, root.minimumSize)
	readonly property real effectiveTravel: Math.max(0, 1 - root.effectiveSize)
	readonly property real nonAnimHeight: root.axisSize / root.axisContentSize
	readonly property real nonAnimY: root.axisContentPos / root.axisContentSize
	readonly property real rawTravel: Math.max(0, 1 - root.nonAnimHeight)
	readonly property bool reversed: isHorizontal ? (flickable instanceof ListView && flickable.layoutDirection === Qt.RightToLeft) : (flickable instanceof ListView && flickable.verticalLayoutDirection === ListView.BottomToTop)
	property bool shouldBeActive
	readonly property real travelScale: root.rawTravel > 0 ? root.effectiveTravel / root.rawTravel : 0

	enabled: !Visibilities.getForActive().isDrawing
	implicitWidth: size === 1 ? 0 : isHorizontal ? 0 : Tokens.padding.extraSmall * 2
	implicitHeight: size === 1 ? 0 : isHorizontal ? Tokens.padding.extraSmall * 2 : 0

	contentItem: Item {}
	Behavior on position {
		enabled: !fullMouse.pressed

		Anim {}
	}

	onHoveredChanged: {
		if (hovered)
			shouldBeActive = hovered;
		else
			hideDelay.restart();
	}

	Connections {
		function onMovingChanged() {
			if (root.flickable.moving)
				root.shouldBeActive = true;
			else
				hideDelay.restart();
		}

		target: root.flickable
	}

	Loader {
		anchors.fill: parent
		active: root.size < 1
		sourceComponent: root.isHorizontal ? horizontalTrack : verticalTrack
	}

	Component {
		id: verticalTrack

		VerticalScrollBarTrack {
			scrollBar: root
			mouseArea: fullMouse
		}
	}

	Component {
		id: horizontalTrack

		HorizontalScrollBarTrack {
			scrollBar: root
			mouseArea: fullMouse
		}
	}

	Timer {
		id: hideDelay

		interval: 600

		onTriggered: root.shouldBeActive = root.flickable.moving || root.hovered
	}

	CustomMouseArea {
		id: fullMouse

		property real pressOffset: 0

		function contentPosFromThumbStart(thumbStart) {
			var visualPos = root.effectiveTravel > 0 ? thumbStart / root.travelScale : 0;
			return root.reversed ? (visualPos - 1) * root.axisContentSize : visualPos * root.axisContentSize;
		}

		function updateFromEvent(event) {
			var eventPos = root.isHorizontal ? event.x : event.y;
			var posInTrack = eventPos / root.axisLength;
			var thumbStart = posInTrack - pressOffset;
			thumbStart = Math.max(0, Math.min(root.effectiveTravel, thumbStart));

			var newPos = contentPosFromThumbStart(thumbStart);
			if (root.isHorizontal)
				root.flickable.contentX = newPos + (root.flickable.originX + (root.reversed ? root.flickable.contentWidth : 0));
			else
				root.flickable.contentY = newPos + (root.flickable.originY + (root.reversed ? root.flickable.contentHeight : 0));
		}

		function visualThumbStart() {
			const visualPos = root.reversed ? (1 + root.nonAnimY) : root.nonAnimY;
			return visualPos * root.travelScale;
		}

		function onWheel(event: WheelEvent): void {
			if (root.horizontal) {
				event.accepted = false;
				return;
			}

			var delta = event.angleDelta.y > 0 ? -0.1 : 0.1;
			var newPos = Math.max(0, Math.min(1 - root.size, root.position + delta));
			root.position = newPos;
		}

		anchors.fill: parent
		cursorShape: undefined
		hoverEnabled: true
		preventStealing: true

		onPositionChanged: event => {
			if (pressed)
				updateFromEvent(event);
		}
		onPressed: event => {
			var currentStart = visualThumbStart();
			var currentEnd = currentStart + root.effectiveSize;
			var eventPos = root.isHorizontal ? event.x : event.y;
			var clickPos = eventPos / root.axisLength;

			var clickedInsideThumb = clickPos >= currentStart && clickPos <= currentEnd;
			pressOffset = clickedInsideThumb ? (clickPos - currentStart) : root.effectiveSize / 2;

			updateFromEvent(event);
		}
	}
}
