package audio.funkwhale.ffa.utils import audio.funkwhale.ffa.model.Radio import audio.funkwhale.ffa.model.Track import com.google.android.exoplayer2.offline.Download import com.google.android.exoplayer2.offline.DownloadCursor import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch sealed class Command { class StartService(val command: Command) : Command() object RefreshService : Command() object ToggleState : Command() class SetState(val state: Boolean) : Command() object NextTrack : Command() object PreviousTrack : Command() class Seek(val progress: Int) : Command() class AddToQueue(val tracks: List) : Command() class AddToPlaylist(val tracks: List) : Command() class PlayNext(val track: Track) : Command() class ReplaceQueue(val queue: List, val fromRadio: Boolean = false) : Command() class RemoveFromQueue(val track: Track) : Command() class MoveFromQueue(val oldPosition: Int, val newPosition: Int) : Command() object ClearQueue : Command() object ShuffleQueue : Command() class PlayRadio(val radio: Radio) : Command() class SetRepeatMode(val mode: Int) : Command() class PlayTrack(val index: Int) : Command() class PinTrack(val track: Track) : Command() class PinTracks(val tracks: List) : Command() class RefreshTrack(val track: Track?) : Command() } sealed class Event { object LogOut : Event() class PlaybackError(val message: String) : Event() object PlaybackStopped : Event() class Buffering(val value: Boolean) : Event() class TrackFinished(val track: Track?) : Event() class StateChanged(val playing: Boolean) : Event() object QueueChanged : Event() object RadioStarted : Event() object ListingsChanged : Event() class DownloadChanged(val download: Download) : Event() } sealed class Request(var channel: Channel? = null) { object GetState : Request() object GetQueue : Request() object GetCurrentTrack : Request() object GetCurrentTrackIndex : Request() object GetDownloads : Request() } sealed class Response { class State(val playing: Boolean) : Response() class Queue(val queue: List) : Response() class CurrentTrack(val track: Track?) : Response() class CurrentTrackIndex(val index: Int) : Response() class Downloads(val cursor: DownloadCursor) : Response() } object EventBus { private var _events = MutableSharedFlow() val events = _events.asSharedFlow() fun send(event: Event) { GlobalScope.launch(IO) { _events.emit(event) } } fun get() = events } object CommandBus { private var _commands = MutableSharedFlow() var commands = _commands.asSharedFlow() fun send(command: Command) { GlobalScope.launch(IO) { _commands.emit(command) } } fun get() = commands } object RequestBus { // `replay` allows send requests before the PlayerService starts listening private var _requests = MutableSharedFlow(replay = 100) var requests = _requests.asSharedFlow() fun send(request: Request): Channel { return Channel().also { GlobalScope.launch(IO) { request.channel = it _requests.emit(request) } } } fun get() = requests } object ProgressBus { private var _progress = MutableStateFlow(Triple(0, 0, 0)) val progress = _progress.asStateFlow() fun send(current: Int, duration: Int, percent: Int) { _progress.value = Triple(current, duration, percent) } fun get() = progress } suspend inline fun Channel.wait(): T? { return when (val response = this.receive()) { is T -> response else -> null } }