<script lang="ts">
  import type { IEntry } from "upend/types";

  import api from "../../../lib/api";
  import { useEntity } from "../../../lib/entity";
  import IconButton from "../../utils/IconButton.svelte";
  import Spinner from "../../utils/Spinner.svelte";
  import UpObject from "../UpObject.svelte";

  export let address: string;
  export let editable: boolean;
  export let detail: boolean;

  const { entity } = useEntity(address);

  let imageLoaded = false;
  let imageEl: HTMLImageElement;

  $: svg = Boolean($entity?.get("FILE_MIME")?.toString().includes("svg+xml"));

  interface Annotorious {
    addAnnotation: (a: W3cAnnotation) => void;
    on: ((
      e: "createAnnotation" | "deleteAnnotation",
      c: (a: W3cAnnotation) => void
    ) => void) &
      ((
        e: "updateAnnotation",
        c: (a: W3cAnnotation, b: W3cAnnotation) => void
      ) => void);
    clearAnnotations: () => void;
    readOnly: boolean;
    destroy: () => void;
  }

  interface W3cAnnotation {
    type: "Annotation";
    body: Array<{ type: "TextualBody"; value: string; purpose: "commenting" }>;
    target: {
      selector: {
        type: "FragmentSelector";
        conformsTo: "http://www.w3.org/TR/media-frags/";
        value: string;
      };
    };
    "@context": "http://www.w3.org/ns/anno.jsonld";
    id: string;
  }

  let anno: Annotorious;
  $: if (anno) anno.readOnly = !editable;
  $: if (anno) {
    anno.clearAnnotations();
    $entity?.backlinks
      .filter((e) => e.attribute == "ANNOTATES")
      .forEach(async (e) => {
        const annotation = await api.fetchEntity(e.entity);
        if (annotation.get("W3C_FRAGMENT_SELECTOR")) {
          anno.addAnnotation({
            type: "Annotation",
            body: annotation.attr["LBL"].map((e) => {
              return {
                type: "TextualBody",
                value: String(e.value.c),
                purpose: "commenting",
              };
            }),
            target: {
              selector: {
                type: "FragmentSelector",
                conformsTo: "http://www.w3.org/TR/media-frags/",
                value: String(annotation.get("W3C_FRAGMENT_SELECTOR")),
              },
            },
            "@context": "http://www.w3.org/ns/anno.jsonld",
            id: e.entity,
          });
        }
      });
  }
  $: hasAnnotations = $entity?.backlinks.some(
    (e) => e.attribute === "ANNOTATES"
  );

  let a8sLinkTarget: HTMLDivElement;
  let a8sLinkAddress: string;

  async function loaded() {
    const { Annotorious } = await import("@recogito/annotorious");

    if (anno) {
      anno.destroy();
    }

    anno = new Annotorious({
      image: imageEl,
      drawOnSingleClick: true,
      fragmentUnit: "percent",
      widgets: [
        "COMMENT",
        (info: { annotation: W3cAnnotation }) => {
          a8sLinkAddress = info.annotation?.id;
          return a8sLinkTarget;
        },
      ],
    });

    anno.on("createAnnotation", async (annotation) => {
      const [_, uuid] = await api.putEntry({
        entity: {
          t: "Uuid",
        },
      });

      annotation.id = uuid;

      await api.putEntry([
        {
          entity: uuid,
          attribute: "ANNOTATES",
          value: {
            t: "Address",
            c: address,
          },
        },
        {
          entity: uuid,
          attribute: "W3C_FRAGMENT_SELECTOR",
          value: {
            t: "String",
            c: annotation.target.selector.value,
          },
        },
        ...annotation.body.map((body) => {
          return {
            entity: uuid,
            attribute: "LBL",
            value: {
              t: "String",
              c: body.value,
            },
          } as IEntry;
        }),
      ]);
    });
    anno.on("updateAnnotation", async (annotation) => {
      const annotationObject = await api.fetchEntity(annotation.id);
      await Promise.all(
        annotationObject.attr["LBL"]
          .concat(annotationObject.attr["W3C_FRAGMENT_SELECTOR"])
          .map(async (e) => api.deleteEntry(e.address))
      );
      await api.putEntry([
        {
          entity: annotation.id,
          attribute: "W3C_FRAGMENT_SELECTOR",
          value: {
            t: "String",
            c: annotation.target.selector.value,
          },
        },
        ...annotation.body.map((body) => {
          return {
            entity: annotation.id,
            attribute: "LBL",
            value: {
              t: "String",
              c: body.value,
            },
          } as IEntry;
        }),
      ]);
    });
    anno.on("deleteAnnotation", async (annotation) => {
      await api.deleteEntry(annotation.id);
    });

    imageLoaded = true;
  }

  function clicked() {
    if (!document.fullscreenElement) {
      if (!editable && !hasAnnotations) {
        imageEl.requestFullscreen();
      }
    } else {
      document.exitFullscreen();
    }
  }

  let brightnesses = [0.5, 0.75, 1, 1.25, 1.5, 2, 2.5];
  let brightnessIdx = 2;
  function cycleBrightness() {
    brightnessIdx++;
    brightnessIdx = brightnessIdx % brightnesses.length;
  }

  let contrasts = [0.5, 0.75, 1, 1.25, 1.5];
  let contrastsIdx = 2;
  function cycleContrast() {
    contrastsIdx++;
    contrastsIdx = contrastsIdx % contrasts.length;
  }

  $: {
    if (imageEl) {
      const brightness = brightnesses[brightnessIdx];
      const contrast = contrasts[contrastsIdx];
      imageEl.style.filter = `brightness(${brightness}) contrast(${contrast})`;
    }
  }
</script>

<div class="image-viewer">
  {#if !imageLoaded}
    <Spinner centered />
  {/if}
  {#if imageLoaded}
    <div class="toolbar">
      <IconButton name="brightness-half" on:click={cycleBrightness} />
      <IconButton name="tone" on:click={cycleContrast} />
    </div>
  {/if}
  <div
    class="image"
    class:zoomable={!editable && !hasAnnotations}
    on:click={clicked}
  >
    <img
      class="preview-image"
      src="{api.apiUrl}/{detail || svg ? 'raw' : 'thumb'}/{address}"
      alt={address}
      on:load={loaded}
      bind:this={imageEl}
      draggable="false"
    />
  </div>
  <div class="a8sUpLink" bind:this={a8sLinkTarget}>
    {#if a8sLinkAddress}
      <div class="link">
        <UpObject link address={a8sLinkAddress} />
      </div>
    {/if}
  </div>
</div>

<style global lang="scss">
  @use "@recogito/annotorious/dist/annotorious.min.css";

  .image-viewer {
    display: flex;
    flex-direction: column;
    min-height: 0;

    .image {
      display: flex;
      justify-content: center;
      min-height: 0;

      & > *,
      img {
        min-width: 0;
        max-width: 100%;
        min-height: 0;
        max-height: 100%;
      }

      img {
        margin: auto;
      }
    }

    .toolbar {
      display: flex;
      justify-content: center;
      margin-bottom: 0.5em;
    }

    .zoomable {
      cursor: zoom-in;
    }

    img:fullscreen {
      cursor: zoom-out;
    }
  }

  .r6o-editor {
    font-family: inherit;
  }

  .a8sUpLink {
    text-align: initial;

    .link {
      margin: 0.5em 1em;
    }
  }
</style>
