<script lang="ts">
  /**
   * Show nearby images which can be clicked
   */
  import type { OsmTags } from "../../Models/OsmFeature"
  import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
  import type { SpecialVisualizationState } from "../SpecialVisualization"
  import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
  import LinkableImage from "./LinkableImage.svelte"
  import type { Feature, Geometry, Point } from "geojson"
  import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
  import Loading from "../Base/Loading.svelte"
  import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
  import Tr from "../Base/Tr.svelte"
  import Translations from "../i18n/Translations"
  import MapillaryLink from "../BigComponents/MapillaryLink.svelte"
  import MaplibreMap from "../Map/MaplibreMap.svelte"
  import { Map as MlMap } from "maplibre-gl"
  import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
  import ShowDataLayer from "../Map/ShowDataLayer"
  import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
  import * as geocoded_image from "../../../public/assets/generated/layers/geocoded_image.json"
  import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
  import { onDestroy } from "svelte"
  import { BBox } from "../../Logic/BBox"
  import PanoramaxLink from "../BigComponents/PanoramaxLink.svelte"
  import { GeoOperations } from "../../Logic/GeoOperations"
  import type { HotspotProperties, PanoramaView } from "../../Logic/ImageProviders/ImageProvider"

  export let tags: UIEventSource<OsmTags>
  export let state: SpecialVisualizationState
  export let lon: number
  export let lat: number
  export let feature: Feature

  export let linkable: boolean = true
  export let layer: LayerConfig

  let imagesProvider = state.nearbyImageSearcher

  let loadedImages = AllImageProviders.loadImagesFor(tags).mapD(
    (loaded) => new Set(loaded.map((img) => img.url))
  )
  let imageState = imagesProvider.getImagesAround(lon, lat)
  let result: Store<P4CPicture[]> = imageState.images.mapD(
    (pics: P4CPicture[]) =>
      pics
        .filter(
          (p: P4CPicture) => !loadedImages.data.has(p.pictureUrl) // We don't show any image which is already linked
        )
        .slice(0, 25),
    [loadedImages]
  )

  // Panorama-views get a geojson feature to browse around
  let asFeatures = result.map((p4cs) =>
    p4cs.map(
      (p4c) =>
        <Feature<Point, PanoramaView>>{
          type: "Feature",
          geometry: {
            type: "Point",
            coordinates: [p4c.coordinates.lng, p4c.coordinates.lat],
          },
          properties: <PanoramaView>{
            id: p4c.pictureUrl,
            url: p4c.pictureUrl,
            northOffset: p4c.direction,
            rotation: p4c.direction,
            spherical: p4c.details.isSpherical ? "yes" : "no",
            provider: p4c.provider
          },
        }
    )
  )

  let selected = new UIEventSource<P4CPicture>(undefined)
  let selectedAsFeature = selected.mapD((s) => {
    return [
      <Feature<Point>>{
        type: "Feature",
        geometry: {
          type: "Point",
          coordinates: [s.coordinates.lng, s.coordinates.lat],
        },
        properties: {
          id: s.pictureUrl,
          selected: "yes",
          rotation: s.direction,
        },
      },
    ]
  })

  let someLoading = imageState.state.mapD((stateRecord) =>
    Object.values(stateRecord).some((v) => v === "loading")
  )
  let errors = imageState.state.mapD((stateRecord) =>
    Object.keys(stateRecord).filter((k) => stateRecord[k] === "error")
  )
  let highlighted = new UIEventSource<string>(undefined)

  onDestroy(
    highlighted.addCallbackD((hl) => {
      const p4c = result.data?.find((i) => i.pictureUrl === hl)
      selected.set(p4c)
    })
  )

  let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
  let mapProperties = new MapLibreAdaptor(map, {
    rasterLayer: state.mapProperties.rasterLayer,
    rotation: state.mapProperties.rotation,
    pitch: state.mapProperties.pitch,
    zoom: new UIEventSource<number>(16),
    location: new UIEventSource({ lon, lat }),
  })

  const geocodedImageLayer = new LayerConfig(<LayerConfigJson>geocoded_image)
  new ShowDataLayer(map, {
    features: new StaticFeatureSource(asFeatures),
    layer: geocodedImageLayer,
    zoomToFeatures: true,
    onClick: (feature) => {
      console.log("CLicked:", feature.properties)
      highlighted.set(feature.properties.id)
    },
  })

  ShowDataLayer.showMultipleLayers(map, new StaticFeatureSource([feature]), state.theme.layers)

  onDestroy(
    asFeatures.addCallbackAndRunD((features) => {
      if (features.length == 0) {
        return
      }
      let bbox = BBox.get(features[0])
      for (const f of features) {
        bbox = bbox.unionWith(BBox.get(f))
      }
      mapProperties.maxbounds.set(bbox.pad(4))
    })
  )

  new ShowDataLayer(map, {
    features: new StaticFeatureSource(selectedAsFeature),
    layer: geocodedImageLayer,
    onClick: (feature) => {
      highlighted.set(feature.properties.id)
    },
  })

  let nearbyFeatures: Store<Feature<Geometry, HotspotProperties>[]> = asFeatures.map(
    (nearbyPoints) => {
      return [
        {
          type: "Feature",
          geometry: { type: "Point", coordinates: GeoOperations.centerpointCoordinates(feature) },
          properties: <HotspotProperties>{
            name: layer.title?.GetRenderValue(feature.properties).Subs(feature.properties).txt,
            focus: true,
          },
        },
        ...nearbyPoints
          .filter((p) => p.properties["spherical"] === "yes")
          .map((f) => ({
            ...f,
            properties: <HotspotProperties>{
              name: "Nearby panorama",
              pitch: "auto",
              type: "scene",
              gotoPanorama: f,
              focus: false,
            },
          })),
      ]
    }
  )

  onDestroy(
    tags.addCallbackAndRunD((tags) => {
      if (
        tags.id.startsWith("node/") ||
        tags.id.startsWith("way/") ||
        tags.id.startsWith("relation/")
      ) {
        return
      }
      linkable = false
    })
  )
</script>

<div class="flex flex-col">
  {#if $result.length === 0}
    {#if $someLoading}
      <div class="m-4 flex justify-center">
        <Loading />
      </div>
    {:else}
      <Tr t={Translations.t.image.nearby.noNearbyImages} cls="alert" />
    {/if}
  {:else}
    <div class="flex w-full space-x-4 overflow-x-auto" style="scroll-snap-type: x proximity">
      {#each $result as image (image.pictureUrl)}
        <span
          class="w-fit shrink-0"
          style="scroll-snap-align: start"
          on:mouseenter={() => {
            highlighted.set(image.pictureUrl)
          }}
          on:mouseleave={() => {
            highlighted.set(undefined)
            selected.set(undefined)
          }}
        >
          <LinkableImage
            {tags}
            {image}
            {state}
            {feature}
            {layer}
            {linkable}
            {highlighted}
            {nearbyFeatures}
          />
        </span>
      {/each}
    </div>
  {/if}
  <div class="flex w-full flex-wrap justify-end gap-x-8 pt-2">
    <PanoramaxLink
      large={false}
      mapProperties={{ zoom: new ImmutableStore(16), location: new ImmutableStore({ lon, lat }) }}
    />
    <MapillaryLink
      large={false}
      mapProperties={{ zoom: new ImmutableStore(16), location: new ImmutableStore({ lon, lat }) }}
    />
  </div>

  <div class="my-2 flex justify-between">
    <div>
      {#if $someLoading && $result.length > 0}
        <Loading />
      {/if}
      {#if $errors.length > 0}
        <Tr
          cls="alert font-sm block"
          t={Translations.t.image.nearby.failed.Subs({ service: $errors.join(", ") })}
        />
      {/if}
    </div>
  </div>

  <div class="h-48">
    <MaplibreMap interactive={false} {map} {mapProperties} />
  </div>
</div>
