<script lang="ts">
    import * as Grain from '../../../../../grain-types'
    import type { Writable } from 'svelte/store'
    import { getContext, tick } from 'svelte'
    import type * as L from 'leaflet'
    let { zoom, save } = $props()
    import { onMount, onDestroy } from 'svelte'
    let assets: string = '/static'

    import { browser } from '$app/environment'
    import { asset } from '$app/paths'
    import Question from '../../../../Question.svelte'
    import XCircle from '../../../../XCircle.svelte'

    let redIcon: null | L.Icon = null
    let yelIcon: null | L.Icon = null
    let srv =
        import.meta.env.MODE == 'production'
            ? 'https://coturnix.fr'
            : 'http://localhost:5173'

    type Compteur_ = Grain.Compteur & { dot?: L.Marker }
    type Communaute_ = Grain.Communaute & {
        compteurs?: Compteur_[]
        circle?: L.Circle
        isMoving?: boolean
    }

    let communaute: Writable<Communaute_ | null> = getContext('communaute')

    let derogation = $state($communaute?.derogation ?? 'non')
    let map: null | L.Map = null
    let circleOpts = {
        radius: 1000,
        stroke: true,
        fillOpacity: 0,
        color: '#b81413',
        weight: 3,
    }

    let leaflet: null | typeof L = null

    function changeDerogation(e: Event) {
        console.log('Changederogation', derogation, $communaute, e)
        if ($communaute) {
            $communaute.derogation = derogation
            let d = derogationRayonKm() * 1000
            checkRayon($communaute, d)
            if ($communaute?.circle) {
                $communaute.circle.setRadius(d)
            } else if (leaflet) {
                circleOpts.radius = d
                $communaute.circle = leaflet.circle($communaute, circleOpts)
                $communaute.circle.addTo(map!)
            }
            save()
        }
    }

    function derogationRayonKm(): number {
        if ($communaute?.derogation == '20') {
            return 10
        } else if ($communaute?.derogation == '10') {
            return 5
        } else {
            return 1
        }
    }

    type Commune = {
        nom: string
        densite: number
    }

    let communes: null | Commune[] = $state(null)
    let consommateurs: {
        lat: number
        lng: number
        puissance: number
        point?: L.CircleMarker
    }[] = $state([])
    let pompiers: {
        lat: number
        lng: number
        nom: string
        telephone: string
    }[] = $state([])
    let pompierMarkers: L.Marker[] = []
    let total = $state(0)

    function firebrigadeIcon() {
        return leaflet!.divIcon({
            html: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 75" width="40" height="50">
    <path d="M30 73 C18 57 6 50 6 36 A24 24 0 1 1 54 36 C54 50 42 57 30 73Z" fill="#C53030"/>
    <circle cx="30" cy="36" r="21" fill="white"/>
    <rect x="18" y="27" width="26" height="2" rx="1" fill="#9B2C2C"/>
    <line x1="24" y1="27" x2="24" y2="29" stroke="#9B2C2C" stroke-width="1.5"/>
    <line x1="31" y1="27" x2="31" y2="29" stroke="#9B2C2C" stroke-width="1.5"/>
    <line x1="38" y1="27" x2="38" y2="29" stroke="#9B2C2C" stroke-width="1.5"/>
    <rect x="18" y="29" width="26" height="13" rx="2" fill="#C53030"/>
    <rect x="11" y="31" width="10" height="11" rx="2" fill="#C53030"/>
    <rect x="13" y="33" width="6" height="6" rx="1" fill="white" opacity="0.85"/>
    <circle cx="18" cy="42" r="4" fill="#444"/>
    <circle cx="18" cy="42" r="2" fill="white" opacity="0.6"/>
    <circle cx="40" cy="42" r="4" fill="#444"/>
    <circle cx="40" cy="42" r="2" fill="white" opacity="0.6"/>
    <rect x="13" y="27" width="5" height="2.5" rx="1" fill="#FC8181"/>
  </svg>`,
            className: '',
            iconSize: [40, 50],
            iconAnchor: [20, 50],
            popupAnchor: [0, -52],
        })
    }

    async function checkRayon(latlng: Grain.LatLng | null, rayon: number) {
        if (latlng) {
            console.log('checkRayon')
            await Promise.all([
                (async () => {
                    let resp = await fetch(
                        `${srv}/api/intersect?lat=${latlng.lat}&lng=${latlng.lng}&rayon=${rayon}`,
                        {
                            credentials: 'include',
                        }
                    )
                    if (resp.status == 200) {
                        communes = await resp.json()
                    }
                })(),
                (async () => {
                    for (let c of consommateurs) {
                        if (c.point && map) {
                            c.point.removeFrom(map)
                        }
                    }
                    let resp = await fetch(
                        `${srv}/api/grain/consommateurs?lat=${latlng.lat}&lng=${latlng.lng}&rayon=${derogationRayonKm() * 1000}`,
                        {
                            credentials: 'include',
                        }
                    )
                    if (resp.status == 200) {
                        consommateurs = await resp.json()
                        if (leaflet && map) {
                            total = 0
                            for (let c of consommateurs) {
                                c.point = leaflet.circleMarker(c, {
                                    radius: 1,
                                    fill: true,
                                    color: '#b81413',
                                })
                                c.point.addTo(map)
                                total += c.puissance
                            }
                        }
                    }
                })(),
                (async () => {
                    console.log('Pompier')
                    for (let marker of pompierMarkers) {
                        if (marker && map) {
                            marker.removeFrom(map)
                        }
                    }
                    pompierMarkers = []
                    pompiers = []

                    let resp = await fetch(
                        `${srv}/api/grain/pompiers?lat=${latlng.lat}&lng=${latlng.lng}&rayon=10000`,
                        {
                            credentials: 'include',
                        }
                    )
                    console.log('Pompier', resp.status)
                    if (resp.status == 200) {
                        pompiers = await resp.json()
                        if (leaflet && map) {
                            for (let p of pompiers) {
                                let marker = leaflet.marker(p, {
                                    icon: firebrigadeIcon(),
                                })
                                marker
                                    .addTo(map)
                                    .bindPopup(
                                        `<b>${p.nom}</b>${p.telephone ? `<br/>Tél: ${p.telephone}` : ''}`
                                    )
                                    .on('mouseover', function () {
                                        marker.openPopup()
                                    })
                                    .on('mouseout', function () {
                                        marker.closePopup()
                                    })
                                pompierMarkers.push(marker)
                            }
                        }
                    }
                })(),
            ])
        }
    }

    let mapContainer: HTMLElement

    onDestroy(() => {
        console.log('destroy', map)
        if (browser && 'coturnixMap' in document) {
            delete document.coturnixMap
        }
        // map && map.off && map.off(); map && map.remove && map.remove()
    })

    onMount(async () => {
        checkRayon($communaute, derogationRayonKm())
        console.log('map onload', browser)
        if (!browser) {
            return
        }
        leaflet = await import('leaflet')
        await import('leaflet.fullscreen')
        const G = await import('leaflet-gesture-handling')

        leaflet.Map.addInitHook(
            'addHandler',
            'gestureHandling',
            G.GestureHandling
        )

        await tick()
        console.log('Map.svelte')
        map = leaflet.map(leaflet.DomUtil.create('div'), {
            fullscreenControl: true,
            fullscreenControlOptions: {
                position: 'topleft',
            },
            attributionControl: false,
            //@ts-expect-error Plugin
            gestureHandling: true,
        })
        ;(document as Document & { coturnixMap: L.Map }).coturnixMap = map

        // Un peu crados, mais on peut avoir besoin de réinitialiser
        // la carte si elle a déjà été chargée, donc il faut pouvoir la
        // virer du DOM.
        // eslint-disable-next-line svelte/no-dom-manipulating
        mapContainer.appendChild(map.getContainer())

        map.getContainer().style.width = '100%'
        map.getContainer().style.height = '100%'
        map.invalidateSize()

        if ($communaute) {
            derogation = $communaute.derogation
            circleOpts.radius = derogationRayonKm() * 1000
            $communaute.circle = leaflet
                .circle($communaute, circleOpts)
                .addTo(map)
            map.setView($communaute, zoom)
        } else {
            map.setView({ lat: 46, lng: 3 }, 5)
        }
        let layer = leaflet
            .tileLayer(`https://tile.openstreetmap.org/{z}/{x}/{y}.png`, {
                minZoom: 0,
                maxZoom: 18,
                attribution: 'IGN-F/Geoportail',
                tileSize: 256,
                noWrap: true,
            })
            .addTo(map)

        layer.on('tileerror', (e) => {
            console.log(e)
        })

        if (!redIcon) {
            redIcon = leaflet.icon({
                iconUrl: `${assets}/signal-red.png`,
                iconRetinaUrl: `${assets}/signal-red.png`,
                iconAnchor: [12, 41],
            } as L.IconOptions)
        }
        if (!yelIcon) {
            yelIcon = leaflet.icon({
                iconUrl: `${assets}/signal-yellow.png`,
                iconRetinaUrl: `${assets}/signal-yellow.png`,
                iconAnchor: [12, 41],
            } as L.IconOptions)
        }
        if ($communaute?.compteurs) {
            for (let c of $communaute.compteurs) {
                c.dot = leaflet.marker(c, {
                    icon: yelIcon!,
                    draggable: false,
                })
                c.dot.addTo(map)
            }
        }

        map.on('zoomend', function () {
            if (map) zoom = map.getZoom()
        })

        let initialCentre: null | { lat: number; lng: number } = null
        map.on('mousemove', function (e: L.LeafletMouseEvent) {
            if ($communaute?.circle && e.originalEvent.shiftKey) {
                // let m = document.getElementById('descrmap')
                // m!.focus()
                let d = derogationRayonKm() * 1000
                if (initialCentre) {
                    let testCentre = {
                        lat: e.latlng.lat - initialCentre.lat,
                        lng: e.latlng.lng - initialCentre.lng,
                    }
                    if (
                        !checkDistance(
                            testCentre,
                            $communaute.compteurs ?? [],
                            d
                        )
                    ) {
                        initialCentre.lat = e.latlng.lat - $communaute.lat
                        initialCentre.lng = e.latlng.lng - $communaute.lng
                        $communaute.isMoving = true
                    } else {
                        $communaute.lat = testCentre.lat
                        $communaute.lng = testCentre.lng
                        $communaute.circle.setLatLng($communaute)
                        $communaute.isMoving = true
                    }
                } else {
                    if (map && map.distance($communaute, e.latlng) <= d) {
                        $communaute.isMoving = true
                        initialCentre = {
                            lat: e.latlng.lat - $communaute.lat,
                            lng: e.latlng.lng - $communaute.lng,
                        }
                    }
                }
            }
        })

        document.addEventListener('keyup', async (e: KeyboardEvent) => {
            console.log('KEYUP', e)
            if (e.key == 'Shift' && $communaute?.isMoving) {
                if ($communaute) {
                    delete $communaute.isMoving
                    checkRayon($communaute, derogationRayonKm() * 1000)
                    save()
                }
                console.log('invalidDrag')
                initialCentre = null
            }
        })

        map.on('click', function (e: L.LeafletMouseEvent) {
            console.log(e)

            if (map && !$communaute) {
                communaute.set({
                    lat: e.latlng.lat,
                    lng: e.latlng.lng,
                    compteurs: [],
                    derogation,
                })
                circleOpts.radius = derogationRayonKm() * 1000
                $communaute!.circle = leaflet!
                    .circle(e.latlng, circleOpts)
                    .addTo(map)
                checkRayon($communaute, derogationRayonKm() * 1000)
            }
            if (
                map &&
                map.distance($communaute!, e.latlng) <=
                    derogationRayonKm() * 1000
            ) {
                let dot = {
                    lat: e.latlng.lat,
                    lng: e.latlng.lng,
                    dot: leaflet!.marker(e.latlng, {
                        icon: redIcon!,
                        draggable: false,
                    }),
                    azimuth: 0,
                    inclinaison: 0,
                    puissance: 0,
                }
                dot.dot.addTo(map)
                if (!$communaute!.compteurs) {
                    $communaute!.compteurs = []
                }
                $communaute!.compteurs.push(dot)
                $communaute = $communaute
            }
            save()
        })

        recentrer()
    })

    function recentrer(zoom?: number) {
        if (!$communaute || !map) {
            return
        }
        let lng0 = 180
        let lng1 = -180
        let lat0 = 90
        let lat1 = -90

        let mar = 0.5
        let dy = (mar + derogationRayonKm()) / 111
        let lat2 = $communaute.lat - dy
        let lat3 = $communaute.lat + dy

        let dx = dy / Math.cos(($communaute.lat * Math.PI) / 180)
        let lng2 = $communaute.lng - dx
        let lng3 = $communaute.lng + dx
        lng0 = Math.min(lng0, lng2)
        lng1 = Math.max(lng1, lng3)
        lat0 = Math.min(lat0, lat2)
        lat1 = Math.max(lat1, lat3)
        if (zoom === undefined && lng0 < lng1 && lat0 < lat1) {
            console.log([
                [lat0, lng0],
                [lat1, lng1],
            ])
            map.fitBounds([
                [lat0, lng0],
                [lat1, lng1],
            ])
        } else {
            map.setView($communaute, zoom)
        }
    }

    function reset() {
        if (!$communaute || !map) {
            return
        }
        $communaute.circle?.removeFrom(map)
        for (let m of $communaute?.compteurs || []) {
            m.dot?.removeFrom(map)
        }
        $communaute = null
    }

    function checkDistance(
        e: { lat: number; lng: number },
        compteurs: { lat: number; lng: number }[],
        d: number
    ): boolean {
        if (!map) {
            return false
        }
        for (let v of compteurs) {
            let dist = map.distance(e, v)
            if (dist > d) {
                return false
            }
        }
        return true
    }

    function deleteCompteur(event: Event, i: number) {
        event.preventDefault()
        if ($communaute?.compteurs) {
            if (map) {
                $communaute?.compteurs[i]?.dot?.removeFrom(map)
            }
            $communaute.compteurs.splice(i, 1)
        }
        $communaute = $communaute
        save()
    }

    let geoc = $state(false)
    let endroit: string = $state('')

    function diff(a: string, b: string): number {
        const m = a.length,
            n = b.length
        const dp = Array(m + 1)
            .fill(0)
            .map(() => Array(n + 1).fill(0))
        dp[0] = dp[0].map((_, j) => j)
        dp.forEach((row, i) => (row[0] = i))
        for (let i = 1; i <= m; i++) {
            for (let j = 1; j <= n; j++) {
                if (a[i - 1] === b[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1]
                } else {
                    dp[i][j] =
                        1 +
                        Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
                }
            }
        }

        return dp[m][n]
    }

    async function geocoding(e: Event) {
        e.preventDefault()
        geoc = true
        await tick()
        let resp = await fetch(
            `https://data.geopf.fr/geocodage/search?q=${endroit}`
        )
        geoc = false
        if (resp.status == 200) {
            const b = await resp.json()
            console.log('features', b)
            if ('features' in b && b.features.length) {
                let min = -1
                let argmin = 0
                for (let i = 0; i < b.features.length; i++) {
                    const d = diff(
                        b.features[i].properties.label.toLowerCase(),
                        endroit.toLowerCase()
                    )
                    if (min < 0 || d < min) {
                        min = d
                        argmin = i
                    }
                }
                if (min >= 0) {
                    console.log('diff', min, argmin)
                    if (!$communaute) {
                        communaute.set({
                            lat: b.features[argmin].geometry.coordinates[1],
                            lng: b.features[argmin].geometry.coordinates[0],
                            compteurs: [],
                            derogation,
                        })
                    }
                    $communaute!.lng =
                        b.features[argmin].geometry.coordinates[0]
                    $communaute!.lat =
                        b.features[argmin].geometry.coordinates[1]
                }
            }
            if ($communaute) {
                let d = derogationRayonKm() * 1000
                await checkRayon($communaute, d)
                circleOpts.radius = d
                if ($communaute.circle) {
                    $communaute.circle.setLatLng($communaute)
                } else if (leaflet) {
                    $communaute.circle = leaflet
                        .circle($communaute, circleOpts)
                        .addTo(map!)
                }
                recentrer(d == 10000 ? 10 : d == 5000 ? 11 : 12)
            }
        }
        return null
    }

    let fmt = (n: number) =>
        Intl.NumberFormat('fr-FR', { maximumFractionDigits: n })

    function formatConso(x: number) {
        let f = 0
        let s = ''
        if (Math.abs(x) <= 1000) {
            s = ' MWh/an'
            f = x
        } else if (Math.abs(x) <= 1000000) {
            s = ' GWh/an'
            f = x / 1000
        } else {
            s = ' TWh/an'
            f = x / 1000000
        }
        return fmt(2).format(f) + s
    }
</script>

<svelte:head>
    <link rel="stylesheet" href={asset('/static/leaflet.css')} />
    <link rel="stylesheet" href={asset('/static/leafletfs.css')} />
    <link
        rel="stylesheet"
        href={asset('/static/leaflet-gesture-handling.min.css')} />
</svelte:head>
<div class="p-10" style="min-height: calc(100vh - 180px);">
    <h1 class="my-8 text-4xl font-bold">
        Quelques <span class="text-primary">détails</span> de plus.
    </h1>

    <div class="my-3 mt-10">
        Cliquez sur la carte ci-dessous pour ajouter des compteurs, ou cliquez
        en maintenant la touche Shift pour positionner la communauté.
    </div>
    <div class="my-3">
        <form onsubmit={geocoding}>
            <div class="flex gap-2 items-center">
                <input type="text" class="input" bind:value={endroit} />
                <button class="btn btn-outline btn-primary" onclick={geocoding}
                    >Recherche
                </button>
                {#if geoc}
                    <div class="ms-3 loading"></div>
                {/if}
            </div>
        </form>
    </div>
    <div bind:this={mapContainer} class="w-full" style="height:500px"></div>

    <div class="flex flex-col">
        <button
            class="btn btn-sm btn-outline btn-secondary my-2 ms-auto"
            onclick={() => recentrer()}>Recentrer</button>
        <button
            class="btn btn-sm btn-outline btn-secondary my-2 ms-auto"
            onclick={reset}>Reset</button>
        {#if $communaute}
            <div class="my-3">
                Total des consommations: {formatConso(total)}<br />Nombre de
                consommateurs: {consommateurs.length}<br /><a
                    href="/api/grain/consommateurs/csv?lat={$communaute.lat}&lng={$communaute.lng}&rayon={derogationRayonKm() *
                        1000}"
                    target="blank">Télécharger les adresses au format CSV</a>
            </div>
        {/if}

        {#if $communaute?.compteurs?.length}
            <div class="my-3">
                <table class="table">
                    <thead>
                        <tr
                            ><th>Puissance</th><th
                                >Azimuth (le Sud est à 0°, l'Est à -90°, l'Ouest
                                à 90°)</th
                            ><th>Inclinaison</th><th></th></tr>
                    </thead>
                    <tbody>
                        {#each $communaute.compteurs as c, i (i)}
                            <tr>
                                <td>
                                    <input
                                        type="number"
                                        class="input"
                                        bind:value={c.puissance} />
                                </td>
                                <td>
                                    <input
                                        type="number"
                                        class="input"
                                        bind:value={c.azimuth} />
                                </td>
                                <td>
                                    <input
                                        type="number"
                                        class="input"
                                        bind:value={c.inclinaison} />
                                </td>
                                <td>
                                    <button
                                        class="btn btn-sm btn-outline btn-primary"
                                        aria-label="Supprimer le compteur"
                                        onclick={(e) => deleteCompteur(e, i)}
                                        ><XCircle /></button>
                                </td>
                            </tr>
                        {/each}
                    </tbody>
                </table>
            </div>
        {/if}

        <div class="p-3 flex flex-col">
            <div class="flex">
                <label for="derog" class="form-label">
                    Extension de périmètre (habitat rural et/ou dispersé)
                </label>&nbsp;<Question
                    datatip="Par dérogation, le rayon de la communauté peut être étendu à 5km ou 10km." />&nbsp;
                <select
                    id="derog"
                    class="select select-sm w-auto inline ms-5"
                    name="derog"
                    bind:value={derogation}
                    onchange={changeDerogation}>
                    <option value="non">Non</option>
                    <option value="10">10km</option>
                    <option value="20">20km</option>
                </select>
            </div>
            {#if communes && communes.length}
                <h2 class="text-2xl mt-5">Communes concernées</h2>
                <table class="table">
                    <thead>
                        <tr><th>Nom</th><th>Éligibilité</th></tr>
                    </thead>
                    <tbody>
                        {#each communes as c (c.nom)}
                            <tr
                                ><td>{c.nom}</td><td
                                    >{#if c.densite >= 5}20km{:else if c.densite >= 3}10km{#if derogation == '20'}<i
                                                class="ms-3 text-danger bi bi-exclamation-triangle"
                                            ></i
                                            >{/if}{:else}2km{#if derogation != 'non'}<i
                                                class="ms-3 text-danger bi bi-exclamation-triangle"
                                            ></i
                                            >{/if}{/if}</td
                                ></tr>
                        {/each}
                    </tbody>
                </table>
            {/if}
        </div>
    </div>
</div>
