<script setup>
import PostComponent from '@/components/PostComponent.vue'
import { usePostsStore } from '@/stores/posts.js'
import { nextTick, onMounted, ref } from 'vue'
import WelcomeComponent from '@/components/WelcomeComponent.vue'
import { useRoute, useRouter } from 'vue-router'

const postsStore = usePostsStore()
const route = useRoute()
const router = useRouter()

const menu = ref(null)
const menuItems = ref([])

const fullpage = ref(null)
let fpMouseInScrollableElement = false
let fpIsScrolling = false
let fpCurrentlyInView = 0
let fpTouchScroll = null
let fpTouchScrollX = 0
let fpTouchScrollY = 0
let fpResizeTimeout = false

onMounted(async () => {
  await postsStore.fetchPosts()
  await nextTick()
  fullpageScrollToAnchor(window.location.hash.substring(1))
})

window.addEventListener('popstate', (event) => {
  fullpageScrollToAnchor(window.location.hash.substring(1))
})

window.addEventListener('resize', (event) => {
  clearTimeout(fpResizeTimeout)
  fpResizeTimeout = setTimeout(fullpageScrollToAnchor(window.location.hash.substring(1)), 500)
})

function onFullpageWheel(event) {
  if (fpMouseInScrollableElement) return
  if (fpIsScrolling) return

  // prevent scroll
  event.preventDefault()
  event.stopPropagation()

  fullpageScrollDir(event.deltaY > 0 ? 1 : -1)
}

document.addEventListener('keydown', (event) => {
  if (event.key === 'ArrowUp' || event.key === 'PageUp') fullpageScrollDir(-1)
  else if (event.key === 'ArrowDown' || event.key === ' ' || event.key === 'PageDown')
    fullpageScrollDir(1)
  else return

  event.preventDefault()
  event.stopPropagation()
})

// touch screen support
function onFullpageTouchStart(event) {
  // more than one finger, not a scroll
  if (fpTouchScroll) {
    fpTouchScroll = null
    return
  }

  const touch = event.changedTouches.item(0)
  fpTouchScrollX = touch.clientX
  fpTouchScrollY = touch.clientY
  fpTouchScroll = touch
}

function onFullpageTouchMove(event) {
  if (fpMouseInScrollableElement) {
    return
  }
  event.preventDefault()
  event.stopPropagation()
}

function onFullpageTouchEnd(event) {
  if (fpMouseInScrollableElement) {
    fpMouseInScrollableElement = false
    return
  }
  if (fpIsScrolling) return

  const touch = event.changedTouches.item(0)
  const endTouchX = touch.clientX
  const endTouchY = touch.clientY

  const diffX = endTouchX - fpTouchScrollX
  const diffY = endTouchY - fpTouchScrollY

  if (Math.abs(diffY) > Math.abs(diffX)) {
    // small diff, not worth scroll
    if (Math.abs(diffY) < 10) {
      fpTouchScroll = null
      return
    }
    if (diffY > 0) fullpageScrollDir(-1)
    else fullpageScrollDir(1)
  }

  fpTouchScroll = null
}

// actual scrolling
function fullpageScrollDir(direction) {
  if (direction < 0 && fpCurrentlyInView <= 0) return
  if (direction > 0 && fpCurrentlyInView >= fullpage.value.children.length - 1) return

  fullpageScrollToIndex(fpCurrentlyInView + direction)
}

function fullpageScrollToAnchor(target) {
  for (let i = 0; i < fullpage.value.children.length; i++) {
    const slide = fullpage.value.children[i]
    if (slide.dataset.anchor === target) {
      fullpageScrollToIndex(i)
      break
    }
  }
}

const postComponents = ref([])
function fullpageScrollToIndex(target) {
  fpIsScrolling = true
  if (fpCurrentlyInView !== 0) {
    const from = postComponents.value[fpCurrentlyInView - 1]
    if (Object.hasOwn(from, 'onInvisible')) from.onInvisible()
  }
  if (target !== 0) {
    const to = postComponents.value[target - 1]
    if (Object.hasOwn(to, 'onVisible')) to.onVisible()
  }
  const to = fullpage.value.children[target]
  onLeave(null, to.dataset.anchor)
  fullpage.value.scroll({
    top: window.innerHeight * target,
    left: 0,
    behavior: 'smooth'
  })
  router.push({ name: route.name, hash: '#' + to.dataset.anchor })
  fpCurrentlyInView = target
  for (let i = 0; i < menu.value.children.length; i++) {
    const menuItem = menu.value.children[i]
    if (menuItem.dataset.menuanchor === to.dataset.anchor) {
      menuItem.classList.add('active')
    } else {
      menuItem.classList.remove('active')
    }
  }
  setTimeout(() => {
    fpIsScrolling = false
  }, 500)
}

function onFullpageScrollEnd() {
  fpIsScrolling = false
}

function fullpageOnScrollableElementEnter() {
  fpMouseInScrollableElement = true
}
function fullpageOnScrollableElementLeave() {
  fpMouseInScrollableElement = false
}

// eslint-disable-next-line no-unused-vars
function onLeave(origin, destination) {
  if (destination === 'welcome-section') {
    menu.value.classList.add('opacity-0')
    menu.value.classList.remove('scrollable-element')
  } else {
    menu.value.classList.remove('opacity-0')
    menu.value.classList.add('scrollable-element')

    // Disgusting way to make the menu automatically scroll when changing slide.
    // For some reason this doesn't work on Chrome, I love web dev
    for (const menuItem of menuItems.value) {
      if (menuItem.dataset.menuanchor === destination) {
        menuItem.scrollIntoView({
          behavior: 'smooth',
          inline: 'center'
        })
        break
      }
    }
  }
}
</script>

<template>
  <div
    id="menu-grid"
    class="fixed z-50 h-dvh w-full grid grid-cols-5 grid-rows-3 place-items-stretch pointer-events-none"
  >
    <ul
      id="menu"
      class="col-start-1 col-span-full row-start-1 max-h-32 lg:max-h-52 h-fit mt-28 sm:mt-16 lg:m-0 lg:col-start-5 lg:col-span-1 lg:row-start-3 lg:row-span-1 pointer-events-auto overflow-y-scroll overscroll-y-contain transition-opacity duration-1000 opacity-0"
      ref="menu"
      @mouseenter="fullpageOnScrollableElementEnter"
      @touchstart="fullpageOnScrollableElementEnter"
      @mouseleave="fullpageOnScrollableElementLeave"
    >
      <li
        :data-menuanchor="'post-' + post.id"
        v-for="post in postsStore.posts"
        :key="post.id"
        class="m-2 backdrop-blur-sm rounded-lg bg-black/10 hover:bg-gray-500/10 transition-colors duration-200"
        ref="menuItems"
      >
        <a
          :href="'#post-' + post.id"
          class="block text-right px-4 py-3 transition-all duration-300"
          >{{ post.formatedDate }}</a
        >
      </li>
    </ul>
  </div>
  <div
    class="fixed w-screen h-dvh overflow-y-scroll overscroll-y-contain"
    ref="fullpage"
    @wheel="onFullpageWheel"
    @touchstart="onFullpageTouchStart"
    @touchend="onFullpageTouchEnd"
    @scrollend="onFullpageScrollEnd"
    @touchmove="onFullpageTouchMove"
  >
    <WelcomeComponent class="w-screen h-dvh" />
    <PostComponent
      v-for="post in postsStore.posts"
      :key="post.id"
      :post="post"
      @scrollableelemententer="fullpageOnScrollableElementEnter"
      @scrollableelementleave="fullpageOnScrollableElementLeave"
      ref="postComponents"
    />
  </div>
</template>

<style scoped>
#menu li.active,
#menu li.active:hover {
  background-color: rgba(255, 255, 255, 0.1);
}

#menu li.active a,
#menu li.active:hover a:hover {
  font-weight: bold;
}
</style>
