<script lang="ts">
    import { onMount, createEventDispatcher } from "svelte";
    import * as L from "leaflet";
    import type { Map as LeafletMap, LayerGroup } from "leaflet";
    import "leaflet/dist/leaflet.css";
    import WindVisualization from './WindVisualisation.svelte';
    import { distHaversine } from "../lib/mathutil.ts";
    import type { PredictionData, TelemetryData } from "../lib/types.ts";



    /**
     * @type {'prediction' | 'telemetry'}
     */
    export let mode: "prediction" | "telemetry" = "prediction";
    export let data: PredictionData | TelemetryData | null = null;

    let map: typeof LeafletMap | undefined;
    let mapContainer: HTMLDivElement;
    let plotLayerGroup: typeof LayerGroup;
    let mouseLat = 0;
    let mouseLng = 0;
    let isSelecting = false;

    let windData;

    const dispatch = createEventDispatcher<{ coordinatesSelected: { lat: number; lng: number } }>();

    onMount(async () => {
        if (!mapContainer) return;

        map = L.map(mapContainer).setView([51.505, -0.09], 13);
        plotLayerGroup = L.layerGroup().addTo(map);

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

        const response = await fetch("src/routes/testVelo.json");
        windData = await response.json();

        map.on("mousemove", (e: any) => {
            mouseLat = e.latlng.lat;
            mouseLng = e.latlng.lng;
        });

        map.on("click", (e: any) => {
            if (isSelecting) {
                dispatch("coordinatesSelected", { lat: e.latlng.lat, lng: e.latlng.lng });
                stopSelection();
            }
        });
    });

    $: if (map && data) {
        plotData(data);
    } else if (map) {
        clearMapLayers();
    }

    export const startSelection = () => {
        isSelecting = true;
        if (mapContainer) mapContainer.style.cursor = "crosshair";
    };

    export const stopSelection = () => {
        isSelecting = false;
        if (mapContainer) mapContainer.style.cursor = "";
    };

    export const plotData = (plotData: PredictionData | TelemetryData) => {
        if (mode === "prediction") {
            plotPrediction(plotData as PredictionData);
        } else if (mode === "telemetry") {
            plotTelemetry(plotData as TelemetryData);
        }
    };

    export const clearMapLayers = () => {
        plotLayerGroup?.clearLayers();
    };

    const launchIcon = L.icon({ iconUrl: "target-blue.png", iconSize: [10, 10], iconAnchor: [5, 5] });
    const landIcon = L.icon({ iconUrl: "target-red.png", iconSize: [10, 10], iconAnchor: [5, 5] });
    const burstIcon = L.icon({ iconUrl: "pop-marker.png", iconSize: [16, 16], iconAnchor: [8, 8] });
    const telemetryIcon = L.icon({ iconUrl: "marker-sm-red.png", iconSize: [10, 10], iconAnchor: [5, 5] });

    const plotPrediction = (prediction: PredictionData) => {
        const { launch, landing, burst, flight_path, flight_time } = prediction;

        const range = distHaversine(launch.latlng, landing.latlng, 1);
        const f_hours = Math.floor(flight_time / 3600);
        const f_minutes = Math.floor((flight_time % 3600) / 60).toString().padStart(2, "0");
        const flighttime = `${f_hours}hr${f_minutes}`;

        L.marker(launch.latlng, { title: `Launch`, icon: launchIcon }).addTo(plotLayerGroup);
        L.marker(landing.latlng, { title: `Landing`, icon: landIcon }).addTo(plotLayerGroup);
        L.marker(burst.latlng, { title: `Burst`, icon: burstIcon }).addTo(plotLayerGroup);

        L.polyline(flight_path, { weight: 3, color: "#000000" }).addTo(plotLayerGroup);

        map?.fitBounds(L.latLngBounds(flight_path));
    };

    const plotTelemetry = (telemetry: TelemetryData) => {
        L.marker(telemetry.launch.latlng, { title: `Launch`, icon: launchIcon }).addTo(plotLayerGroup);

        telemetry.datapoints.forEach((point) => {
            L.marker([point.latitude, point.longitude], {
                title: `Telemetry at ${point.datetime}`,
                icon: telemetryIcon,
            })
                .bindPopup(`<b>Telemetry Point</b><br>Lat: ${point.latitude.toFixed(6)}<br>Lon: ${point.longitude.toFixed(6)}`)
                .addTo(plotLayerGroup);
        });

        L.polyline(telemetry.flight_path, { weight: 3, color: "#000000" }).addTo(plotLayerGroup);

        map?.fitBounds(L.latLngBounds(telemetry.flight_path));
    };

    export const panTo = (lat: number, lng: number) => {
        if (map) {
            map.setView([lat, lng], map.getZoom());
        }
    };

    export const zoomTo = (lat: number, lng: number, zoomLevel: number) => {
        if (map) {
            map.setView([lat, lng], zoomLevel);
        }
    };

    export const getMap = () => {
        return map;
    };
</script>

<div class="map-container" bind:this={mapContainer}>
    <div class="card coordinates-display">
        <p class="card-text">
            <b>Lat:</b>
            {mouseLat.toFixed(6)},
            <b>Lon:</b>
            {mouseLng.toFixed(6)}
        </p>
    </div>
    <div class="panel-container">
        <slot />
    </div>
    {#if map && windData}
        <WindVisualization {map} windData={windData} />
    {/if}
</div>