<script lang="ts">
    import type {ActionReturn} from 'svelte/action';
    import type * as Leaflet from 'leaflet';
    import {type Control, type Map as LeafletMap, type TileLayer} from 'leaflet';
    import {tilerLayerAttribution, tilerLayerUrl} from '$lib/map/layer';
    import {untrack} from "svelte";
    import {SvelteDate} from "svelte/reactivity";

    interface Props {
		map: LeafletMap | null;
		selected: boolean;
		baseLayer: 'osm';
		dataLayer: 'noaa_mrms_merged_composite_reflectivity_qc' | null;
		overlayLayers: string[];
        reload: boolean;
	}
	let { map = $bindable(null), selected, baseLayer, dataLayer, reload = $bindable(false) }: Props = $props();

	let mapContainerElement: HTMLElement;
	// await import('leaflet') done at runtime
	let L: Leaflet | null = $state(null);
	// Base layer - openstreetmap, carto, etc
	let layer0: TileLayer;
	// Data layer - composite reflectivity, velocity (the actual data)
	let layer1: TileLayer;
    let attrControl: Control.Attribution | null = $state(null);
    let dataValidity: SvelteDate | null = $state(null);

    // redrawing
    $effect(() => {
        if (!L) return;
        if (!map) return;

        if (reload == true) {
            untrack(() => {reload = false});
            if (!dataLayer) return;

            if (layer1) {
                layer1.setUrl(tilerLayerUrl(dataLayer) + "?r=" + Math.random())
                layer1.redraw();
            }

            if (dataLayer) {
                fetch(tilerLayerUrl(dataLayer).replace("{z}", "0").replace("{x}", "0").replace("{y}", "0")).then((r) => {
                    let headerval = r.headers.get("x-wxbox-tiler-data-valid-time");
                    if (!headerval) {
                        return;
                    }
                    let valid_from = Number.parseInt(headerval);
                    dataValidity = new SvelteDate(valid_from * 1000);
                })
            }
        }
    })

	// Layer0 (base) updating
	$effect(() => {
		// if leaflet hasn't been imported yet, skip
		// this also sets a dependency, so we'll be re-ran once it has
		if (!L) return;
		if (!map) return;

		// if there is already a layer0 and a map, remove the old one
		if (layer0 && map) {
			layer0.removeFrom(map);
		}

		// OpenStreetMap
		if (baseLayer === 'osm') {
			layer0 = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
				attribution:
					'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
			});
			layer0.addTo(map);
		}
	});

	// Layer1 (data) updating
	$effect(() => {
		if (!L) return;
		if (!map) return;

		// remove existing layer1, if there is one
		if (layer1 && map) {
			layer1.removeFrom(map);
		}

		if (dataLayer) {
			layer1 = L.tileLayer(tilerLayerUrl(dataLayer), {
				attribution: tilerLayerAttribution(dataLayer)
			});
			layer1.addTo(map);
            // fetch the data validity
            fetch(tilerLayerUrl(dataLayer).replace("{z}", "0").replace("{x}", "0").replace("{y}", "0")).then((r) => {
                let headerval = r.headers.get("x-wxbox-tiler-data-valid-time");
                if (!headerval) {
                    return;
                }
                let valid_from = Number.parseInt(headerval);
                dataValidity = new SvelteDate(valid_from * 1000);
            });
		} else {
            dataValidity = null;
        }
	});

    const ukrainianFlag = '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8" class="leaflet-attribution-flag"><path fill="#4C7BE1" d="M0 0h12v4H0z"/><path fill="#FFD500" d="M0 4h12v3H0z"/><path fill="#E0BC00" d="M0 7h12v1H0z"/></svg>';

    // Attribution control & data validity
    $effect(() => {
        if (!L) return;
        if (!map) return;

        let basePrefix = `<a href="https://leafletjs.com" title="A JavaScript library for interactive maps">${ukrainianFlag}Leaflet</a>`;

        if (!attrControl) {
            attrControl = L.control.attribution({
                prefix: basePrefix
            });
        }
        if (!attrControl) return;
        attrControl.addTo(map);

        let prefix = basePrefix;

        if (!dataValidity) {
            prefix += " | Data validity unknown";
        } else {
            prefix += ` | Data valid from ${dataValidity.toLocaleTimeString('en-US')}`
        }

        if (!attrControl) {
            return;
        }

        attrControl.setPrefix(prefix);
    })

	// ran when the div below (see use:mapAction) is created
	async function mapAction(): Promise<ActionReturn> {
		// dynamically imports leaflet, as it's a browser lib
		L = await import('leaflet');
		// imports leaflet.sync, for syncing
		await import('leaflet.sync');

		// create the map
		map = L.map(mapContainerElement, {
			// geo center of CONUS
			center: [39.83, -98.583],
			zoom: 5,
            attributionControl: false
		});

		if (!map) return {};
	}
</script>

<div class="map" class:mapselected={selected} bind:this={mapContainerElement} use:mapAction></div>

<style>
	.map {
		flex: 1;
		box-sizing: border-box;
		border: 3px solid #000;
	}
	.mapselected {
		box-sizing: border-box;
		border: 3px solid green;
	}
</style>
