package com.vgmlr.grain
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
internal const val HISTORY_MIN = 0
internal const val HISTORY_MAX = 50
internal const val HISTORY_DEFAULT = 25
internal const val PAD_MIN = 0
internal const val PAD_MAX = 150
internal const val PAD_DEFAULT = 0
internal const val DEC_MIN = 0
internal const val DEC_MAX = 12
internal const val DEC_DEFAULT = 4
internal const val KEY_AUTO = 0
internal const val KEY_RANGE = 15
private const val HISTORY_SEP = "\n"
enum class Bias(val label: String) {
LEFT("Left"), CENTER("Center"), RIGHT("Right"), FULL("Full");
val padded: Boolean get() = this == LEFT || this == RIGHT
}
enum class Side(val label: String) { LEFT("Left"), RIGHT("Right") }
enum class Digits(val label: String) { MONO("Mono"), SANS("Sans") }
private val Context.store: DataStore<Preferences> by preferencesDataStore("grain")
class GrainPrefs(context: Context) {
private val ds = context.applicationContext.store
private val kBias = stringPreferencesKey("bias")
private val kDigits = stringPreferencesKey("digits")
private val kMax = intPreferencesKey("history_max")
private val kHistory = stringPreferencesKey("history")
private val kPad = intPreferencesKey("edge_pad")
private val kOps = stringPreferencesKey("ops_side")
private val kDec = intPreferencesKey("decimals")
private val kKey = intPreferencesKey("key_size")
val bias: Flow<Bias> = ds.data.map { p ->
Bias.entries.firstOrNull { it.name == p[kBias] } ?: Bias.LEFT
}
val digits: Flow<Digits> = ds.data.map { p ->
Digits.entries.firstOrNull { it.name == p[kDigits] } ?: Digits.MONO
}
val historyMax: Flow<Int> = ds.data.map { it[kMax] ?: HISTORY_DEFAULT }
val history: Flow<List<String>> = ds.data.map { read(it) }
val edgePad: Flow<Int> = ds.data.map {
(it[kPad] ?: PAD_DEFAULT).coerceIn(PAD_MIN, PAD_MAX)
}
val opsSide: Flow<Side> = ds.data.map { p ->
Side.entries.firstOrNull { it.name == p[kOps] } ?: Side.RIGHT
}
val decimals: Flow<Int> = ds.data.map {
(it[kDec] ?: DEC_DEFAULT).coerceIn(DEC_MIN, DEC_MAX)
}
val keySize: Flow<Int> = ds.data.map {
(it[kKey] ?: KEY_AUTO).coerceAtLeast(KEY_AUTO)
}
suspend fun setBias(b: Bias) {
ds.edit { it[kBias] = b.name }
}
suspend fun setEdgePad(n: Int) {
ds.edit { it[kPad] = n.coerceIn(PAD_MIN, PAD_MAX) }
}
suspend fun setKeySize(n: Int) {
ds.edit { it[kKey] = n.coerceAtLeast(KEY_AUTO) }
}
suspend fun setOpsSide(s: Side) {
ds.edit { it[kOps] = s.name }
}
suspend fun setDecimals(n: Int) {
ds.edit { it[kDec] = n.coerceIn(DEC_MIN, DEC_MAX) }
}
suspend fun setDigits(d: Digits) {
ds.edit { it[kDigits] = d.name }
}
suspend fun setHistoryMax(n: Int) {
ds.edit { p ->
val max = n.coerceIn(HISTORY_MIN, HISTORY_MAX)
p[kMax] = max
p[kHistory] = read(p).takeLast(max).joinToString(HISTORY_SEP)
}
}
suspend fun addHistory(line: String) {
ds.edit { p ->
val max = p[kMax] ?: HISTORY_DEFAULT
if (max == 0) return@edit
p[kHistory] = (read(p) + line).takeLast(max).joinToString(HISTORY_SEP)
}
}
private fun read(p: Preferences): List<String> =
p[kHistory]?.split(HISTORY_SEP)?.filter { it.isNotEmpty() } ?: emptyList()
}