package dev.isxander.yacl3.dsl import dev.isxander.yacl3.api.* import net.minecraft.locale.Language import net.minecraft.network.chat.Component fun YetAnotherConfigLib(namespace: String, block: YACLDsl.() -> Unit): YetAnotherConfigLib { val context = YACLDslContext(namespace) context.block() return context.build() } class YACLDslContext( private val namespace: String, private val builder: YetAnotherConfigLib.Builder = YetAnotherConfigLib.createBuilder() ) : YACLDsl { private val categoryMap = LinkedHashMap() private val categoryDslReferenceMap = mutableMapOf>() override val namespaceKey = "yacl3.config.$namespace" private var used = false private var built: YetAnotherConfigLib? = null private var saveFunction: () -> Unit = {} override val categories = object : YACLDslReference { override fun get(): YetAnotherConfigLib? = built override operator fun get(id: String): FutureValue = FutureValue.Impl(categoryMap[id]?.groups).also { categoryDslReferenceMap[id] = it } override fun registering(block: CategoryDsl.() -> Unit): RegisterableDelegateProvider { return RegisterableDelegateProvider({ id, configuration -> category(id, configuration) }, block) } override val isBuilt: Boolean get() = built != null } init { title(Component.translatable("$namespaceKey.title")) } override fun title(component: Component) { builder.title(component) } override fun title(block: () -> Component) { title(block()) } override fun category(id: String, block: CategoryDsl.() -> Unit): ConfigCategory { val context = YACLDslCategoryContext(id, this) context.block() categoryMap[id] = context categoryDslReferenceMap[id]?.value = context.groups val built = context.build() builder.category(built) return built } override fun save(block: () -> Unit) { val oldSaveFunction = saveFunction saveFunction = { // allows stacking of save functions oldSaveFunction() block() } } fun build(): YetAnotherConfigLib { if (used) error("Cannot use the same DSL context twice!") used = true builder.save(saveFunction) return builder.build().also { built = it } } } class YACLDslCategoryContext( private val id: String, private val root: YACLDslContext, private val builder: ConfigCategory.Builder = ConfigCategory.createBuilder(), ) : CategoryDsl { private val groupMap = LinkedHashMap() private val groupDslReferenceMap = mutableMapOf>() val categoryKey = "${root.namespaceKey}.$id" private var built: ConfigCategory? = null private val rootGroup: YACLDslGroupContext = YACLDslGroupContext(id, this, builder.rootGroupBuilder(), root = true) override val groups = object : CategoryDslReference { override fun get(): ConfigCategory? = built override fun get(id: String): FutureValue = FutureValue.Impl(groupMap[id]?.options).also { groupDslReferenceMap[id] = it } override val root: GroupDslReference get() = rootGroup.options override fun registering(block: GroupDsl.() -> Unit): RegisterableDelegateProvider { return RegisterableDelegateProvider({ id, configuration -> group(id, configuration) }, block) } override val isBuilt: Boolean get() = built != null } override val options = rootGroup.options init { builder.name(Component.translatable("$categoryKey.title")) } override fun name(component: Component) { builder.name(component) } override fun name(block: () -> Component) { name(block()) } override fun group(id: String, block: GroupDsl.() -> Unit): OptionGroup { val context = YACLDslGroupContext(id, this) context.block() groupMap[id] = context groupDslReferenceMap[id]?.value = context.options return context.build().also { builder.group(it) } } override fun option(id: String, block: OptionDsl.() -> Unit): Option = rootGroup.option(id, block) override fun tooltip(vararg component: Component) { builder.tooltip(*component) } override fun tooltipBuilder(block: TooltipBuilderDsl.() -> Unit) { val builder = TooltipBuilderDsl.Delegate { builder.tooltip(it) } builder.block() } override fun useDefaultTooltip(lines: Int) { if (lines == 1) { builder.tooltip(Component.translatable("$categoryKey.tooltip")) } else for (i in 1..lines) { builder.tooltip(Component.translatable("$categoryKey.tooltip.$i")) } } fun build(): ConfigCategory { return builder.build().also { built = it } } } class YACLDslGroupContext( private val id: String, private val category: YACLDslCategoryContext, private val builder: OptionGroup.Builder = OptionGroup.createBuilder(), private val root: Boolean = false, ) : GroupDsl { private val optionMap = LinkedHashMap>() private val optionDslReferenceMap = mutableMapOf>>() val groupKey = "${category.categoryKey}.$id" private var built: OptionGroup? = null override val options = object : GroupDslReference { override fun get(): OptionGroup? = built override fun get(id: String): FutureValue> = FutureValue.Impl(optionMap[id]).flatMap { it.option as FutureValue> }.also { optionDslReferenceMap[id] = it as FutureValue.Impl> } override fun registering(block: OptionDsl.() -> Unit): RegisterableDelegateProvider, Option> { return RegisterableDelegateProvider({ id, configuration -> option(id, configuration) }, block) } override val isBuilt: Boolean get() = built != null } override fun name(component: Component) { builder.name(component) } override fun name(block: () -> Component) { name(block()) } override fun descriptionBuilder(block: OptionDescription.Builder.() -> Unit) { builder.description(OptionDescription.createBuilder().apply(block).build()) } override fun description(description: OptionDescription) { builder.description(description) } init { if (!root) { builder.name(Component.translatable("$groupKey.name")) } } override fun option(id: String, block: OptionDsl.() -> Unit): Option { val context = YACLDslOptionContext(id, this) context.block() optionMap[id] = context return context.build().also { optionDslReferenceMap[id]?.value = it builder.option(it) } } override fun useDefaultDescription(lines: Int) { descriptionBuilder { if (lines == 1) { text(Component.translatable("$groupKey.description")) } else for (i in 1..lines) { text(Component.translatable("$groupKey.description.$i")) } } } fun build(): OptionGroup { return builder.build().also { built = it } } } class YACLDslOptionContext( private val id: String, private val group: YACLDslGroupContext, private val builder: Option.Builder = Option.createBuilder() ) : Option.Builder by builder, OptionDsl { val optionKey = "${group.groupKey}.$id" private var built: Option? = null private val taskQueue = ArrayDeque<(Option) -> Unit>() override val option = FutureValue.Impl>() init { name(Component.translatable("$optionKey.name")) } override fun OptionDescription.Builder.addDefaultDescription(lines: Int?) { if (lines != null) { if (lines == 1) { text(Component.translatable("$optionKey.description")) } else for (i in 1..lines) { text(Component.translatable("$optionKey.description.$i")) } } else { // loop until we find a key that doesn't exist var i = 1 while (i < 100) { val key = "$optionKey.description.$i" if (Language.getInstance().has(key)) { text(Component.translatable(key)) } i++ } } } override fun build(): Option { return builder.build().also { built = it option.value = it while (taskQueue.isNotEmpty()) { taskQueue.removeFirst()(it) } } } }