package dev.stave.domain.renderer
import dev.stave.domain.model.DidlItem
import kotlinx.coroutines.flow.StateFlow
import kotlin.time.Duration
/**
* The thing that actually plays audio. v0.0.1 ships [dev.stave.playback.LocalRenderer] only.
* Cast (play flavor) and UPnP AV/OpenHome plug in behind this interface without UI changes.
*/
interface Renderer {
val state: StateFlow<PlaybackState>
suspend fun load(tracks: List<DidlItem.Track>, startIndex: Int = 0, autoplay: Boolean = true)
/** Append [tracks] to the end of the queue. If the queue is empty, they become the queue
* but are NOT auto-started — the user explicitly asked to queue, not to play. */
suspend fun enqueue(tracks: List<DidlItem.Track>)
/** Insert [tracks] immediately after the currently-playing track. If the queue is empty,
* the tracks become the queue AND playback begins. */
suspend fun playNext(tracks: List<DidlItem.Track>)
/** Jump directly to queue position [index] and start playing. Out-of-range is a no-op. */
suspend fun skipToQueueItem(index: Int)
/** Remove queue item at [index]. Removing the current item advances to the next; removing
* the last remaining item stops playback. */
suspend fun removeFromQueue(index: Int)
/** Reorder the queue by moving the item at [from] to position [to]. */
suspend fun moveInQueue(from: Int, to: Int)
/** Empty the queue and stop playback. */
suspend fun clearQueue()
suspend fun play()
suspend fun pause()
suspend fun stop()
suspend fun seekTo(position: Duration)
suspend fun next()
suspend fun previous()
suspend fun setVolume(volumePercent: Int)
/** Cycle repeat mode: OFF → ALL → ONE → OFF. */
suspend fun setRepeatMode(mode: RepeatMode)
/** Shuffle toggle. Turning on saves the current queue order; turning off restores it.
* Implementation is responsible for the save/restore — UI just flips the flag. */
suspend fun setShuffle(enabled: Boolean)
}
enum class RepeatMode { OFF, ALL, ONE }
data class PlaybackState(
val current: DidlItem.Track? = null,
val queue: List<DidlItem.Track> = emptyList(),
val queueIndex: Int = -1,
val isPlaying: Boolean = false,
val isBuffering: Boolean = false,
/** True when the user has explicitly stopped (or nothing has ever been loaded).
* Distinguishes from paused-mid-track. Used by the NowPlayingBar visibility rule:
* the bar shows while playing OR paused, but hides while stopped even if the queue
* still has tracks loaded. */
val isStopped: Boolean = true,
val position: Duration = Duration.ZERO,
val duration: Duration = Duration.ZERO,
val volumePercent: Int = 100,
val repeatMode: RepeatMode = RepeatMode.OFF,
val shuffleEnabled: Boolean = false,
val error: String? = null,
)