package mg.dot.feufaro.solfa

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.TransformedText
import androidx.compose.ui.text.input.VisualTransformation

class SolfaVisualTransformation : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        val mapping = IntArray(text.text.length + 1)
        var currentOriginalIndex = 0

        val transformed = buildAnnotatedString {
            val lines = text.text.split("\n")
            val hasU0 = lines.any { it.startsWith("U0:") }
            val hasT0 = lines.any { it.startsWith("T0:") }

            lines.forEachIndexed { index, line ->
                val lineStartIndex = currentOriginalIndex
                val startLen = this.length // Longueur actuelle du AnnotatedString

                when {
                    line.startsWith("M0:") -> parseMetadataLine(line)
                    line.startsWith("U0:") || line.startsWith("T0:") -> parseControlLine(line)
                    line.startsWith("N") && line.getOrNull(2) == ':' -> parseNoteLine(line)
                    (line.startsWith("Y") || line.startsWith("E")) && (line.getOrNull(2) == ':' || line.getOrNull(3) == ':') -> parseLyricLine(line)
                    else -> append(line)
                }

                val lineTextLength = line.length
                val transformedLineLength = this.length - startLen

                for (i in 0 until lineTextLength) {
                    val transformedOffset = if (transformedLineLength == 0) startLen
                    else startLen + (i * transformedLineLength / lineTextLength)
                    mapping[lineStartIndex + i] = transformedOffset
                }

                currentOriginalIndex += lineTextLength

                if (index < lines.size - 1) {
                    mapping[currentOriginalIndex] = this.length
                    append("\n")
                    currentOriginalIndex++
                }
            }
            mapping[text.text.length] = this.length
        }

        val solfaOffsetMapping = object : OffsetMapping {
            override fun originalToTransformed(offset: Int): Int {
                return mapping.getOrElse(offset) { transformed.length }.coerceIn(0, transformed.length)
            }

            override fun transformedToOriginal(offset: Int): Int {
                var bestOriginal = 0
                for (i in mapping.indices) {
                    if (mapping[i] <= offset) bestOriginal = i
                    else break
                }
                return bestOriginal.coerceIn(0, text.text.length)
            }
        }

        return TransformedText(transformed, solfaOffsetMapping)
    }
}

// COULEURS DU THÈME
val ColorPrefixA = Color(0xFF6200EE) // Violet
val ColorPrefixB = Color(0xFF03DAC5) // Teal
val ColorSeparator = Color(0xFFB0A400) // Jaune
val ColorNoteOdd = Color(0xFF2196F3) // Bleu
val ColorNoteEven = Color(0xFFE91E63) // Rose
val ColorDigit = Color(0xFF4CAF50) // Vert
val ColorSpecial = Color(0xFFFF9800) // Orange (#)
val ColorValue = Color(0xFF757575) // Gris
val ColorMarker = Color(0xFF795548) // Marron

val MarkerRegex = Regex("\\$\\{.*?\\}|\\$[^ \n]")

private fun AnnotatedString.Builder.parseMetadataLine(line: String) {
    val parts = line.split(":")
    // M0:
    pushStyle(SpanStyle(color = ColorPrefixA, fontWeight = FontWeight.Bold))
    append(parts[0] + ":")
    pop()

    if (parts.size > 1) {
        val remaining = line.substring(parts[0].length + 1)
        val segments = remaining.split("|")
        segments.forEachIndexed { i, seg ->
            if (seg.contains(":")) {
                val kv = seg.split(":")
                pushStyle(SpanStyle(color = ColorPrefixB)) // m:, r:, t:
                append(kv[0] + ":")
                pop()
                pushStyle(SpanStyle(color = ColorValue))
                append(kv[1])
                pop()
            } else {
                append(seg)
            }
            if (i < segments.size - 1) {
                pushStyle(SpanStyle(color = ColorSeparator))
                append("|")
                pop()
            }
        }
    }
}

private fun AnnotatedString.Builder.appendWithMarkers(text: String) {
    var lastIndex = 0
    MarkerRegex.findAll(text).forEach { match ->
        append(text.substring(lastIndex, match.range.first))
        pushStyle(SpanStyle(color = ColorMarker, fontWeight = FontWeight.Bold))
        append(match.value)
        pop()

        lastIndex = match.range.last + 1
    }
    append(text.substring(lastIndex))
}

private fun AnnotatedString.Builder.parseControlLine(line: String) {
    val regex = Regex("^([UT]0:)(z[0-9A-Z]:)?(.*)$")
    val match = regex.find(line)
    if (match != null) {
        pushStyle(SpanStyle(color = ColorPrefixA, fontWeight = FontWeight.Bold))
        append(match.groupValues[1])
        pop()

        if (match.groupValues[2].isNotEmpty()) {
            pushStyle(SpanStyle(color = ColorPrefixB))
            append(match.groupValues[2])
            pop()
        }
        parseGenericContent(match.groupValues[3])
    } else {
        append(line)
    }
}

private fun AnnotatedString.Builder.parseNoteLine(line: String) {
    val prefix = line.take(3) // N1:
    pushStyle(SpanStyle(color = ColorPrefixA, fontWeight = FontWeight.Bold))
    append(prefix)
    pop()

    val content = line.drop(3)
    var noteCount = 0

    var i = 0
    while (i < content.length) {
        val char = content[i]
        when {
            char == '#' -> {
                pushStyle(SpanStyle(color = ColorSpecial))
                append(char)
                pop()
            }
            char == '/' -> {
                pushStyle(SpanStyle(color = ColorSeparator, fontWeight = FontWeight.Bold))
                append(char)
                pop()
            }
            char.isDigit() -> {
                pushStyle(SpanStyle(color = ColorDigit))
                append(char)
                pop()
            }
            char.lowercaseChar() in "drmfsltr" -> {
                // Détecter la note complète avec ses octaves (',')
                val start = i
                while (i + 1 < content.length && (content[i+1] == '\'' || content[i+1] == ',')) {
                    i++
                }
                val fullNote = content.substring(start, i + 1)
                val color = if (noteCount % 2 == 0) ColorNoteOdd else ColorNoteEven
                pushStyle(SpanStyle(color = color))
                append(fullNote)
                pop()
                noteCount++
            }
            else -> append(char)
        }
        i++
    }
}

private fun AnnotatedString.Builder.parseLyricLine(line: String) {
    pushStyle(SpanStyle(color = ColorPrefixA))
    append(line.take(3))
    pop()
    parseGenericContent(line.drop(3))
}

private fun AnnotatedString.Builder.parseGenericContent(content: String) {
    val parts = content.split("/")
    parts.forEachIndexed { i, part ->
        appendWithMarkers(part)

        if (i < parts.size - 1) {
            pushStyle(SpanStyle(color = ColorSeparator))
            append("/")
            pop()
        }
    }
}