<template>
    <div
        ref="root"
        :class="bem()"
        @pointerenter="mouseEnter"
        @pointermove="mouseMove"
        @pointerleave="mouseLeave"
        @pointerdown="mouseDown"
        @pointerup="mouseUp"
    >
        <div :class="bem('scrubber')">
            <div
                v-if="!dragging"
                :class="bem('preview')"
                :style="{
                    width: cssPcnt(dragging ? progress : Math.max(progress, previewProgress)),
                }"
            ></div>
            <div
                :class="bem('progress')"
                :style="{
                    width: cssPcnt(
                        previewing && !dragging ? Math.min(previewProgress, progress) : progress,
                    ),
                }"
            ></div>
        </div>
    </div>
</template>

<script setup>
import { ref, watch } from 'vue';
import useVModelRef from '../composables/useVModelRef.js';

// NAME

defineOptions({
    name: 'px-scrubber',
});

//  PROPS

const props = defineProps({
    /**
     * If true, the user can drag the scrubber to change the progress.
     */
    dragEnabled: { type: Boolean, default: true },

    /**
     * Progress baby!
     */
    progress: { type: Number, default: null },
});

// EVENTS

const emit = defineEmits(['update:progress', 'seek', 'dragstart', 'dragend']);

// V-MODEL

const progress = useVModelRef(props, 'progress', emit);

// REFS

const dragging = ref(false);
const previewing = ref(false);
const previewProgress = ref(0);
const root = ref(null);

// IMPLEMENTATION

watch(progress, nv => {
    emit('seek', nv);
});

function mouseEnter() {
    previewing.value = true;
}

function mouseLeave() {
    previewing.value = false;
    previewProgress.value = 0;
}

function mouseDown(e) {
    if (props.dragEnabled) {
        dragging.value = true;
        root.value.setPointerCapture(e.pointerId);
        emit('dragstart');
    }

    progress.value = Math.max(0, Math.min(1, e.offsetX / root.value.offsetWidth));
}

function mouseUp(e) {
    if (dragging.value) {
        dragging.value = false;
        root.value.releasePointerCapture(e.pointerId);
        emit('dragend');
    }
    e.stopPropagation();
    e.preventDefault();
}

function mouseMove(e) {
    const p = Math.max(0, Math.min(1, e.offsetX / root.value.offsetWidth));

    if (dragging.value) {
        progress.value = p;
        previewProgress.value = p;
        emit('seek', { progress: progress.value });
    } else if (previewing) {
        previewProgress.value = p;
    }

    e.stopPropagation();
    e.preventDefault();
}
</script>

<style lang="scss">
@use '../styles/px.scss' as *;

@at-root :root {
    --px-scrubber-height: 4px;
    --px-scrubber-bg: #{clr(accent, $alpha: 0.1)};
    --px-scrubber-padding: 1em 0;
    --px-scrubber-progress-bg: #{clr(accent, 1)};
    --px-scrubber-preview-bg: #{clr(accent, $alpha: 0.33)};
}

.px-scrubber {
    width: 100%;
    padding: var(--px-scrubber-padding);
    cursor: pointer;

    &__scrubber {
        position: relative;
        width: 100%;
        height: var(--px-scrubber-height);
        background: var(--px-scrubber-bg);
    }

    &__progress,
    &__preview {
        position: absolute;
        left: 0;
        top: 0;
        height: 100%;
    }

    &__preview {
        background: var(--px-scrubber-preview-bg);
    }

    &__progress {
        background: var(--px-scrubber-progress-bg);
    }
}
</style>
