<script setup lang="ts">
import Playhead from '@/components/timeline/Playhead.vue';
// import Timestamp from '@/components/timeline/Timestamp.vue';
// import { dummyAudioTrackForTesting, secondsToBeats } from '@/lib/AudioTrack';
import ZoomSlider from '@/components/library/ZoomSlider.vue';
import ScrollSync from '@/components/scrollsync/ScrollSync.vue';
import TimelineHeader from '@/components/timeline/header/TimelineHeader.vue';
import { onInputKeyStroke } from '@/lib/onInputKeyStroke';
import { useOptionalWidgetState, type UseOptionalWidgetStateReturn } from '@/lib/useOptionalWidgetState';
import { bindTwoWay, toPx } from '@/lib/vue';
import { DEFAULT_ZOOM_HORIZONTAL, DEFAULT_ZOOM_VERTICAL, useTimelineStore } from '@/store/TimelineStore';
import { useTrackStore } from '@/store/TrackStore';
import { useElementBounding, useEventListener, useScroll } from '@vueuse/core';
import { storeToRefs } from 'pinia';
import { computed, useId, useTemplateRef, watch } from 'vue';
import TimelineTrackHeader from './TimelineTrackHeader.vue';
import TimelineTrackView from './TimelineTrackView.vue';
import TimelineMarkers from './markers/TimelineMarkers.vue';

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

const trackStore = useTrackStore();
const timeline = useTimelineStore();

const audioTrack = computed(() => trackStore.currentAudioTrack!);

const {
  headerHeight, sidebarWidth,
  viewportZoomHorizontal, viewportZoomVertical,
  viewportScrollOffsetTop, viewportScrollOffsetLeft,
} = storeToRefs(timeline);

// const visibleTracks = computed(() => timeline.visibleTracks.slice(0, 3));
const visibleTracks = computed(() => timeline.visibleTracks.slice(0, 10));

// const playbackPositionSeconds = defineModel<number | null>('playbackPositionSeconds', { default: null });
// const playbackPositionBeats = computed<number | null>(() => {
//   if (playbackPositionSeconds.value === null) {
//     return null;
//   }
//   return secondsToBeats(audioTrack.value!, playbackPositionSeconds.value);
// });
// const playbackPosition = computed<number>(() => {
//   if (playbackPositionSeconds.value === null) {
//     return 0;
//   }
//   return playbackPositionSeconds.value / timelineTotalDurationSeconds.value;
// });
//
// const cursorPositionSeconds = shallowRef<number | null>(0)
// const cursorPositionBeats = computed<number | null>(() => {
//   if (cursorPositionSeconds.value === null) {
//     return null;
//   }
//   return secondsToBeats(audioTrack.value!, cursorPositionSeconds.value);
// });
// const cursorPosition = computed<number>(() => {
//   if (cursorPositionSeconds.value === null) {
//     return 0;
//   }
//   return cursorPositionSeconds.value / timelineTotalDurationSeconds.value;
// });

// const timelineEl = useTemplateRef('timeline');
// 
// function _cursorToPositionSeconds(e: MouseEvent): number | null {
//   if (audioTrack.value === null || timelineEl.value === null) {
//     return null;
//   }
//   // clientX is broken in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=505521#c80
//   const x = e.pageX - timelineEl.value.offsetLeft;
//   const rect = timelineEl.value.getBoundingClientRect();
//   const position = x / rect.width;
//   const positionClamped = Math.max(0, Math.min(1, position));
//   return positionClamped * timelineTotalDurationSeconds.value;
// }
// 
// const isDragging = shallowRef(false);
// 
// function timelinePointerDown(event: PointerEvent) {
//   const tl = timelineEl.value;
//   if (tl && !isDragging.value) {
//     isDragging.value = true;
//     tl.setPointerCapture(event.pointerId);
//     timelinePointerMove(event);
//   }
// }
// function timelinePointerUp(event: PointerEvent) {
//   timelinePointerMove(event);
//   if (isDragging.value) {
//     isDragging.value = false;
//   }
// }
// function timelinePointerMove(event: PointerEvent) {
//   // preview mouse position
//   console.log("MOVE", cursorPositionSeconds.value);
//   cursorPositionSeconds.value = _cursorToPositionSeconds(event);
//   if (isDragging.value) {
//     // apply mouse position
//     playbackPositionSeconds.value = cursorPositionSeconds.value;
//   }
// }
// function timelinePointerLeave(_event: PointerEvent) {
//   console.log("LEAVE", isDragging.value);
//   if (!isDragging.value) {
//     cursorPositionSeconds.value = null;
//   }
// }


// Questionable thin vertical sidebar on the right, contains vertical zoom slider.
// Not sure I want this to remain, so used a boolean flag to hide.

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);

function scrollZoomHandler(event: WheelEvent) {
  // Note: Math.random() prevents console output history from collapsing same entries.
  // console.log("WHEEEEEL", Math.random().toFixed(3), event.deltaX, event.deltaY, event.target, event);

  // TODO: Ignore Ctrl key because it intercepts touchpad pinch to zoom?
  // TODO: what if the user doesn't use a touchpad, and thus has
  // no way to scroll horizontally other than by dragging a scrollbar?
  const ignoreCtrlWheel = false;

  if (event.shiftKey) {
    event.preventDefault();
    event.stopPropagation();
    event.stopImmediatePropagation();
    viewportZoomVertical.value -= event.deltaY / 100;
  }
  else if (event.altKey) {
    event.preventDefault();
    event.stopPropagation();
    event.stopImmediatePropagation();
    viewportZoomHorizontal.value -= event.deltaY / 100;
  }
  else if (event.ctrlKey && !ignoreCtrlWheel) {
    event.preventDefault();
    event.stopPropagation();
    event.stopImmediatePropagation();
    viewportScrollOffsetLeft.value += event.deltaY;
  }
}

useEventListener(timelineRootElement, "wheel", scrollZoomHandler, { passive: false, });

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

</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`,
  }" style="border-top: var(--view-separator-border);">

    <!-- 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 v-model:zoom="viewportZoomHorizontal" :default-zoom="DEFAULT_ZOOM_HORIZONTAL" extended
        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">
        <TimelineTrackHeader :timelineTrack />
      </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);">

      <TimelineHeader />

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

    </ScrollSync>

    <!-- TODO -->
    <!-- <div ref="timeline" class="timeline" @pointerdown="timelinePointerDown" @pointerup="timelinePointerUp"
    @pointermove="timelinePointerMove" @pointerleave="timelinePointerLeave"> -->

    <!-- 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">
            <TimelineTrackView :timelineTrack />
          </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>

      <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>
    </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.contentWidthPx }">

        <!-- actuals playback position -->
        <Playhead :positionSeconds="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>

      <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>
    </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);">
    </div>


    <!-- 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 v-model:zoom="viewportZoomVertical" orientation="vertical" :default-zoom="DEFAULT_ZOOM_VERTICAL"
        class="tw:w-full tw:min-h-0" />
    </div>

  </div>
</template>
<style scoped>
/* .timeline {
  background-color: var(--timeline-background-color);
  position: relative;
  user-select: none;
  touch-action: none;
  overflow: hidden;
} */

.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>
