<script setup>
import { selectedDate, changeDate, changeDay } from "../store";
import { Swiper, SwiperSlide } from "swiper/vue";
import { Virtual } from "swiper";
import { ref, watch } from "vue";
import { getDateSkippingWeekend } from "../util";
import ScrollableContainer from "../components/scrollable-container.vue";

defineProps({
  element: {
    required: true,
  },
});

// Get swiper element
const swiperModules = ref([Virtual]);
const swiperElement = ref();
function setSwiper(swiper) {
  swiperElement.value = swiper;
}

// Load current, previous and next day
const loadedDates = ref([
  getDateSkippingWeekend(new Date(selectedDate.value.getTime() - 86400000)),
  new Date(selectedDate.value.getTime()),
  getDateSkippingWeekend(new Date(selectedDate.value.getTime() + 86400000)),
]);

// Load left or right date on slide change
function slideChange(swiper) {
  const activeSlide = swiper.activeIndex;
  // Only trigger data refresh if date is different
  const newSelectedDate = loadedDates.value[activeSlide];
  if (selectedDate.value.getTime() != newSelectedDate.getTime())
    selectedDate.value = newSelectedDate;
  // Check if current slide is the last one
  if (activeSlide == loadedDates.value.length - 1) {
    // Append next day to loadedDates
    const lastDate = loadedDates.value.at(-1);
    loadedDates.value.push(getDateSkippingWeekend(calculateDate(lastDate, 1)));
  }
}
function slideChangeEnd(swiper) {
  const activeSlide = swiper.activeIndex;
  // Check if the current slide is the first one
  if (activeSlide == 0) {
    // Prepend the previous day to loadedDates
    // and slide to the next slide without transition
    // so the same day as before is shown
    const lastDate = loadedDates.value[0];
    loadedDates.value = [
      getDateSkippingWeekend(calculateDate(lastDate, -1)),
      ...loadedDates.value,
    ];
    swiper.slideTo(activeSlide + 1, 0);
  }
}
// Also check if a day needs to be prepended
// if the user releases the card later because
// a different event is fired for that
function slideResetEnd(swiper) {
  slideChangeEnd(swiper);
}

// Helper functions
function calculateDate(date, offset) {
  return new Date(date.getTime() + 86400000 * offset);
}
function calculateSlide(date) {
  return loadedDates.value.findIndex((e) => e.getTime() == date.getTime());
}
function slideToDate(date) {
  const slide = calculateSlide(date);
  if (slide != -1) swiperElement.value.slideTo(slide);
  else {
    // If the slide is not loaded yet reset
    // loaded dates and load that date
    loadedDates.value = [calculateDate(date, -1), date, calculateDate(date, 1)];
    swiperElement.value.slideTo(1, 0);
  }
  selectedDate.value = date;
}

// Watch for slide change instructions
watch(changeDay, (change) => {
  if (change == 0) return;
  const calculatedDate = calculateDate(selectedDate.value, change);
  slideToDate(getDateSkippingWeekend(calculatedDate));
  changeDay.value = 0;
});
watch(changeDate, (date) => {
  slideToDate(new Date(date.setUTCHours(0, 0, 0, 0)));
});
</script>

<template>
  <swiper
    :modules="swiperModules"
    :slides-per-view="1"
    :space-between="50"
    :initial-slide="1"
    :virtual="true"
    @slide-change-transition-end="slideChangeEnd"
    @slide-reset-transition-end="slideResetEnd"
    @slide-change="slideChange"
    @swiper="setSwiper"
  >
    <swiper-slide
      v-for="(date, index) in loadedDates"
      :key="index"
      :virtualIndex="index"
    >
      <ScrollableContainer>
        <component :is="element" :date="date" />
      </ScrollableContainer>
    </swiper-slide>
  </swiper>
</template>

<style>
@import "swiper/css";
</style>

<style scoped>
.swiper {
  padding: 5px 10px 0px 10px;
  z-index: 0;
  height: 100%;
  box-sizing: border-box;
}
</style>
