
<template>
  <div @mousemove="onMouseMove" class="full-page" @keyup.esc.stop="onClose" @keyup.left.stop="onPrev" @keyup.right.stop="onNext" tabindex="1" :style="{ opacity: dismissDistance ? (200 - dismissDistance)/100 : 1 }">
    <div class="image-layer" ref="imageContainer" @scroll="onScroll($event)">
      <div class="image" v-for="(entry, index) in entries" :key="entry.id" v-bind:id="`image${index}`" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd" :style="{ marginTop: dismissDistance ? (dismissDistance + 'px') : 0 }">
        <img :src="entry.fullFileUrl" v-if="getFileTypeGroup(entry) === 'image'" draggable="false"/>
        <video v-if="getFileTypeGroup(entry) !== 'image'" ref="videoElements" controls>
          <source :src="entry.fullFileUrl" />
        </video>
      </div>
    </div>
    <Button icon="fa-solid fa-download" v-show="showOverlay && hasDownloadHandler" secondary large plain tool @click="onDownload" class="download-button"/>
    <Button icon="fa-solid fa-xmark" v-show="showOverlay" secondary large plain tool @click="onClose" class="close-button"/>
    <Button icon="fa-solid fa-chevron-left" secondary large plain tool @click="onPrev" v-show="showOverlay && curIndex > 0" class="prev-button"/>
    <Button icon="fa-solid fa-chevron-right" secondary large plain tool @click="onNext" v-show="showOverlay && curIndex < (entries.length-1)" class="next-button"/>
    <video style="display: none" ref="typeTestVideo"></video>
  </div>
</template>

<script>

import { getFileTypeGroup } from '../utils.js';
import Button from '../components/Button.vue';

let touchDist = 0;
let startY = 0;
const dismissDistanceThreshold = 150;
const dismissTriggerThreshold = 50;

export default {
    name: 'ImageViewer',
    emits: [ 'close', 'saved' ],
    props: {
      downloadHandler: {
        type: Function,
        default: null
      },
      navigationHandler: {
        type: Function,
        default: (toEntry) => {
          this.$router.replace(toEntry.resourcePath);
        }
      }
    },
    components: {
      Button
    },
    data() {
      return {
        showOverlay: false,
        showOverlayTimer: null,
        lockScroll: false,
        curIndex: -1,
        lastMousePosition: {},
        entry: {},
        entries: [],
        dismissDistance: 0
      };
    },
    computed: {
      hasDownloadHandler() { return typeof this.downloadHandler === 'function' }
    },
    methods: {
      getFileTypeGroup,
      keepOverlayAlive() {
        this.showOverlay = true;
        clearTimeout(this.showOverlayTimer);
        this.showOverlayTimer = setTimeout(() => {
          // avoid hiding if cursor is above a button
          // this crashes sometimes, not yet sure how to better prevent for now "Document.elementFromPoint: Argument 1 is not a finite floating-point value."
          try {
            if (document.elementFromPoint(this.lastMousePosition.x || 0, this.lastMousePosition.y || 0) !== this.$refs.imageContainer) return;
          } catch (e) {
            console.error('ImageViewer crash. lastMousePosition', this.lastMousePosition, e);
          }

          this.showOverlay = false;
        }, 2000);
      },
      onMouseMove() {
        this.lastMousePosition = { x: event.pageX, y: event.pageY };
        this.keepOverlayAlive();
      },
      canHandle(entry) {
        return (getFileTypeGroup(entry) === 'image' || this.$refs.typeTestVideo.canPlayType(entry.mimeType))
          && !(entry.mimeType === 'image/heif') // supported on apple it seems
          && !(entry.mimeType === 'image/vnd.adobe.photoshop');
      },
      onScroll() {
        if (this.lockScroll) return;

        for (let i = 1; i < this.entries.length; i++) {
          const elem = document.getElementById('image'+i);
          const rect = elem.getBoundingClientRect();
          const midX = (rect.x+(rect.width/2));
          if (midX >= (rect.width/4) && midX <= 3*(rect.width/4)) {
            this.entry = this.entries[i];

            if (this.curIndex !== i) {
              this.curIndex = i;
              this.navigationHandler(this.entry);
            }

            break;
          }
        }
      },
      onTouchStart(event) {
        this.dismissDistance = 0;
        touchDist = 0;
        startY = event.changedTouches[0].pageY;
      },
      onTouchMove(event) {
        touchDist = event.changedTouches[0].pageY - startY // get total dist traveled by finger while in contact with surface
        if (touchDist > dismissTriggerThreshold) this.dismissDistance = touchDist - dismissTriggerThreshold;
      },
      onTouchEnd() {
        if (touchDist > dismissDistanceThreshold) this.onClose();
        touchDist = 0;
        this.dismissDistance = 0;
      },
      open(entry, entries, instant = true) {
        if (!entry || entry.isDirectory || !this.canHandle(entry)) return;

        this.lockScroll = true;
        this.keepOverlayAlive();
        this.entry = entry;
        this.entries = entries;
        this.curIndex = entries.findIndex((item) => item.fileName === entry.fileName);

        setTimeout(() => {
          document.getElementById(`image${this.curIndex}`).scrollIntoView({ behavior: instant ? 'instant' : 'smooth' });
          this.$el.focus();
        }, 0);

        setTimeout(() => {
          this.lockScroll = false;
        }, 500);
      },
      onDownload() {
        if (!this.hasDownloadHandler) return;
        this.downloadHandler(this.entry);
      },
      onClose() {
        this.$emit('close');

        // pause/stop video and audio playback on close
        if (this.$refs.videoElements && this.$refs.videoElements.length) this.$refs.videoElements.forEach(e => e.pause());

        // given some time for closing to keep DOM elements alive
        setTimeout(() => {
          this.entry = {};
        }, 500);
      },
      onPrev() {
        if (this.curIndex < 1) return;
        this.navigationHandler(this.entries[this.curIndex-1]);
        this.open(this.entries[this.curIndex-1], this.entries, false);
      },
      onNext() {
        if (this.curIndex >= this.entries.length) return;
        this.navigationHandler(this.entries[this.curIndex+1]);
        this.open(this.entries[this.curIndex+1], this.entries, false);
      }
    },
    mounted() {}
};

</script>

<style scoped>

.full-page {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: black;
  display: flex;
  flex-direction: column;
}

.download-button {
  position: absolute;
  bottom: 10px;
  right: 10px;
}

.close-button {
  position: absolute;
  top: 10px;
  right: 10px;
}

.prev-button {
  position: absolute;
  top: 50%;
  left: 10px;
}

.next-button {
  position: absolute;
  top: 50%;
  right: 10px;
}

.image-layer {
  display: flex;
  position: fixed;
  height: 100%;
  width: 100%;
  overflow-y: hidden;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.image {
  flex: 0 0 auto;
  width: 100%;
  height: 100%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
  display: inline-block;
  user-select: none;
}

img, video {
  object-fit: contain;
  width: 100%;
  height: 100%;
  user-select: none;
}

</style>
