package com.fabledsword.minstrel.player.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import androidx.compose.runtime.withFrameMillis
import kotlinx.coroutines.isActive

/**
 * Per-frame interpolated playhead. `positionMs` comes in at the
 * polling cadence of [com.fabledsword.minstrel.player.PlayerController]
 * (~500ms today) — using it directly for the scrubber paints a sudden
 * 500ms jump on each tick. This wraps it in a [produceState] that
 * resets to the canonical value on every emission and then advances
 * forward at 1ms/ms while the player is playing, so the displayed
 * position moves continuously at the natural playback rate.
 *
 * On track end / pause / seek the produceState's keys change and the
 * coroutine restarts from the new canonical value — so when the
 * canonical position changes by more than a frame can account for
 * (skip, scrub, seek), we snap instead of slewing across the bar.
 *
 * For paused state the position stays at the canonical value with no
 * extrapolation; for ended/durationMs<=0 the helper returns
 * `positionMs` unchanged.
 */
@Composable
fun rememberSmoothPositionMs(
    positionMs: Long,
    durationMs: Long,
    isPlaying: Boolean,
): State<Long> = produceState(
    initialValue = positionMs,
    key1 = positionMs,
    key2 = isPlaying,
    key3 = durationMs,
) {
    value = positionMs
    if (!isPlaying || durationMs <= 0L) return@produceState
    val startFrame = withFrameMillis { it }
    val startPos = positionMs
    while (isActive) {
        withFrameMillis { now ->
            val advanced = startPos + (now - startFrame)
            value = advanced.coerceAtMost(durationMs)
        }
    }
}
