<script setup lang="ts">
import type { UseOptionalWidgetStateReturn } from '@/lib/useOptionalWidgetState'
import type { UseZoomAxis } from '@/lib/useZoomAxis'
import { useElementBounding, useScroll } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { useId, useTemplateRef, watch } from 'vue'
import ZoomSlider from '@/components/library/ZoomSlider.vue'
import ScrollSync from '@/components/scrollsync/ScrollSync.vue'
import TimelineHeader from '@/components/timeline/header/TimelineHeader.vue'
import Playhead from '@/components/timeline/Playhead.vue'
import { onInputKeyStroke } from '@/lib/onInputKeyStroke'
import { useTimelineScrubbing } from '@/lib/useTimelineScrubbing'
import { useVeiwportWheel } from '@/lib/useVeiwportWheel'
import { bindTwoWay, toPx } from '@/lib/vue'
import { useTimelineStore } from '@/store/TimelineStore'
import TimelineMarkers from './markers/TimelineMarkers.vue'
import TimelineTrackHeader from './TimelineTrackHeader.vue'
import TimelineTrackView from './TimelineTrackView.vue'

const {
  rightSidebar,
} = defineProps<{
  rightSidebar: UseOptionalWidgetStateReturn
}>()

const timeline = useTimelineStore()

const {
  headerHeight,
  sidebarWidth,
  viewportScrollOffsetTop,
  viewportScrollOffsetLeft,
  contentWidthIncludingEmptySpacePx,
  visibleTracks,
} = storeToRefs(timeline)
// nested composable marked with markRaw
const viewportZoomHorizontal = timeline.viewportZoomHorizontal as any as UseZoomAxis
const viewportZoomVertical = timeline.viewportZoomVertical as any as UseZoomAxis

const timelineScrollGroup = useId()

const timelineRootElement = useTemplateRef('timelineRootElement')
const timelineScrollView = useTemplateRef<InstanceType<typeof ScrollSync>>('timelineScrollView')
const timelineScrollViewBounding = useElementBounding(timelineScrollView)
watch(timelineScrollViewBounding.width, (value) => {
  timeline.viewportWidth = value
})
watch(timelineScrollViewBounding.height, (value) => {
  timeline.viewportHeight = value
})
const {
  arrivedState: timelineScrollViewArrivedState,
  x: timelineScrollViewOffsetLeft,
  y: timelineScrollViewOffsetTop,
} = useScroll(() => timelineScrollView.value?.$el)

bindTwoWay(timelineScrollViewOffsetTop, viewportScrollOffsetTop)
bindTwoWay(timelineScrollViewOffsetLeft, viewportScrollOffsetLeft)

useVeiwportWheel(timelineRootElement, {
  axisHorizontal: viewportZoomHorizontal,
  axisVertical: viewportZoomVertical,
  scrollOffsetLeft: timelineScrollViewOffsetLeft,
})

// Shift+Z - reset zoom
onInputKeyStroke(event => event.shiftKey && (event.key === 'Z' || event.key === 'z'), (event) => {
  timeline.zoomToggleBetweenWholeAndLoop()
  event.preventDefault()
})

const scrubbing = useTemplateRef('scrubbing')
useTimelineScrubbing(scrubbing)
</script>

<template>
  <div
    ref="timelineRootElement" class="tw:w-full tw:grid tw:gap-0" :style="{
      'grid-template-columns': `${toPx(sidebarWidth)} 1fr ${rightSidebar.visible.value ? rightSidebar.width.string.value : ''}`,
      'grid-template-rows': `${toPx(headerHeight)} 1fr`,
    }"
  >
    <!-- top left corner, contains zoom controls -->
    <div
      class="toolbar-background tw:max-w-full tw:flex tw:flex-row tw:flex-nowrap tw:items-center"
      style="grid-row: 1; grid-column: 1; border-right: var(--view-separator-border); border-bottom: var(--view-separator-border);"
    >
      <ZoomSlider :axis="viewportZoomHorizontal" class="tw:flex-1" />
    </div>

    <!-- left sidebar with timeline track names -->
    <ScrollSync
      :group="timelineScrollGroup" :vertical="true" class="toolbar-background scrollbar-none"
      style="grid-row: 2; grid-column: 1; border-right: var(--view-separator-border);"
    >
      <template v-for="timelineTrack in visibleTracks" :key="timelineTrack.name">
        <TimelineTrackHeader :timeline-track />
      </template>
    </ScrollSync>

    <!-- header with timestamps -->
    <ScrollSync
      :group="timelineScrollGroup" :horizontal="true" class="timeline-background scrollbar-none tw:relative"
      style="grid-row: 1; grid-column: 2; border-bottom: var(--view-separator-border);"
    >
      <div ref="scrubbing" class="tw:relative tw:h-full" :style="{ width: contentWidthIncludingEmptySpacePx }">
        <TimelineHeader />
      </div>

      <!-- <Playhead :positionSeconds="timeline.playheadPosition"> -->
      <!-- <Timestamp :seconds="cursorPositionSeconds ?? 0" :beats="cursorPositionBeats ?? 0" v-if="isDragging" /> -->
      <!-- </Playhead> -->
    </ScrollSync>

    <!-- timeline content -->
    <ScrollSync
      ref="timelineScrollView" :group="timelineScrollGroup" :horizontal="true" :vertical="true"
      class="tw:size-full timeline-background tw:relative" style="grid-row: 2; grid-column: 2;"
    >
      <!-- timeline content wrapper for good measure -->
      <div
        class="tw:relative tw:overflow-hidden tw:min-h-full"
        :style="{ width: timeline.contentWidthIncludingEmptySpacePx }"
      >
        <!-- timeline markers -->
        <TimelineMarkers />

        <!-- timeline tracks -->
        <div>
          <template v-for="timelineTrack in visibleTracks" :key="timelineTrack.name">
            <TimelineTrackView :timeline-track />
          </template>
        </div>
      </div>
    </ScrollSync>

    <!-- horizontal bars of scroll shadow, on top of sidebar and content, but under playhead -->
    <div class="tw:size-full tw:relative tw:pointer-events-none" style="grid-row: 2; grid-column: 1 / 3;">
      <div
        class="tw:absolute tw:top-0 tw:left-0 tw:h-0 tw:w-full"
        :class="{ 'tw:invisible': timelineScrollViewArrivedState.top }"
      >
        <div class="tw:h-4 tw:w-full shadow-bottom" />
      </div>

      <div
        class="tw:absolute tw:bottom-4 tw:left-0 tw:h-0 tw:w-full"
        :class="{ 'tw:invisible': timelineScrollViewArrivedState.bottom }"
      >
        <div class="tw:h-4 tw:w-full shadow-top" />
      </div>
    </div>

    <!-- playhead -->
    <ScrollSync
      :group="timelineScrollGroup" :horizontal="true" class="tw:size-full tw:pointer-events-none"
      style="grid-row: 1 / 3; grid-column: 2;"
    >
      <div
        class="tw:h-full tw:relative tw:overflow-hidden"
        :style="{ width: timeline.contentWidthIncludingEmptySpacePx }"
      >
        <!-- actuals playback position -->
        <Playhead :position-seconds="timeline.playheadPosition" :knob="true">
          <!-- <Timestamp :seconds="cursorPositionSeconds ?? 0" :beats="cursorPositionBeats ?? 0" v-if="isDragging" /> -->
        </Playhead>
      </div>
    </ScrollSync>

    <!-- cursor on hover -->
    <!-- <Playhead :position="cursorPosition" :timelineWidth="timelineWidth" :knob="false"
      :hidden="cursorPositionSeconds === null || isDragging">
      <Timestamp :seconds="cursorPositionSeconds ?? 0" :beats="cursorPositionBeats ?? 0" />
    </Playhead> -->

    <!-- vertical bars of scroll shadow, on top of header, content AND playhead -->
    <div class="tw:size-full tw:relative tw:pointer-events-none" style="grid-row: 1 / -1; grid-column: 2;">
      <div
        class="tw:absolute tw:top-0 tw:left-0 tw:w-0 tw:h-full"
        :class="{ 'tw:invisible': timelineScrollViewArrivedState.left }"
      >
        <div class="tw:w-4 tw:h-full shadow-right" />
      </div>

      <div
        class="tw:absolute tw:top-0 tw:right-4 tw:w-0 tw:h-full"
        :class="{ 'tw:invisible': timelineScrollViewArrivedState.right }"
      >
        <div class="tw:w-4 tw:h-full shadow-left" />
      </div>
    </div>

    <!-- empty cell at the top right -->
    <div
      v-if="rightSidebar.visible.value" class="toolbar-background"
      style="grid-row: 1; grid-column: 3; border-bottom: var(--view-separator-border); border-left: var(--view-separator-border);"
    />

    <!-- right sidebar with vertical zoom slider -->
    <div
      v-if="rightSidebar.visible.value"
      class="toolbar-background tw:size-full tw:min-h-0 tw:py-2 tw:flex tw:flex-col tw:items-center"
      style="grid-row: 2; grid-column: 3; border-left: var(--view-separator-border);"
    >
      <ZoomSlider :axis="viewportZoomVertical" orientation="vertical" class="tw:w-full tw:min-h-0" />
    </div>
  </div>
</template>

<style scoped>
.shadow-top,
.shadow-right,
.shadow-bottom,
.shadow-left {
  --shadow-darkest: rgba(0, 0, 0, 0.3);
}

.shadow-top {
  background-image: linear-gradient(to top, var(--shadow-darkest) 0%, rgba(0, 0, 0, .0) 100%);
}

.shadow-right {
  background-image: linear-gradient(to right, var(--shadow-darkest) 0%, rgba(0, 0, 0, .0) 100%);
}

.shadow-bottom {
  background-image: linear-gradient(to bottom, var(--shadow-darkest) 0%, rgba(0, 0, 0, .0) 100%);
}

.shadow-left {
  background-image: linear-gradient(to left, var(--shadow-darkest) 0%, rgba(0, 0, 0, .0) 100%);
}
</style>
