package audio.funkwhale.ffa.repositories import android.content.Context import audio.funkwhale.ffa.utils.AppContext import audio.funkwhale.ffa.utils.Cache import audio.funkwhale.ffa.utils.CacheItem import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Job import kotlinx.coroutines.flow.* import java.io.BufferedReader import kotlin.math.ceil interface Upstream { fun fetch(size: Int = 0): Flow> } abstract class Repository> { protected val scope: CoroutineScope = CoroutineScope(Job() + IO) enum class Origin(val origin: Int) { Cache(0b01), Network(0b10) } data class Response(val origin: Origin, val data: List, val page: Int, val hasMore: Boolean) abstract val context: Context? abstract val cacheId: String? abstract val upstream: Upstream open fun cache(data: List): C? = null protected open fun uncache(reader: BufferedReader): C? = null fun fetch(upstreams: Int = Origin.Cache.origin and Origin.Network.origin, size: Int = 0): Flow> = flow { if (Origin.Cache.origin and upstreams == upstreams) fromCache().collect { emit(it) } if (Origin.Network.origin and upstreams == upstreams) fromNetwork(size).collect { emit(it) } } private fun fromCache() = flow { cacheId?.let { cacheId -> Cache.get(context, cacheId)?.let { reader -> uncache(reader)?.let { cache -> return@flow emit(Response(Origin.Cache, cache.data, ceil(cache.data.size / AppContext.PAGE_SIZE.toDouble()).toInt(), false)) } } return@flow emit(Response(Origin.Cache, listOf(), 1, false)) } }.flowOn(IO) private fun fromNetwork(size: Int) = flow { upstream .fetch(size) .map { response -> Response(Origin.Network, onDataFetched(response.data), response.page, response.hasMore) } .collect { response -> emit(Response(Origin.Network, response.data, response.page, response.hasMore)) } } protected open fun onDataFetched(data: List) = data }