<script lang="ts">
    import { Label } from '$lib/components/ui/label/index.js';
    import { Button } from '$lib/components/ui/button';
    import { Slider } from '$lib/components/ui/slider';
    import { selection } from '$lib/components/file-list/Selection';
    import {
        ListItem,
        ListRootItem,
        ListTrackSegmentItem
    } from '$lib/components/file-list/FileList';
    import Help from '$lib/components/Help.svelte';
    import { Filter } from 'lucide-svelte';
    import { _ } from 'svelte-i18n';
    import WithUnits from '$lib/components/WithUnits.svelte';
    import { dbUtils, fileObservers } from '$lib/db';
    import { map } from '$lib/stores';
    import { onDestroy } from 'svelte';
    import { ramerDouglasPeucker, TrackPoint, type SimplifiedTrackPoint } from 'gpx';
    import { derived } from 'svelte/store';

    let sliderValue = [50];
    let maxPoints = 0;
    let currentPoints = 0;

    $: validSelection = $selection.hasAnyChildren(new ListRootItem(), true, ['waypoints']);

    $: tolerance = 2 ** (sliderValue[0] / (100 / Math.log2(10000)));

    let simplified = new Map<string, [ListItem, number, SimplifiedTrackPoint[]]>();
    let unsubscribes = new Map<string, () => void>();

    function update() {
        maxPoints = 0;
        currentPoints = 0;

        let data: GeoJSON.FeatureCollection = {
            type: 'FeatureCollection',
            features: []
        };

        simplified.forEach(([item, maxPts, points], itemFullId) => {
            maxPoints += maxPts;

            let current = points.filter(
                (point) => point.distance === undefined || point.distance >= tolerance
            );
            currentPoints += current.length;

            data.features.push({
                type: 'Feature',
                geometry: {
                    type: 'LineString',
                    coordinates: current.map((point) => [
                        point.point.getLongitude(),
                        point.point.getLatitude()
                    ])
                },
                properties: {}
            });
        });

        if ($map) {
            let source = $map.getSource('simplified');
            if (source) {
                source.setData(data);
            } else {
                $map.addSource('simplified', {
                    type: 'geojson',
                    data: data
                });
            }
            if (!$map.getLayer('simplified')) {
                $map.addLayer({
                    id: 'simplified',
                    type: 'line',
                    source: 'simplified',
                    paint: {
                        'line-color': 'white',
                        'line-width': 3
                    }
                });
            } else {
                $map.moveLayer('simplified');
            }
        }
    }

    $: if ($fileObservers) {
        unsubscribes.forEach((unsubscribe, fileId) => {
            if (!$fileObservers.has(fileId)) {
                unsubscribe();
                unsubscribes.delete(fileId);
            }
        });
        $fileObservers.forEach((fileStore, fileId) => {
            if (!unsubscribes.has(fileId)) {
                let unsubscribe = derived([fileStore, selection], ([fs, sel]) => [
                    fs,
                    sel
                ]).subscribe(([fs, sel]) => {
                    if (fs) {
                        fs.file.forEachSegment((segment, trackIndex, segmentIndex) => {
                            let segmentItem = new ListTrackSegmentItem(
                                fileId,
                                trackIndex,
                                segmentIndex
                            );
                            if (sel.hasAnyParent(segmentItem)) {
                                let statistics = fs.statistics.getStatisticsFor(segmentItem);
                                simplified.set(segmentItem.getFullId(), [
                                    segmentItem,
                                    statistics.local.points.length,
                                    ramerDouglasPeucker(statistics.local.points, 1)
                                ]);
                                update();
                            } else if (simplified.has(segmentItem.getFullId())) {
                                simplified.delete(segmentItem.getFullId());
                                update();
                            }
                        });
                    }
                });
                unsubscribes.set(fileId, unsubscribe);
            }
        });
    }

    $: if (tolerance) {
        update();
    }

    onDestroy(() => {
        if ($map) {
            if ($map.getLayer('simplified')) {
                $map.removeLayer('simplified');
            }
            if ($map.getSource('simplified')) {
                $map.removeSource('simplified');
            }
        }
        unsubscribes.forEach((unsubscribe) => unsubscribe());
        simplified.clear();
    });

    function reduce() {
        let itemsAndPoints = new Map<ListItem, TrackPoint[]>();
        simplified.forEach(([item, maxPts, points], itemFullId) => {
            itemsAndPoints.set(
                item,
                points
                    .filter((point) => point.distance === undefined || point.distance >= tolerance)
                    .map((point) => point.point)
            );
        });
        dbUtils.reduce(itemsAndPoints);
    }
</script>

<div class="flex flex-col gap-3 w-full max-w-80 {$$props.class ?? ''}">
    <div class="p-2">
        <Slider bind:value={sliderValue} min={0} max={100} step={1} />
    </div>
    <Label class="flex flex-row justify-between">
        <span>{$_('toolbar.reduce.tolerance')}</span>
        <WithUnits value={tolerance / 1000} type="distance" decimals={3} class="font-normal" />
    </Label>
    <Label class="flex flex-row justify-between">
        <span>{$_('toolbar.reduce.number_of_points')}</span>
        <span class="font-normal">{currentPoints}/{maxPoints}</span>
    </Label>
    <Button variant="outline" disabled={!validSelection} on:click={reduce}>
        <Filter size="16" class="mr-1" />
        {$_('toolbar.reduce.button')}
    </Button>

    <Help link="./help/toolbar/minify">
        {#if validSelection}
            {$_('toolbar.reduce.help')}
        {:else}
            {$_('toolbar.reduce.help_no_selection')}
        {/if}
    </Help>
</div>
