package mg.dot.feufaro.solfa

class ParseULine (var line: String, var measure: Int) {
    private var cursorX = 0
    var parsedString = ""
    private var afterDollar = false
    private var inComment = false
    private var charX = ' '
    companion object {
        val mapXLate = mutableMapOf<Char, List<String>>(
            '1' to listOf(","   , "."     , ","   , ":"),
            '2' to listOf("."   , ".-,"   , ":"   , ":-,"),
            '3' to listOf(".-," , ".-:"   , ":-," , ":-."),
            '4' to listOf(":"   , ".-;-," , ":-." , ":-.-,"),
            'y' to listOf(".,D:", ".-,:D,", ":,D.", ":-,.D,"),
            't' to listOf("DD", "DD", "DD", "DD")
        )
        var blankDuration: Int = 0
    }

    fun parsed() : String {
        val matchResult = Regex("^z([048CEKO]):(.+)$").find(line)
        if (matchResult != null) {
            val capturedChar = matchResult.groups[1]!!.value
            cursorX = when (capturedChar) {
                "E" -> 14
                "C" -> 12
                "K" -> 20
                "O" -> 24
                else -> capturedChar.toInt()
            }
            blankDuration = cursorX
            line = matchResult.groups[2]!!.value
            parsedString = ":"
            if (capturedChar == "0") {
                parsedString = "|"
            }
            if (capturedChar == "8") {
                parsedString = "!"
            }
        }
        parseUStage1()
        parseUStage3()
        return parsedString
    }

    private fun parseUStage1() {
        val parsedChars = line
            // Beware, Android Regex requires "}" to be escaped!!!
            .replace(Regex("\\$\\{x([1-9]\\d*)\\}(.*?)\\$\\{x0\\}")) { matchResult ->
                val (nbIteration, iterable) = matchResult.destructured
                val nTimes = nbIteration.toInt()
                iterable.repeat(nTimes)
            }/*.replace("y", "2z11")*/.toCharArray()

        var actualComment = ""
        parsedChars.forEach {
            if (inComment) {
                parsedString += it
                if (it == '}') {
                    inComment = false
                    val matchMeasure = Regex("^m:(\\d+)(/.*)?").find(actualComment)
                    if (matchMeasure != null) {
                        measure = matchMeasure.groups[1]!!.value.toInt()
                    }
                    actualComment = ""
                } else {
                    actualComment += it
                }
                return@forEach
            }
            if (afterDollar) {
                if (it == '{') {
                    inComment = true
                }
                afterDollar = false
                parsedString += it
                return@forEach
            }
            if (charX != 'z') {
                charX = 'D'
            }
            when (it) {
                '$' -> run {
                    afterDollar = !afterDollar
                    parsedString += "$"
                }
                'z', '-' -> run {
                    charX = it
                }
                'y' -> run {
                    parseUStage2('2')
                    charX = 'z'
                    parseUStage2('1')
                    charX = 'D'
                    parseUStage2('1')
                }
                'W' -> repeat(8) { parseUStage2('4')}
                'S' -> repeat(7) { parseUStage2('4')}
                'O' -> repeat(6) { parseUStage2('4')}
                'K' -> repeat(5) { parseUStage2('4')}
                'G' -> repeat(4) { parseUStage2('4')  }
                'E' -> run {
                    parseUStage2('4')
                    parseUStage2('4')
                    parseUStage2('4')
                    parseUStage2('2')
                }
                'C' -> repeat(3) { parseUStage2('4') }
                'B' -> run {
                    parseUStage2('4')
                    parseUStage2('4')
                    parseUStage2('3')
                }
                '8' -> repeat(2) { parseUStage2('4') }
                '4', '3', '2', '1', 't' -> parseUStage2(it)
                '6' -> repeat(2) { parseUStage2('3')}
                '9' -> repeat(3) { parseUStage2('3')}
                'A' -> run {
                    parseUStage2('4')
                    parseUStage2('4')
                    parseUStage2('2')
                }
                '(', ')', '/' -> parsedString += it
            }
            if (it != 'z') {
                charX = '-'
            }
        }
    }
    private fun parseUStage2(codeChar : Char) {
        val indexByFour = cursorX % 4
        var sequence = mapXLate[codeChar]!![indexByFour]!!
        if (charX == 'z') {
            sequence = sequence.replace('-', charX)
        }
        if (codeChar == 't') {
            cursorX += 4
            sequence = $$"$TDD:"
        } else {
            cursorX += codeChar.toString().toInt()
        }
        if (sequence.contains(':')) {
            val bar: Int = (cursorX / 4) % measure
            var replacement = ':'
            if (   (bar == 2 && measure == 4 )
                || (bar == 3 && measure in listOf(6, 9, 12))
                || (bar in listOf(6, 9) && measure in listOf(9, 12)))
            {
                replacement = '!'
            }
            if (bar == 0) {
                replacement = '|'
            }
            if (replacement != ':') {
                sequence = sequence.replace(':', replacement)
            }
        }
        var noteChar = 'D'
        if (charX in listOf('z', '-')) {
            noteChar = charX
        }
        parsedString += noteChar + sequence
        if (charX != 'z') {
            charX = '-'
        }
    }
    private fun parseUStage3() {
        parsedString = parsedString.replace("-,-", "-")
            .replace(Regex("([:!|])-,-([:!|])"), "$1-$2")
            .replace(Regex("([:!|])-\\.-([:!|])"), "$1-$2")
            .replace(Regex(",-([:!|])"), "$1")
            .replace(Regex("(?<!D)\\.-([:!|])"), "$1")
            .replace(Regex("[\\.,]([:!|])"), "$1")
            .replace(Regex("([:!|])\\)/"), ")$1/")
            .replace(Regex("[:!|]/"), "/")
            .replace(Regex("/[:!|]"), "/")
            .replace(Regex("[z0]([:!|])"), "$1")
    }
}