package fr.username404.snowygui.config import com.typesafe.config.Config import com.typesafe.config.ConfigException import com.typesafe.config.ConfigFactory.* import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigValueFactory import fr.username404.snowygui.ClickGui import fr.username404.snowygui.Snowy import fr.username404.snowygui.Snowy.Companion.MissingComponent import fr.username404.snowygui.gui.elements.ClickBox import fr.username404.snowygui.gui.feature.Category import fr.username404.snowygui.gui.feature.Macro import io.github.config4k.extract import io.github.config4k.getValue import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import net.minecraft.client.Minecraft import java.io.File import kotlin.reflect.KProperty object Configuration { @Deprecated("Use the getValue or setValue methods instead", level = DeprecationLevel.ERROR) @JvmStatic val ModifiableValues: MutableMap by lazy { mutableMapOf( "enabledFeatures" to enabledFeatures, "macros" to lazy { (ClickGui.components.find { it is ClickBox && it.isCategory(Category.MACROS) } as ClickBox).buttons.map { Json.encodeToString(it as Macro) } }, "box_colors" to mapOf(*ClickGui.clickBoxes.filter { box -> (box.name.key != MissingComponent.key) && Category.fromBox(box)?.categoryColor != box.color }.map { it.name.key to it.color }.toTypedArray()), ) } private fun Config.withFullModifiableValues() = @Suppress("DEPRECATION_ERROR") ModifiableValues.entries.fold(this) { previous, entry -> previous.withValue(entry.key, entry.value.let { ConfigValueFactory.fromAnyRef( if (it !is Lazy<*>) ConfigValueFactory.fromAnyRef(it) else it.value ) } ) } private val configDirectory: String = (Minecraft.getInstance().gameDirectory.absolutePath + File.separator + "config").also { File(it).mkdir() } private val file: File = File(configDirectory + File.separator + "snowy.conf") private suspend fun writeConfig(c: Config) = coroutineScope { launch(start = CoroutineStart.UNDISPATCHED) { file.writeText( """ |Snowy { |${ c.root().render( ConfigRenderOptions.defaults() .setFormatted(true) .setJson(false) .setOriginComments(false) ).prependIndent(" ").trimEnd() } |} """.trimMargin() ) } } operator fun invoke() = obtained inline operator fun getValue(ref: Any?, property: KProperty<*>): T = @Suppress("DEPRECATION_ERROR") if (ModifiableValues.containsKey(property.name)) ModifiableValues[property.name] as T else invoke().getValue(ref, property) operator fun setValue(ref: Any?, property: KProperty<*>, value: T) = @Suppress("DEPRECATION_ERROR") ModifiableValues.setValue(ref, property, value) private val obtained: Config = run { var result: Config = empty() with(file) { if (!exists()) { createNewFile() setWritable(true) setReadable(true) } else try { result = parseFile(file) } catch (e: ConfigException) { Snowy.logs.warn("Could not parse the snowy configuration file, the default configuration will be used instead.") } } load(result).withFallback( parseString( """ |Snowy { | displayInitMessage = false | macros = [] | riskyCheats = false | sortAlphabetically = true |} """.trimMargin() ) ).extract("Snowy") }.also { runBlocking { writeConfig(it) } } val enabledFeatures = mutableMapOf().apply { "enabledFeatures".let { obtained.run { if (hasPath(it)) { putAll(extract(it)) } } } } internal val foundMacros: Set = run { obtained.getStringList("macros").map { Json.decodeFromString(it) }.toSet() } init { Runtime.getRuntime().addShutdownHook( Thread { runBlocking { ClickGui.boxContext { enabledFeatures.putAll(buttons.map { it.title to it.toggled }) } writeConfig(obtained.withFullModifiableValues()).join() } } ) } }