<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"
  import { ExclamationTriangle as TriangleOutline } from "@babeard/svelte-heroicons/outline/ExclamationTriangle"
  import LoginToggle from "../Base/LoginToggle.svelte"

  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 error = false
  let notFound = false
  let ignoreHidden = false
  let isInStrictMode = new UIEventSource(false)

  async function detectErrorReason() {
    try {

      const response = await fetch(
        image.url,
        {
          headers: {
            "Accept": "image/avif,image/webp,*/*",
          },
        },
      )
      if (response.status === 404) {
        notFound = true
      }
    } catch
      (e) {
      console.log("Could not load image while trying to remediate", e)
    }
  }

  async function onError() {
    Mapillary.isInStrictMode().addCallbackAndRunD(isStrict => {
      isInStrictMode.set(isStrict)
      return true // unregister
    })
    await detectErrorReason()
    error = true
  }

  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 error}
  <div class="h-80 w-60 interactive flex flex-col justify-center items-center p-4 text-center">
    {#if notFound}
      <div class="alert flex items-center">
        <TriangleOutline class="shrink-0 h-8 w-8" />
        Not found
      </div>
      This image is probably incorrect or deleted.
      <slot name="not-found-extra" />
    {:else}
      <div class="alert flex items-center">
        <TriangleOutline class="shrink-0 h-8 w-8" />
        <Tr t={Translations.t.image.loadingFailed} />
      </div>
      {#if image.provider.name.toLowerCase() === "mapillary" && $isInStrictMode}
        <Tr t={Translations.t.image.mapillaryTrackingProtection} />
      {:else if $isInStrictMode}
        <Tr t={Translations.t.image.strictProtectionDetected} />
        {image.provider.name}
        <div class="subtle text-sm mt-8">{image.url}</div>
      {/if}
    {/if}
  </div>
{:else if image.status !== undefined && image.status !== "ready" && image.status !== "hidden"}
  <div class="flex h-80 w-60 flex-col justify-center p-4">
    <Loading>
      <Tr t={Translations.t.image.processing} />
    </Loading>
  </div>
{:else if image.status !== "hidden" || ignoreHidden}
  <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={() => {
          onError()
        }}
        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="h-80 w-60 flex flex-col justify-center items-center break-words p-4 text-center">
    <TriangleOutline class="w-8 h-8 subtle" />
    <Tr t={Translations.t.image.reported} />
    <button class="text-sm" on:click={() => ignoreHidden = true}>
      <Tr t={Translations.t.image.showAnyway} />
    </button>
  </div>
{/if}
