<template>
  <div class="outer" v-show="years.length > 0">
    <div class="inner" ref="inner">
      <div
        v-for="year of years"
        class="group"
        :key="year.year"
        @click="click(year)"
      >
        <img class="fill-block" :src="year.url" />

        <div class="overlay">
          {{ year.text }}
        </div>
      </div>
    </div>

    <div class="left-btn dir-btn" v-if="hasLeft">
      <NcActions>
        <NcActionButton
          :aria-label="t('memories', 'Move left')"
          @click="moveLeft"
        >
          {{ t("memories", "Move left") }}
          <template #icon> <LeftMoveIcon :size="28" /> </template>
        </NcActionButton>
      </NcActions>
    </div>
    <div class="right-btn dir-btn" v-if="hasRight">
      <NcActions>
        <NcActionButton
          :aria-label="t('memories', 'Move right')"
          @click="moveRight"
        >
          {{ t("memories", "Move right") }}
          <template #icon> <RightMoveIcon :size="28" /> </template>
        </NcActionButton>
      </NcActions>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

import NcActions from "@nextcloud/vue/dist/Components/NcActions";
import NcActionButton from "@nextcloud/vue/dist/Components/NcActionButton";

import * as utils from "../../services/Utils";
import * as dav from "../../services/DavRequests";
import { IPhoto } from "../../types";
import { getPreviewUrl } from "../../services/FileUtils";

import LeftMoveIcon from "vue-material-design-icons/ChevronLeft.vue";
import RightMoveIcon from "vue-material-design-icons/ChevronRight.vue";

interface IYear {
  year: number;
  url: string;
  preview: IPhoto;
  photos: IPhoto[];
  text: string;
}

export default defineComponent({
  name: "OnThisDay",
  components: {
    NcActions,
    NcActionButton,
    LeftMoveIcon,
    RightMoveIcon,
  },

  props: {
    viewer: {
      type: Object,
      required: false,
    },
  },

  data: () => ({
    getPreviewUrl,
    years: [] as IYear[],
    hasRight: false,
    hasLeft: false,
    scrollStack: [] as number[],
  }),

  mounted() {
    const inner = this.$refs.inner as HTMLElement;
    inner.addEventListener("scroll", this.onScroll.bind(this), {
      passive: true,
    });

    this.refresh();
  },

  methods: {
    onload() {
      this.$emit("load");
    },

    async refresh() {
      // Look for cache
      const dayIdToday = utils.dateToDayId(new Date());
      const cacheUrl = `/onthisday/${dayIdToday}`;
      const cache = await utils.getCachedData<IPhoto[]>(cacheUrl);
      if (cache) this.process(cache);

      // Network request
      const photos = await dav.getOnThisDayRaw();
      utils.cacheData(cacheUrl, photos);

      // Check if exactly same as cache
      if (
        cache?.length === photos.length &&
        cache.every((p, i) => p.fileid === photos[i].fileid)
      )
        return;
      this.process(photos);
    },

    async process(photos: IPhoto[]) {
      this.years = [];

      let currentYear = 9999;
      let currentText = "";

      for (const photo of photos) {
        const dateTaken = utils.dayIdToDate(photo.dayid);
        const year = dateTaken.getUTCFullYear();
        photo.key = `${photo.fileid}`;

        // DateTime calls are expensive, so check if the year
        // itself is different first, then also check the text
        if (year !== currentYear) {
          const text = utils.getFromNowStr(dateTaken);
          if (text !== currentText) {
            this.years.push({
              year,
              text,
              url: "",
              preview: null,
              photos: [],
            });
            currentText = text;
          }
          currentYear = year;
        }

        const yearObj = this.years[this.years.length - 1];
        yearObj.photos.push(photo);
      }

      // For each year, randomly choose 10 photos to display
      for (const year of this.years) {
        year.photos = utils.randomSubarray(year.photos, 10);
      }

      // Choose preview photo
      for (const year of this.years) {
        // Try to prioritize landscape photos on desktop
        if (globalThis.windowInnerWidth <= 600) {
          const landscape = year.photos.filter((p) => p.w > p.h);
          year.preview = utils.randomChoice(landscape);
        }

        // Get random photo
        year.preview ||= utils.randomChoice(year.photos);
        year.url = getPreviewUrl(year.preview, false, 512);
      }

      await this.$nextTick();
      this.onScroll();
      this.onload();
    },

    moveLeft() {
      const inner = this.$refs.inner as HTMLElement;
      inner.scrollBy(-(this.scrollStack.pop() || inner.clientWidth), 0);
    },

    moveRight() {
      const inner = this.$refs.inner as HTMLElement;
      const innerRect = inner.getBoundingClientRect();
      const nextChild = Array.from(inner.children)
        .map((c) => c.getBoundingClientRect())
        .find((rect) => rect.right > innerRect.right);

      let scroll = nextChild
        ? nextChild.left - innerRect.left
        : inner.clientWidth;
      scroll = Math.min(
        inner.scrollWidth - inner.scrollLeft - inner.clientWidth,
        scroll
      );
      this.scrollStack.push(scroll);
      inner.scrollBy(scroll, 0);
    },

    onScroll() {
      const inner = this.$refs.inner as HTMLElement;
      if (!inner) return;
      this.hasLeft = inner.scrollLeft > 0;
      this.hasRight =
        inner.clientWidth + inner.scrollLeft < inner.scrollWidth - 20;
    },

    click(year: IYear) {
      const allPhotos = this.years.flatMap((y) => y.photos);
      this.viewer.openStatic(year.preview, allPhotos, 512);
    },
  },
});
</script>

<style lang="scss" scoped>
$height: 200px;
$mobHeight: 150px;

.outer {
  width: calc(100% - 50px);
  height: $height;
  overflow: hidden;
  position: relative;
  padding: 0 calc(28px * 0.6);

  // Sloppy: ideally this should be done in Timeline
  // to put a gap between the title and this
  margin-top: 10px;

  .inner {
    height: calc(100% + 20px);
    white-space: nowrap;
    overflow-x: scroll;
    scroll-behavior: smooth;
    border-radius: 10px;
  }

  :deep .dir-btn button {
    transform: scale(0.6);
    box-shadow: var(--color-main-text) 0 0 3px 0 !important;
    background-color: var(--color-main-background) !important;
  }

  .left-btn {
    position: absolute;
    top: 50%;
    left: 0;
    transform: translate(-10%, -50%);
  }

  .right-btn {
    position: absolute;
    top: 50%;
    right: 0;
    transform: translate(10%, -50%);
  }

  @media (max-width: 768px) {
    width: 98%;
    padding: 0;
    .inner {
      padding: 0 8px;
    }
    .dir-btn {
      display: none;
    }
  }
  @media (max-width: 600px) {
    height: $mobHeight;
  }
}

.group {
  height: $height;
  aspect-ratio: 4/3;
  display: inline-block;
  position: relative;
  cursor: pointer;

  &:not(:last-of-type) {
    margin-right: 8px;
  }

  img {
    cursor: inherit;
    object-fit: cover;
    border-radius: 10px;
    background-color: var(--color-background-dark);
    background-clip: padding-box, content-box;
  }

  .overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.2);
    border-radius: 10px;
    display: flex;
    align-items: end;
    justify-content: center;
    color: white;
    font-size: 1.2em;
    padding: 5%;
    white-space: normal;
    cursor: inherit;
    transition: background-color 0.2s ease-in-out;
  }

  &:hover .overlay {
    background-color: transparent;
  }

  @media (max-width: 600px) {
    aspect-ratio: 3/4;
    height: $mobHeight;
    .overlay {
      font-size: 1.1em;
    }
  }
}
</style>
