package com.github.apognu.otter.repositories import android.content.Context import com.github.apognu.otter.utils.Cache import com.github.apognu.otter.utils.CacheItem import com.github.apognu.otter.utils.untilNetwork import com.google.gson.Gson import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch import java.io.BufferedReader interface Upstream { fun fetch(size: Int = 0): Channel>? } abstract class Repository> { enum class Origin(val origin: Int) { Cache(0b01), Network(0b10) } data class Response(val origin: Origin, val data: List, val hasMore: Boolean) abstract val context: Context? abstract val cacheId: String? abstract val upstream: Upstream private var _channel: Channel>? = null private val channel: Channel> get() { if (_channel?.isClosedForSend ?: true) { _channel = Channel(10) } return _channel!! } 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): Channel> { if (Origin.Cache.origin and upstreams == upstreams) fromCache() if (Origin.Network.origin and upstreams == upstreams) fromNetwork(size) return channel } private fun fromCache() { GlobalScope.launch(IO) { cacheId?.let { cacheId -> Cache.get(context, cacheId)?.let { reader -> uncache(reader)?.let { cache -> channel.offer(Response(Origin.Cache, cache.data, false)) } } } } } private fun fromNetwork(size: Int) { upstream.fetch(size)?.untilNetwork(IO) { data, hasMore -> val data = onDataFetched(data) cacheId?.let { cacheId -> Cache.set( context, cacheId, Gson().toJson(cache(data)).toByteArray() ) } channel.offer(Response(Origin.Network, data, hasMore)) } } protected open fun onDataFetched(data: List) = data }