<script lang="ts" module>
	export interface PipsProps {
		min?: number;
		max?: number;
		step?: number;
		values?: number[];
		vertical?: boolean;
		reversed?: boolean;
		hoverable?: boolean;
		disabled?: boolean;
		pipstep?: number;
		prefix?: string;
		suffix?: string;
		focus?: boolean;
		range?: undefined | boolean | 'min' | 'max';
		all?: undefined | boolean | 'pip' | 'label';
		first?: boolean | 'pip' | 'label';
		last?: boolean | 'pip' | 'label';
		rest?: boolean | 'pip' | 'label';
		percentOf: (v: number) => number;
		fixFloat: (v: number) => number;
		orientationStart?: 'top' | 'bottom' | 'left' | 'right';
		orientationEnd?: 'top' | 'bottom' | 'left' | 'right';
		formatter?: (v: number, i: number, p: number) => string;
		moveHandle: undefined | ((index: number | undefined, value: number) => number);
		normalisedClient: (e: MouseEvent | TouchEvent) => { x: number; y: number };
	}
</script>

<script lang="ts">
	let {
		range = false,
		min = 0,
		max = 100,
		step = 1,
		values = [(max + min) / 2],
		vertical = false,
		reversed = false,
		hoverable = true,
		disabled = false,
		pipstep,
		all = true,
		first,
		last,
		rest,
		prefix = '',
		suffix = '',
		focus,
		orientationStart,
		// eslint-disable-next-line no-unused-vars
		formatter = (v, i, p) => v.toString(),
		percentOf,
		moveHandle,
		fixFloat,
		normalisedClient
	}: PipsProps = $props();

	let clientStart = $state({ x: 0, y: 0 });
	let pipStep = $derived(
		pipstep ||
			((max - min) / step >= (vertical ? 50 : 100) ? (max - min) / (vertical ? 10 : 20) : 1)
	);
	let pipCount = $derived(parseInt(((max - min) / (step * pipStep)).toString(), 10));
	let pipVal = $derived((val: number) => fixFloat(min + val * step * pipStep));
	let isSelected = $derived((val: number) => values.some((v) => fixFloat(v) === fixFloat(val)));
	let inRange = $derived((val: number) => {
		if (range === 'min') {
			return values[0] > val;
		}
		if (range === 'max') {
			return values[0] < val;
		}
		if (range) {
			return values[0] < val && values[1] > val;
		}
	});

	/**
	 * function to run when the user clicks on a label
	 * we store the original client position so we can check if the user has moved the mouse/finger
	 * @param {MouseEvent} e the event from browser
	 **/
	const labelDown = (e: MouseEvent) => {
		clientStart = { x: e.clientX, y: e.clientY };
	};

	/**
	 * function to run when the user releases the mouse/finger
	 * we check if the user has moved the mouse/finger, if not we "click" the label
	 * and move the handle it to the label position
	 * @param {number} val the value of the label
	 * @param {MouseEvent|TouchEvent} e the event from browser
	 */
	function labelUp(val: number, e: MouseEvent | TouchEvent) {
		if (disabled) {
			return;
		}

		const clientPos = normalisedClient(e);
		const distanceMoved = Math.sqrt(
			Math.pow(clientStart.x - clientPos.x, 2) + Math.pow(clientStart.y - clientPos.y, 2)
		);

		if (clientStart && distanceMoved <= 5) {
			moveHandle?.(undefined, val);
		}
	}
</script>

<div
	class="rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6"
	class:disabled
	class:hoverable
	class:vertical
	class:reversed
	class:focus
>
	{#if (all && first !== false) || first}
		<span
			class="pip-680f0f01-664b-43b5-9e1c-789449c63c62 first"
			class:selected={isSelected(min)}
			class:in-range={inRange(min)}
			style="{orientationStart}: 0%;"
			onpointerdown={labelDown}
			onpointerup={(e) => labelUp(min, e)}
		>
			{#if all === 'label' || first === 'label'}
				<span class="pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b">
					{prefix}{formatter(fixFloat(min), 0, 0)}{suffix}
				</span>
			{/if}
		</span>
	{/if}

	{#if (all && rest !== false) || rest}
		<!-- eslint-disable-next-line no-unused-vars -->
		{#each Array(pipCount + 1) as _, i}
			{#if pipVal(i) !== min && pipVal(i) !== max}
				<span
					class="pip-680f0f01-664b-43b5-9e1c-789449c63c62"
					class:selected={isSelected(pipVal(i))}
					class:in-range={inRange(pipVal(i))}
					style="{orientationStart}: {percentOf(pipVal(i))}%;"
					onpointerdown={labelDown}
					onpointerup={(e) => labelUp(pipVal(i), e)}
				>
					{#if all === 'label' || rest === 'label'}
						<span class="pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b">
							{prefix}{formatter(pipVal(i), i, percentOf(pipVal(i)))}{suffix}
						</span>
					{/if}
				</span>
			{/if}
		{/each}
	{/if}

	{#if (all && last !== false) || last}
		<span
			class="pip last"
			class:selected={isSelected(max)}
			class:in-range={inRange(max)}
			style="{orientationStart}: 100%;"
			onpointerdown={labelDown}
			onpointerup={(e) => labelUp(max, e)}
		>
			{#if all === 'label' || last === 'label'}
				<span class="pipVal">
					{prefix}{formatter(fixFloat(max), pipCount, 100)}{suffix}
				</span>
			{/if}
		</span>
	{/if}
</div>

<style>
	:global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28) {
		--pip: var(--range-pip, lightslategray);
		--pip-text: var(--range-pip-text, var(--pip));
		--pip-active: var(--range-pip-active, darkslategrey);
		--pip-active-text: var(--range-pip-active-text, var(--pip-active));
		--pip-hover: var(--range-pip-hover, darkslategrey);
		--pip-hover-text: var(--range-pip-hover-text, var(--pip-hover));
		--pip-in-range: var(--range-pip-in-range, var(--pip-active));
		--pip-in-range-text: var(--range-pip-in-range-text, var(--pip-active-text));
	}
	:global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6) {
		position: absolute;
		height: 1em;
		left: 0;
		right: 0;
		bottom: -1em;
	}
	:global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical) {
		height: auto;
		width: 1em;
		left: 100%;
		right: auto;
		top: 0;
		bottom: 0;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip-680f0f01-664b-43b5-9e1c-789449c63c62
	) {
		height: 0.4em;
		position: absolute;
		top: 0.25em;
		width: 1px;
		white-space: nowrap;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical
			.pip-680f0f01-664b-43b5-9e1c-789449c63c62
	) {
		height: 1px;
		width: 0.4em;
		left: 0.25em;
		top: auto;
		bottom: auto;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
	) {
		position: absolute;
		top: 0.4em;
		transform: translate(-50%, 25%);
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical
			.pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
	) {
		position: absolute;
		top: 0;
		left: 0.4em;
		transform: translate(25%, -50%);
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip-680f0f01-664b-43b5-9e1c-789449c63c62
	) {
		transition: all 0.15s ease;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
	) {
		transition:
			all 0.15s ease,
			font-weight 0s linear;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip-680f0f01-664b-43b5-9e1c-789449c63c62
	) {
		color: var(--pip-text, lightslategray);
		background-color: var(--pip, lightslategray);
	}
	:global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip.selected) {
		color: var(--pip-active-text, darkslategrey);
		background-color: var(--pip-active, darkslategrey);
	}
	:global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.hoverable:not(.disabled) .pip:hover) {
		color: var(--pip-hover-text, darkslategrey);
		background-color: var(--pip-hover, darkslategrey);
	}
	:global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip.in-range) {
		color: var(--pip-in-range-text, darkslategrey);
		background-color: var(--pip-in-range, darkslategrey);
	}
	:global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip.selected) {
		height: 0.75em;
	}
	:global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical .pip.selected) {
		height: 1px;
		width: 0.75em;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6
			.pip.selected
			.pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
	) {
		font-weight: bold;
		top: 0.75em;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical
			.pip.selected
			.pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
	) {
		top: 0;
		left: 0.75em;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.hoverable:not(.disabled)
			.pip:not(.selected):hover
	) {
		transition: none;
	}
	:global(
		.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.hoverable:not(.disabled)
			.pip:not(.selected):hover
			.pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
	) {
		transition: none;
		font-weight: bold;
	}
</style>
