<script lang="ts">
	import type { FileNode } from "./types";
	import { createEventDispatcher } from "svelte";

	import Arrow from "./ArrowIcon.svelte";
	import Checkbox from "./Checkbox.svelte";
	import FileIcon from "../icons/light-file.svg";
	import FolderIcon from "../icons/light-folder.svg";

	export let path: string[] = [];
	export let selected_files: string[][] = [];
	export let selected_folders: string[][] = [];
	export let is_selected_entirely = false;
	export let interactive: boolean;
	export let ls_fn: (path: string[]) => Promise<FileNode[]>;
	export let file_count: "single" | "multiple" = "multiple";
	export let valid_for_selection: boolean;

	let content: FileNode[] = [];
	let opened_folders: number[] = [];

	const toggle_open_folder = (i: number): void => {
		if (opened_folders.includes(i)) {
			opened_folders = opened_folders.filter((x) => x !== i);
		} else {
			opened_folders = [...opened_folders, i];
		}
	};

	const open_folder = (i: number): void => {
		if (!opened_folders.includes(i)) {
			opened_folders = [...opened_folders, i];
		}
	};

	(async () => {
		content = await ls_fn(path);
		if (valid_for_selection) {
			content = [{ name: ".", type: "file" }, ...content];
		}
		opened_folders = content
			.map((x, i) =>
				x.type === "folder" &&
				(is_selected_entirely || selected_files.some((y) => y[0] === x.name))
					? i
					: null
			)
			.filter((x): x is number => x !== null);
	})();

	$: if (is_selected_entirely) {
		content.forEach((x) => {
			dispatch("check", {
				path: [...path, x.name],
				checked: true,
				type: x.type
			});
		});
	}

	const dispatch = createEventDispatcher<{
		check: { path: string[]; checked: boolean; type: "file" | "folder" };
	}>();
</script>

<ul>
	{#each content as { type, name, valid }, i}
		<li>
			<span class="wrap">
				<Checkbox
					disabled={!interactive ||
						(type === "folder" && file_count === "single")}
					value={(type === "file" ? selected_files : selected_folders).some(
						(x) => x[0] === name && x.length === 1
					)}
					on:change={(e) => {
						let checked = e.detail;
						dispatch("check", {
							path: [...path, name],
							checked,
							type
						});
						if (type === "folder" && checked) {
							open_folder(i);
						}
					}}
				/>

				{#if type === "folder"}
					<span
						class="icon"
						class:hidden={!opened_folders.includes(i)}
						on:click|stopPropagation={() => toggle_open_folder(i)}
						role="button"
						aria-label="expand directory"
						tabindex="0"
						on:keydown={({ key }) => {
							if (key === " " || key === "Enter") {
								toggle_open_folder(i);
							}
						}}><Arrow /></span
					>
				{:else}
					<span class="file-icon">
						<img src={name === "." ? FolderIcon : FileIcon} alt="file icon" />
					</span>
				{/if}
				{name}
			</span>
			{#if type === "folder" && opened_folders.includes(i)}
				<svelte:self
					path={[...path, name]}
					selected_files={selected_files
						.filter((x) => x[0] === name)
						.map((x) => x.slice(1))}
					selected_folders={selected_folders
						.filter((x) => x[0] === name)
						.map((x) => x.slice(1))}
					is_selected_entirely={selected_folders.some(
						(x) => x[0] === name && x.length === 1
					)}
					{interactive}
					{ls_fn}
					{file_count}
					valid_for_selection={valid}
					on:check
				/>
			{/if}
		</li>
	{/each}
</ul>

<style>
	.icon {
		display: inline-block;
		width: 18px;
		height: 18px;
		padding: 3px 2px 3px 3px;
		margin: 0;
		flex-grow: 0;
		display: inline-flex;
		justify-content: center;
		align-items: center;
		border-radius: 2px;
		cursor: pointer;
		transition: 0.1s;
		flex-shrink: 0;
	}

	.file-icon {
		display: inline-block;
		height: 20px;
		margin-left: -1px;
		margin: 0;
		flex-grow: 0;
		display: inline-flex;
		justify-content: center;
		align-items: center;

		transition: 0.1s;
	}

	.file-icon img {
		width: 100%;
		height: 100%;
	}

	.icon:hover {
		background: #eee;
	}

	.icon:hover :global(> *) {
		color: var(--block-info-text-color);
	}

	.icon :global(> *) {
		transform: rotate(90deg);
		transform-origin: 40% 50%;
		transition: 0.2s;
		color: var(--color-accent);
	}

	.hidden :global(> *) {
		transform: rotate(0);
		color: var(--body-text-color-subdued);
	}

	ul {
		margin-left: 26px;
		padding-left: 0;
		list-style: none;
	}

	li {
		margin-left: 0;
		padding-left: 0;
		align-items: center;
		margin: 8px 0;
		font-family: var(--font-mono);
		font-size: var(--scale-00);
		overflow-wrap: anywhere;
		word-break: break-word;
	}

	.wrap {
		display: flex;
		gap: 8px;
		align-items: center;
	}
</style>
