package me.eater.threedom.gl

class ShaderPreProcessor {
    private val regex = Regex("(?<module>[^:]+)::(?<call>[^:(]+)\\((?<args>.+)\\)")
    private val calls: MutableMap<Pair<String, String>, (List<String>) -> String> = mutableMapOf()

    fun process(input: String): String =
        input.split("\n").joinToString("\n") {
            if (it.startsWith("#[") && it.endsWith("]")) {
                val macro = it.substring(2 until it.length - 1)
                val match = regex.find(macro) ?: return@joinToString it
                val module = match.groups["module"]!!.value
                val call = match.groups["call"]!!.value
                val args = match.groups["args"]!!.value.split(",").map { arg -> arg.trim() }.toList()

                calls[module to call]?.invoke(args) ?: throw RuntimeException("Can't find macro $module::$call")
            } else {
                it
            }
        }

    fun register(module: String, call: String, block: (List<String>) -> String) {
        calls[module to call] = block
    }

    fun compile(source: String, type: Shader.ShaderType): Shader {
        return Shader.create(process(source), type)
    }

    companion object {
        fun createDefault() = ShaderPreProcessor().apply {
            register("3dom", "import") { it ->
                val knownUniforms = mapOf(
                    "model" to "mat4",
                    "view" to "mat4",
                    "projection" to "mat4",
                    "normalModel" to "mat3"
                )

                it.joinToString("\n") { uni ->
                    "uniform ${knownUniforms[uni]} $uni;"
                }
            }
        }
    }
}
