<template>
  <div class="split-container" :class="headerClass">
    <div class="primary" ref="primary">
      <component :is="primary" />
    </div>

    <div
      class="separator"
      ref="separator"
      @pointerdown="sepDown"
      @touchmove.passive="sepTouchMove"
      @touchend.passive="pointerUp"
      @touchcancel.passive="pointerUp"
    ></div>

    <div class="timeline">
      <div class="timeline-header" ref="timelineHeader">
        <div class="swiper"></div>
        <div class="title">
          {{ t("memories", "{photoCount} photos", { photoCount }) }}
        </div>
      </div>
      <div class="timeline-inner">
        <Timeline @daysLoaded="daysLoaded" />
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import Timeline from "./Timeline.vue";
const MapSplitMatter = () => import("./top-matter/MapSplitMatter.vue");
import { emit } from "@nextcloud/event-bus";
import Hammer from "hammerjs";

export default defineComponent({
  name: "SplitTimeline",

  components: {
    Timeline,
  },

  data: () => ({
    pointerDown: false,
    primaryPos: 0,
    mobileOpen: 1,
    hammer: null as HammerManager,
    photoCount: 0,
  }),

  computed: {
    primary() {
      switch (this.$route.name) {
        case "map":
          return MapSplitMatter;
        default:
          return "None";
      }
    },

    headerClass() {
      switch (this.mobileOpen) {
        case 0:
          return "m-zero";
        case 1:
          return "m-one";
        case 2:
          return "m-two";
      }
    },
  },

  mounted() {
    // Set up hammerjs hooks
    this.hammer = new Hammer(this.$refs.timelineHeader as HTMLElement);
    this.hammer.get("swipe").set({
      direction: Hammer.DIRECTION_VERTICAL,
      threshold: 3,
    });
    this.hammer.on("swipeup", this.mobileSwipeUp);
    this.hammer.on("swipedown", this.mobileSwipeDown);
  },

  beforeDestroy() {
    this.pointerUp();
    this.hammer.destroy();
  },

  methods: {
    isVertical() {
      return false; // for future
    },

    sepDown(event: PointerEvent) {
      this.pointerDown = true;

      // Get position of primary element
      const primary = <HTMLDivElement>this.$refs.primary;
      const rect = primary.getBoundingClientRect();
      this.primaryPos = this.isVertical() ? rect.top : rect.left;

      // Let touch handle itself
      if (event.pointerType === "touch") return;

      // Otherwise, handle pointer events on document
      document.addEventListener("pointermove", this.documentPointerMove);
      document.addEventListener("pointerup", this.pointerUp);

      // Prevent text selection
      event.preventDefault();
      event.stopPropagation();
    },

    sepTouchMove(event: TouchEvent) {
      if (!this.pointerDown) return;
      this.setFlexBasis(event.touches[0]);
    },

    documentPointerMove(event: PointerEvent) {
      if (!this.pointerDown || !event.buttons) return this.pointerUp();
      this.setFlexBasis(event);
    },

    pointerUp() {
      // Get rid of listeners on document quickly
      this.pointerDown = false;
      document.removeEventListener("pointermove", this.documentPointerMove);
      document.removeEventListener("pointerup", this.pointerUp);
      emit("memories:window:resize", null);
    },

    setFlexBasis(pos: { clientX: number; clientY: number }) {
      const ref = this.isVertical() ? pos.clientY : pos.clientX;
      const newWidth = Math.max(ref - this.primaryPos, 50);
      (<HTMLDivElement>this.$refs.primary).style.flexBasis = `${newWidth}px`;
    },

    daysLoaded({ count }: { count: number }) {
      this.photoCount = count;
    },

    async mobileSwipeUp() {
      this.mobileOpen = Math.min(this.mobileOpen + 1, 2);

      // When swiping up, immediately emit a resize event
      // so that we can prepare in advance for showing more photos
      // on the timeline
      await this.$nextTick();
      emit("memories:window:resize", null);
    },

    async mobileSwipeDown() {
      this.mobileOpen = Math.max(this.mobileOpen - 1, 0);

      // When swiping down, wait for the animation to end, so that
      // we don't hide the lower half of the timeline before the animation
      // ends. Note that this is necesary: the height of the timeline inner
      // div is also animated to the smaller size.
      await new Promise((resolve) => setTimeout(resolve, 300));
      emit("memories:window:resize", null);
    },
  },
});
</script>

<style lang="scss" scoped>
.split-container {
  width: 100%;
  height: 100%;
  display: flex;
  overflow: hidden;

  > div {
    height: 100%;
    max-height: 100%;
  }

  > .primary {
    flex-basis: 60%;
    flex-shrink: 0;
  }

  > .timeline {
    flex-basis: auto;
    flex-grow: 1;
    padding-left: 8px;
    overflow: hidden;
    display: flex;
    flex-direction: column;

    > .timeline-header {
      position: relative;
      display: block;
      height: 50px;
      flex-shrink: 0;
      flex-grow: 0;
      border-bottom: 1px solid var(--color-border-dark);

      .swiper {
        display: none;
      }

      > .title {
        width: 100%;
        height: 100%;
        text-align: center;
        font-weight: 500;
        padding-top: 12px;
      }
    }

    > .timeline-inner {
      flex-grow: 1;
      overflow: hidden;
    }
  }

  > .separator {
    flex-grow: 0;
    flex-shrink: 0;
    width: 5px;
    background-color: gray;
    opacity: 0.1;
    cursor: col-resize;
    margin: 0 0 0 auto;
    transition: opacity 0.4s ease-out, background-color 0.4s ease-out;
  }

  > .separator:hover {
    opacity: 0.4;
    background-color: var(--color-primary);
  }
}

@media (max-width: 768px) {
  /**
   * On mobile the layout works completely differently
   * Both components are full-height, and the separatator
   * brings up the timeline to cover up the primary component
   * fully when dragged up.
   */
  .split-container {
    display: block;

    > div {
      position: absolute;
      width: 100%;
      background-color: var(--color-main-background);
    }

    .primary {
      height: 50%;
      will-change: height;
    }

    > .separator {
      display: none;
    }

    > .timeline {
      height: 50%;
      padding-left: 0;
      will-change: transform, height;
      transition: transform 0.2s ease, height 0.2s ease;
      border-top-left-radius: 20px;
      border-top-right-radius: 20px;

      > .timeline-header {
        height: 58px;

        > .swiper {
          display: block;
          position: absolute;
          left: 50%;
          top: 0;
          transform: translate(-50%, 9px);
          width: 22px;
          height: 4px;
          border-radius: 40px;
          background-color: var(--color-text-light);
          z-index: 1;
          opacity: 0.75;
          pointer-events: none;
        }

        > .title {
          font-size: 1.4em;
          padding-top: 22px;
        }
      }
    }

    &.m-zero > .timeline {
      transform: translateY(calc(200% - 60px)); // show attribution
    }
    &.m-zero > .primary {
      height: calc(100% - 60px); // show full map
    }

    &.m-one > .timeline {
      transform: translateY(100%); // show half map
    }

    &.m-two > .timeline {
      transform: translateY(0); // show timeline
      height: 100%;
    }
  }
}
</style>
