<script lang="ts">
    import {Breadcrumb, Dropzone, Modal, Progressbar} from 'flowbite-svelte';
    import {derived} from 'svelte/store';
    import {CloudUpload} from 'carbon-icons-svelte';
    import {api, changeStateFunction, rpc, state, StateE, token, type UploadFile, workingWrapperR} from '../store';
    import LinkButton from '../components/LinkButton.svelte';
    import DirViewer from '../components/DirViewer.svelte';
    import UploadModal from '../components/UploadModal.svelte';
    import FileViewer from '../components/FileViewer.svelte';

    interface Data {
        node: api.Node|null,
        segments: api.PathSegment[]
    }

    let data = derived<typeof state, Data>(
        state,
        ($state, set) => {
            (async () => {
                let node = await workingWrapperR<api.Node>(() => rpc.FS_get_node($token ?? '', $state.view_node));
                if (!node)
                    return $state.view_node = 0;
                let segments = await workingWrapperR<api.PathSegment[]>(() => rpc.FS_get_path($token ?? '', node!.id));
                if (!segments)
                    return $state.view_node = 0;
                set({node: node as Data['node'], segments });
            })();
        },
        { node: null, segments: [] }
    );

    const getFile = async (entry: FileSystemEntry) => new Promise<File>((o, e) => (entry as FileSystemFileEntry).file(o, e));

    async function handleEntry(parent: number, parent_name: string, entry: FileSystemEntry): Promise<UploadFile[]> {
        if (entry.isFile) {
            try {
                return [{
                    id: parent,
                    name: entry.name,
                    full_name: parent_name + entry.name,
                    file: await getFile(entry),
                    overwrite: false
                }];
            } catch (e) {
                return [];
            }
        }
        const resp = await workingWrapperR<api.CreateNodeInfo>(() => rpc.FS_create_node($token ?? '', false, parent, entry.name));
        if (!resp) return [];
        if (resp.file) return [];
        const reader = (entry as FileSystemDirectoryEntry).createReader();
        const files: UploadFile[] = [];
        const name = parent_name + entry.name + '/';
        while (true) {
            try {
                const entries: FileSystemEntry[] = await new Promise((o, e) => reader.readEntries(o, e));
                if (entries.length == 0) break;
                for (const e of entries)
                    files.push(...await handleEntry(resp.id, name, e));
            } catch (e) { break; }
        }
        return files;
    }

    async function onChange(e: Event) {
        const input = e.target as HTMLInputElement;
        if (!input.files)
            return
        console.log(input.files);
        const files: UploadFile[] = [];
        for (const f of input.files)
            files.push({
                id: $state.view_node,
                name: f.name,
                full_name: f.name,
                file: f,
                overwrite: false
            });
        await upload(files);
    }

    async function onDrop(e: DragEvent) {
        e.preventDefault();
        if (!e.dataTransfer)
            return;
        const files: UploadFile[] = [];
        for (const i of e.dataTransfer.items) {
            const entry = i.webkitGetAsEntry();
            if (!entry)
                console.error("Failed to get entry for: ", i);
            else
                files.push(...await handleEntry($state.view_node, '', entry));
        }
        await upload(files);
    }


    const upload_progress_data = { total: 0, current: 0 }
    let upload_progress: number;
    $: upload_progress = upload_progress_data.total == 0 ? 0 : (upload_progress_data.current / upload_progress_data.total * 100);
    let real_upload: (files: UploadFile[]) => Promise<void>;
    async function upload(files: UploadFile[]) {
        upload_progress_data.total = files.length;
        upload_progress_data.current = 0;
        const upload_files: UploadFile[] = [];
        for (const file of files) {
            const resp = await workingWrapperR<api.CreateNodeInfo>(() => rpc.FS_create_node($token ?? '', true, file.id, file.name));
            if (resp && resp.file)
                upload_files.push({ ...file, id: resp.id, overwrite: resp.exists });
            upload_progress_data.current++;
        }
        await real_upload(upload_files);
    }
</script>

<div class="w-full max-w-4xl">
    <div class="w-full flex mb-2 h-16">
        <Breadcrumb>
            {#each $data.segments as segment, i}
                {#if i > 0}<li class="inline-flex items-center">/</li>{/if}
                <li class="inline-flex items-center">
                    {#if segment.id !== null}
                        <LinkButton on:click={changeStateFunction(StateE.VIEW, segment.id)}>{segment.id === 0 ? 'Files' : segment.name}</LinkButton>
                    {:else}
                        <span style="padding: 0 0.25em;">{segment.name}</span>
                    {/if}
                </li>
            {/each}
        </Breadcrumb>
        <span class="flex-1"></span>
        {#if $data.node?.file === false}
            <Dropzone class="h-full w-64 cursor-pointer" on:drop={onDrop} on:dragover={e => e.preventDefault()} on:change={onChange} multiple>
                <span class="flex flex-row">
                    <CloudUpload class="fill-gray-500 h-12 cursor-pointer" width="40%"/>
                    <span class="inline text-gray-600 w-48 cursor-pointer">Click here or drag<br>to upload files</span>
                </span>
            </Dropzone>
        {/if}
    </div>
    {#if $data.node === null}
        <!-- Waiting for data -->
    {:else if $data.node.file}
        <FileViewer node={$data.node} />
    {:else}
        <DirViewer node={$data.node} />
    {/if}
</div>
<UploadModal bind:upload={real_upload}/>
{#if upload_progress_data.current !== upload_progress_data.total}
    <Modal open dismissable={false} title="Creating files">
        <div class="mb-1 flex justify-between">
            <span>{upload_progress_data.current} / {upload_progress_data.total}</span>
            <span>{upload_progress.toFixed(2)}%</span>
        </div>
        <Progressbar class="!mt-0" size="h-4" bind:progress={upload_progress} />
    </Modal>
{/if}