// SPDX-FileCopyrightText: 2026 Reactorcoremeltdown
// SPDX-License-Identifier: GPL-3.0-or-later

package space.rcmd.android.sizzle.io

import space.rcmd.android.sizzle.model.ToolboxSlot
import space.rcmd.android.sizzle.model.ToolboxType

/**
 * Reads and writes a single instrument/effect preset as plain, human-readable
 * text. The format is deliberately trivial so a person can hand-edit it and a
 * machine can parse it with two lines of code:
 *
 *     SIZZLE-PRESET 1
 *     TYPE=NES_SYNTH
 *     NAME=Lead
 *     wave=Pulse25
 *     attack=0.01
 *     ...
 *
 * The same routine is used for the clipboard copy/paste in the Toolbox tab and
 * for the on-disk preset library.
 */
object PresetIo {
    const val HEADER = "SIZZLE-PRESET 1"

    /** Serialize a slot's device + parameters to text. */
    fun export(slot: ToolboxSlot): String {
        val type = slot.type ?: return "$HEADER\nTYPE=EMPTY"
        return buildString {
            appendLine(HEADER)
            appendLine("TYPE=${type.name}")
            appendLine("NAME=${slot.name}")
            // Emit params in the type's declared order for stable, readable files.
            type.params.forEach { spec ->
                appendLine("${spec.key}=${slot.string(spec.key, spec.default.toString())}")
            }
            // Emit any extra string params the engine stored (e.g. samplePath).
            slot.values.keys
                .filter { key -> type.params.none { it.key == key } }
                .forEach { key -> appendLine("$key=${slot.values[key]}") }
        }
    }

    /**
     * Parse preset [text] into [slot], overwriting its current contents. Unknown
     * or malformed lines are ignored so a partially-edited file still loads.
     * @return true if a valid device type was found and applied.
     */
    fun import(slot: ToolboxSlot, text: String): Boolean {
        val lines = text.lineSequence().map { it.trim() }.filter { it.isNotEmpty() }
        val map = LinkedHashMap<String, String>()
        var type: ToolboxType? = null
        for (line in lines) {
            if (line == HEADER || !line.contains('=')) continue
            val (key, value) = line.split('=', limit = 2)
            when (key) {
                "TYPE" -> type = ToolboxType.entries.firstOrNull { it.name == value }
                "NAME" -> slot.name = value
                else -> map[key] = value
            }
        }
        val t = type ?: return false
        slot.fill(t)                       // reset to defaults for the type ...
        slot.name = slot.name.ifBlank { t.displayName }
        map.forEach { (k, v) -> slot.set(k, v) }  // ... then apply the saved values
        return true
    }
}
