<script setup lang="ts">
import { shallowRef, useTemplateRef } from 'vue';

// Any card with a subtle outline and hover effect
const {
  hoverEnabled = true,
  playheadEnabled = true,
  selected = false,
} = defineProps<{
  hoverEnabled?: boolean,
  playheadEnabled?: boolean,
  selected?: boolean,
}>();

const emit = defineEmits<{
  (e: 'select'): void;
  (e: 'activate'): void;
  (e: 'playhead', pos: number): void;
}>();

// Timeline / playhead position on hover in range 0..1,
// or NaN when not hovered or hover is not enabled.
const playheadPosition01 = shallowRef<number>(NaN);
const playheadEl = useTemplateRef('playheadEl');
const card = useTemplateRef('card');

// Simply tracks pointer enter/leave
const isHovered = shallowRef(false);
// flag to detect a recent touch tap so the following click doesn't also emit 'select'
const lastTapWasTouch = shallowRef(false);
// show playhead on touch while pressing
const isTouchActive = shallowRef(false);
// Once dragging starts, pointer should no longer cause click on release/up
const isTouchDragging = shallowRef(false);
// Playhead is active on mouse hover or touch down, but not after touch up which leaves dangling :hover on mobile.

defineExpose({
  playheadPosition01,
});

// Returns false if playhead wasn't updated
function updatePlayhead(event: PointerEvent): boolean {
  const target = event.currentTarget as HTMLElement | null;
  if (!hoverEnabled || !playheadEnabled || !target) {
    return false;
  }

  const rect = target.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const pos = rect.width > 0 ? Math.max(0, Math.min(1, x / rect.width)) : 0;
  playheadPosition01.value = pos;

  if (playheadEl.value) {
    // position the 1px playhead using percentage so it adapts to responsive widths
    playheadEl.value.style.left = `${pos * 100}%`;
  }
  // emit normalized position for parent components to react (e.g. preview color)
  emit('playhead', pos);
  return true;
}

function onPointerEnter(_event: PointerEvent) {
  if (hoverEnabled) {
    isHovered.value = true;
  }
}

function onPointerLeave(_event: PointerEvent) {
  if (hoverEnabled) {
    isHovered.value = false;
  }
  isTouchActive.value = false;
  isTouchDragging.value = false;
  playheadPosition01.value = NaN;
  if (playheadEl.value) {
    playheadEl.value.style.left = "";
  }
  emit('playhead', NaN);
}

function onPointerDown(event: PointerEvent) {
  if (event.pointerType !== 'touch') return;
  if (updatePlayhead(event)) {
    isTouchActive.value = true;
  }
  if (card.value) {
    card.value.setPointerCapture(event.pointerId);
  }
}

function onPointerUp(event: PointerEvent) {
  // Treat a single touch tap as activation
  if (event.pointerType === 'touch') {
    if (!isTouchDragging.value) {
      emit('activate');
    }
    lastTapWasTouch.value = true;
    // keep the flag true briefly so the subsequent click handler can suppress 'select'
    setTimeout(() => { lastTapWasTouch.value = false; }, 50);
    // clear touch-active playhead state
    isTouchActive.value = false;
    isTouchDragging.value = false;
    onPointerLeave(event);
  }
}

function onPointerMove(event: PointerEvent) {
  updatePlayhead(event);
  if (event.pointerType === 'touch') {
    isTouchDragging.value = true;
  }
}

function onClick(event: MouseEvent) {
  // If this click follows a touch tap, suppress the 'select' event
  if (lastTapWasTouch.value) {
    event.preventDefault();
    lastTapWasTouch.value = false;
    return;
  }
  emit('select');
}

function onDblClick(_event: MouseEvent) {
  emit('activate')
}
</script>
<template>
  <div ref="card" class="card card-border tw:min-w-10 tw:min-h-10 tw:grid" :class="{
    'hover-enabled': hoverEnabled,
    'playhead-enabled': hoverEnabled && playheadEnabled,
    'playhead-active': isHovered || isTouchActive,
    selected,
  }" @pointerenter="onPointerEnter" @pointerleave="onPointerLeave" @pointerdown="onPointerDown"
    @pointerup="onPointerUp" @pointermove="onPointerMove" @click="onClick" @dblclick="onDblClick"
    @focusin="emit('select')">
    <!-- content container -->
    <div class="tw:row-span-full tw:col-span-full tw:w-full tw:h-full">
      <slot />
    </div>
    <!-- playhead container -->
    <div class="playhead-container tw:pointer-events-none tw:row-span-full tw:col-span-full">
      <div ref="playheadEl" class="playhead"></div>
    </div>
  </div>
</template>

<style scoped>
.card {
  color: var(--inactive-text-color);
  background-color: var(--card-background-color);
  overflow: hidden;
  touch-action: pan-y;
}

.card.hover-enabled:hover,
.card.selected {
  color: var(--active-text-color);
}

.card.hover-enabled:hover {
  outline: 2px solid var(--card-outline-color);
}

.card.selected,
.card.selected:hover {
  outline: 2px solid var(--card-outline-selected-color);
}

.playhead-container {
  display: none;
  position: relative;
}

.card.playhead-enabled.playhead-active .playhead-container {
  display: unset;
}

.playhead {
  width: 1px;
  height: 100%;
  /* center the 1px line on the pointer */
  transform: translateX(-50%);
  background-color: var(--timeline-playhead-color);
  position: absolute;
  top: 0;
}
</style>
