<script lang="ts">
  /**
   * Shows an image with attribution
   */
  import ImageAttribution from "./ImageAttribution.svelte"
  import { Store } from "../../Logic/UIEventSource"

  import type { HotspotProperties, ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
  import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
  import { UIEventSource } from "../../Logic/UIEventSource"
  import { MagnifyingGlassPlusIcon } from "@babeard/svelte-heroicons/outline"
  import { CloseButton } from "flowbite-svelte"
  import ImageOperations from "./ImageOperations.svelte"
  import Popup from "../Base/Popup.svelte"
  import { onDestroy } from "svelte"
  import type { Feature, Geometry, Point } from "geojson"
  import Loading from "../Base/Loading.svelte"
  import Translations from "../i18n/Translations"
  import Tr from "../Base/Tr.svelte"
  import DotMenu from "../Base/DotMenu.svelte"
  import LoadingPlaceholder from "../Base/LoadingPlaceholder.svelte"
  import { MenuState } from "../../Models/MenuState"
  import ThemeViewState from "../../Models/ThemeViewState"
  import Panorama360 from "../../assets/svg/Panorama360.svelte"
  import { ExternalLinkIcon } from "@rgossiaux/svelte-heroicons/solid"

  export let image: Partial<ProvidedImage> & { id: string; url: string }
  let fallbackImage: string = undefined
  if (image?.provider === Mapillary.singleton) {
    fallbackImage = "./assets/svg/blocked.svg"
  }

  let imgEl: HTMLImageElement
  export let imgClass: string = undefined
  export let state: ThemeViewState = undefined
  export let attributionFormat: "minimal" | "medium" | "large" = "medium"
  let previewedImage: UIEventSource<Partial<ProvidedImage>> = MenuState.previewedImage
  export let canZoom = previewedImage !== undefined
  export let nearbyFeatures:
    | Feature<Geometry, HotspotProperties>[]
    | Store<Feature<Geometry, HotspotProperties>[]> = []

  let loaded = false
  let visitUrl = image.provider?.visitUrl(image)
  let showBigPreview = new UIEventSource(false)
  onDestroy(
    showBigPreview.addCallbackAndRun((shown) => {
      state?.guistate?.setPreviewedImage(shown ? image : undefined)
    })
  )
  if (previewedImage) {
    onDestroy(
      previewedImage.addCallbackAndRun((previewedImage) => {
        showBigPreview.set(
          previewedImage !== undefined &&
            (previewedImage?.id ?? previewedImage?.url) === (image.id ?? image.url)
        )
      })
    )
  }

  function highlight(entered: boolean = true) {
    if (!entered) {
      state?.geocodedImages.set([])
      return
    }
    if (isNaN(image.lon) || isNaN(image.lat)) {
      return
    }
    const f: Feature<Point> = {
      type: "Feature",
      properties: {
        id: image.id,
        rotation: image.rotation,
      },
      geometry: {
        type: "Point",
        coordinates: [image.lon, image.lat],
      },
    }
    state?.geocodedImages.set([f])
  }
</script>

<Popup shown={showBigPreview} bodyPadding="p-0" dismissable={true}>
  <div style="height: 80vh">
    <ImageOperations {image} {nearbyFeatures}>
      <slot name="preview-action" />
      <slot name="dot-menu-actions" slot="dot-menu-actions" />
    </ImageOperations>
  </div>
  <div class="absolute right-4 top-4">
    <CloseButton
      class="normal-background"
      on:click={() => {
        previewedImage?.set(undefined)
      }}
    />
  </div>
</Popup>

{#if image.status !== undefined && image.status !== "ready" && image.status !== "hidden"}
  <div class="flex h-full flex-col justify-center">
    <Loading>
      <Tr t={Translations.t.image.processing} />
    </Loading>
  </div>
{:else if image.status !== "hidden"}
  <div class="relative shrink-0">
    <div
      class={"relative w-fit"}
      on:mouseenter={() => highlight()}
      on:mouseleave={() => highlight(false)}
    >
      {#if $$slots["dot-menu-actions"] || visitUrl !== undefined}
        <DotMenu dotsPosition="top-0 left-0 absolute" hideBackground>
          <slot name="dot-menu-actions" />
          {#if visitUrl !== undefined}
            <a href={visitUrl} target="_blank" rel="noopener">
              <ExternalLinkIcon class="w-6" />
              <Tr t={Translations.t.image.openOnWebsite.Subs(image.provider)} />
            </a>
          {/if}
        </DotMenu>
      {/if}
      {#if !loaded}
        <LoadingPlaceholder />
      {/if}
      <img
        class:hidden={!loaded}
        bind:this={imgEl}
        on:load={() => (loaded = true)}
        class={imgClass ?? ""}
        class:cursor-zoom-in={canZoom}
        on:click={() => {
          previewedImage?.set(image)
        }}
        on:error={() => {
          if (fallbackImage) {
            imgEl.src = fallbackImage
          }
        }}
        src={image.url}
      />

      {#if image.isSpherical}
        <div
          class="pointer-events-none absolute left-0 top-0 flex h-full w-full items-center justify-center"
        >
          <div class="rounded-full bg-black p-[3.25rem] opacity-50">
            <div class="relative flex h-0 w-0 items-center justify-center">
              <Panorama360 class="absolute h-16 w-16" color="#ffffff" />
            </div>
          </div>
        </div>
      {/if}

      {#if canZoom && loaded}
        <div
          class="bg-black-transparent absolute right-0 top-0 rounded-bl-full"
          on:click={() => previewedImage?.set(image)}
        >
          <MagnifyingGlassPlusIcon class="h-8 w-8 cursor-zoom-in pb-3 pl-3" color="white" />
        </div>
      {/if}
    </div>
    <div class="absolute bottom-0 left-0">
      <ImageAttribution {image} {attributionFormat} />
    </div>
  </div>
{:else if image.status === "hidden"}
  <div class="subtle">This image has been reported</div>
{/if}
