package mg.dot.feufaro.solfa

class PMarkers(val code: String, val offset: Int = 0, var boundaries: Int = 0) {
    fun changeBoundaries(newBoundaries: Int) {
        boundaries = newBoundaries
    }
}
class PTemplate (val template: String, val separatorAfter: String, private val markers: MutableList<PMarkers> = mutableListOf()){
    var lastCalledMarker = 0
    fun resetCalledMarker() {
        lastCalledMarker = 0
    }
    fun hasMarker(marker: String): Boolean {
        return markers.any { it.code == marker }
    }
    fun callNextMarker(): PMarkers? {
        if (markers.size <= lastCalledMarker) return null
        val boundaries = if (markers.size > lastCalledMarker+1) {
            markers[lastCalledMarker+1].offset
        } else 0
        val pMarkers = markers[lastCalledMarker]
        lastCalledMarker++
        pMarkers.changeBoundaries(boundaries)
        return pMarkers
    }
    fun markerToString(): String {
        if (markers.isEmpty()) {
            return ""
        }
        return markers.joinToString(separator = "") {
            val prefix = if (it.offset <= 0) "" else " ".repeat(it.offset) //@todo better position the marker
            prefix + it.code.replace(Regex("^\\$\\{([abd-z]:)?(.*)\\}"), "$2")
                .replace("\$Q", "\uD834\uDD10")
                .replace(Regex("^[<=>]$"), "")
                .replace($$"$T", "")
                .replace(Regex("^c:(.*)"), "Do dia $1")
        }
    }
    fun markerToList(): List<String> {
        if (markers.isEmpty()) {
            return emptyList()
        }
        val mString = markerToString()
        return parseMarkers(mString)
    }
    fun parseMarkers(rawText: String): List<String> {
        val tokens = mutableListOf<String>()
        if (rawText.isBlank()) return tokens

        var remaining = rawText.replace(",", " ")
            .replace(Regex("""\b(DC)\b""", RegexOption.IGNORE_CASE), "D.C.")
            .replace(Regex("""\b(DS)\b""", RegexOption.IGNORE_CASE), "D.S.")
            .replace(Regex("""(D\.?C\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.C.Fin")
            .replace(Regex("""(D\.?S\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.S.Fin")
            .replace(Regex("""(D\.?C\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.C.alCoda")
            .replace(Regex("""(D\.?S\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.S.alCoda")

        val allStaticMarkers = getAllMarker()
        val tempoRegex = Regex("""^(♩\s*=\s*\d+)""", RegexOption.IGNORE_CASE)
        val modulaRegex = Regex("""^(D(?:ô|o)?\s*dia\s*(?:C|Db|D|Eb|E|F|Gb|G|Ab|A|Bb|B))""", RegexOption.IGNORE_CASE)

        //  Lexer
        while (remaining.isNotEmpty()) {
            remaining = remaining.trimStart()
            if (remaining.isEmpty()) break

            val tempoMatch = tempoRegex.find(remaining)
            if (tempoMatch != null) {
                tokens.add(tempoMatch.value)
                remaining = remaining.substring(tempoMatch.value.length)
                continue
            }

            val modulaMatch = modulaRegex.find(remaining)
            if (modulaMatch != null) {
                tokens.add(modulaMatch.value)
                remaining = remaining.substring(modulaMatch.value.length)
                continue
            }

            val matchedMarker = allStaticMarkers.find { remaining.startsWith(it, ignoreCase = true) }
            if (matchedMarker != null) {
                tokens.add(matchedMarker)
                remaining = remaining.substring(matchedMarker.length)
            } else {
                val unknownChunk = remaining.takeWhile { !it.isWhitespace() }
                tokens.add(unknownChunk)
                remaining = remaining.substring(unknownChunk.length)
            }
        }
        return tokens
    }

    fun hasKeyChange(): String {
        var returnFun = ""
        markers.map {
            val regexFound = Regex("^\\$\\{c:(..?)\\}").find(it.code)
            if (regexFound != null) {
                returnFun = regexFound.groupValues[1]
            }
            true
        }
        return returnFun
    }

}