/*
 * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package kotlin.text

//
// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateUnicodeData.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//

private val casedStart = intArrayOf(
    0x10400, 0x104b0, 0x104d8, 0x10c80, 0x10cc0, 0x118a0, 0x16e40, 0x1d400, 0x1d456, 0x1d49e, 0x1d4a2, 0x1d4a5, 0x1d4a9, 0x1d4ae, 0x1d4bb, 0x1d4bd, 0x1d4c5, 0x1d507, 0x1d50d, 0x1d516, 
    0x1d51e, 0x1d53b, 0x1d540, 0x1d546, 0x1d54a, 0x1d552, 0x1d6a8, 0x1d6c2, 0x1d6dc, 0x1d6fc, 0x1d716, 0x1d736, 0x1d750, 0x1d770, 0x1d78a, 0x1d7aa, 0x1d7c4, 0x1e900, 
)
private val casedEnd = intArrayOf(
    0x1044f, 0x104d3, 0x104fb, 0x10cb2, 0x10cf2, 0x118df, 0x16e7f, 0x1d454, 0x1d49c, 0x1d49f, 0x1d4a2, 0x1d4a6, 0x1d4ac, 0x1d4b9, 0x1d4bb, 0x1d4c3, 0x1d505, 0x1d50a, 0x1d514, 0x1d51c, 
    0x1d539, 0x1d53e, 0x1d544, 0x1d546, 0x1d550, 0x1d6a5, 0x1d6c0, 0x1d6da, 0x1d6fa, 0x1d714, 0x1d734, 0x1d74e, 0x1d76e, 0x1d788, 0x1d7a8, 0x1d7c2, 0x1d7cb, 0x1e943, 
)

// Lu + Ll + Lt + Other_Lowercase + Other_Uppercase (PropList.txt of Unicode Character Database files)
// Declared internal for testing
internal fun Int.isCased(): Boolean {
    if (this <= Char.MAX_VALUE.code) {
        when (toChar().getCategoryValue()) {
            CharCategory.UPPERCASE_LETTER.value,
            CharCategory.LOWERCASE_LETTER.value,
            CharCategory.TITLECASE_LETTER.value -> return true
        }
    }
    if (isOtherUppercase() || isOtherLowercase()) {
        return true
    }
    val index = binarySearchRange(casedStart, this)
    return index >= 0 && this <= casedEnd[index]
}

private val caseIgnorableStart = intArrayOf(
    0x0027, 0x002e, 0x003a, 0x00b7, 0x0387, 0x055f, 0x05f4, 0x2018, 0x2019, 0x2024, 0x2027, 0xfe13, 0xfe52, 0xfe55, 0xff07, 0xff0e, 0xff1a, 0x101fd, 0x102e0, 0x10376, 
    0x10a01, 0x10a05, 0x10a0c, 0x10a38, 0x10a3f, 0x10ae5, 0x10d24, 0x10eab, 0x10f46, 0x11001, 0x11038, 0x1107f, 0x110b3, 0x110b9, 0x110bd, 0x110cd, 0x11100, 0x11127, 0x1112d, 0x11173, 
    0x11180, 0x111b6, 0x111c9, 0x111cf, 0x1122f, 0x11234, 0x11236, 0x1123e, 0x112df, 0x112e3, 0x11300, 0x1133b, 0x11340, 0x11366, 0x11370, 0x11438, 0x11442, 0x11446, 0x1145e, 0x114b3, 
    0x114ba, 0x114bf, 0x114c2, 0x115b2, 0x115bc, 0x115bf, 0x115dc, 0x11633, 0x1163d, 0x1163f, 0x116ab, 0x116ad, 0x116b0, 0x116b7, 0x1171d, 0x11722, 0x11727, 0x1182f, 0x11839, 0x1193b, 
    0x1193e, 0x11943, 0x119d4, 0x119da, 0x119e0, 0x11a01, 0x11a33, 0x11a3b, 0x11a47, 0x11a51, 0x11a59, 0x11a8a, 0x11a98, 0x11c30, 0x11c38, 0x11c3f, 0x11c92, 0x11caa, 0x11cb2, 0x11cb5, 
    0x11d31, 0x11d3a, 0x11d3c, 0x11d3f, 0x11d47, 0x11d90, 0x11d95, 0x11d97, 0x11ef3, 0x13430, 0x16af0, 0x16b30, 0x16b40, 0x16f4f, 0x16f8f, 0x16fe0, 0x16fe3, 0x1bc9d, 0x1bca0, 0x1d167, 
    0x1d173, 0x1d185, 0x1d1aa, 0x1d242, 0x1da00, 0x1da3b, 0x1da75, 0x1da84, 0x1da9b, 0x1daa1, 0x1e000, 0x1e008, 0x1e01b, 0x1e023, 0x1e026, 0x1e130, 0x1e2ec, 0x1e8d0, 0x1e944, 0x1f3fb, 
    0xe0001, 0xe0020, 0xe0100, 
)
private val caseIgnorableEnd = intArrayOf(
    0x0027, 0x002e, 0x003a, 0x00b7, 0x0387, 0x055f, 0x05f4, 0x2018, 0x2019, 0x2024, 0x2027, 0xfe13, 0xfe52, 0xfe55, 0xff07, 0xff0e, 0xff1a, 0x101fd, 0x102e0, 0x1037a, 
    0x10a03, 0x10a06, 0x10a0f, 0x10a3a, 0x10a3f, 0x10ae6, 0x10d27, 0x10eac, 0x10f50, 0x11001, 0x11046, 0x11081, 0x110b6, 0x110ba, 0x110bd, 0x110cd, 0x11102, 0x1112b, 0x11134, 0x11173, 
    0x11181, 0x111be, 0x111cc, 0x111cf, 0x11231, 0x11234, 0x11237, 0x1123e, 0x112df, 0x112ea, 0x11301, 0x1133c, 0x11340, 0x1136c, 0x11374, 0x1143f, 0x11444, 0x11446, 0x1145e, 0x114b8, 
    0x114ba, 0x114c0, 0x114c3, 0x115b5, 0x115bd, 0x115c0, 0x115dd, 0x1163a, 0x1163d, 0x11640, 0x116ab, 0x116ad, 0x116b5, 0x116b7, 0x1171f, 0x11725, 0x1172b, 0x11837, 0x1183a, 0x1193c, 
    0x1193e, 0x11943, 0x119d7, 0x119db, 0x119e0, 0x11a0a, 0x11a38, 0x11a3e, 0x11a47, 0x11a56, 0x11a5b, 0x11a96, 0x11a99, 0x11c36, 0x11c3d, 0x11c3f, 0x11ca7, 0x11cb0, 0x11cb3, 0x11cb6, 
    0x11d36, 0x11d3a, 0x11d3d, 0x11d45, 0x11d47, 0x11d91, 0x11d95, 0x11d97, 0x11ef4, 0x13438, 0x16af4, 0x16b36, 0x16b43, 0x16f4f, 0x16f9f, 0x16fe1, 0x16fe4, 0x1bc9e, 0x1bca3, 0x1d169, 
    0x1d182, 0x1d18b, 0x1d1ad, 0x1d244, 0x1da36, 0x1da6c, 0x1da75, 0x1da84, 0x1da9f, 0x1daaf, 0x1e006, 0x1e018, 0x1e021, 0x1e024, 0x1e02a, 0x1e13d, 0x1e2ef, 0x1e8d6, 0x1e94b, 0x1f3ff, 
    0xe0001, 0xe007f, 0xe01ef, 
)

// Mn + Me + Cf + Lm + Sk + Word_Break=MidLetter + Word_Break=MidNumLet + Word_Break=Single_Quote (WordBreakProperty.txt of Unicode Character Database files)
// Declared internal for testing
internal fun Int.isCaseIgnorable(): Boolean {
    if (this <= Char.MAX_VALUE.code) {
        when (toChar().getCategoryValue()) {
            CharCategory.NON_SPACING_MARK.value,
            CharCategory.ENCLOSING_MARK.value,
            CharCategory.FORMAT.value,
            CharCategory.MODIFIER_LETTER.value,
            CharCategory.MODIFIER_SYMBOL.value -> return true
        }
    }
    val index = binarySearchRange(caseIgnorableStart, this)
    return index >= 0 && this <= caseIgnorableEnd[index]
}

@OptIn(kotlin.experimental.ExperimentalNativeApi::class)
private fun String.codePointBefore(index: Int): Int {
    val low = this[index]
    if (low.isLowSurrogate() && index - 1 >= 0) {
        val high = this[index - 1]
        if (high.isHighSurrogate()) {
            return Char.toCodePoint(high, low)
        }
    }
    return low.code
}

// \p{cased} (\p{case-ignorable})* Sigma !( (\p{case-ignorable})* \p{cased} )
// The regular-expression operator * is "possessive", consuming as many characters as possible, with no backup.
// This is significant in the case of Final_Sigma, because the sets of case-ignorable and cased characters are not disjoint.
private fun String.isFinalSigmaAt(index: Int): Boolean {
    if (this[index] == '\u03A3' && index > 0) {
        var i = index - 1
        var codePoint: Int = 0
        while (i >= 0) {
            codePoint = codePointBefore(i)
            if (codePoint.isCaseIgnorable()) {
                i -= codePoint.charCount()
            } else {
                break
            }
        }
        if (i >= 0 && codePoint.isCased()) {
            var j = index + 1
            while (j < length) {
                codePoint = codePointAt(j)
                if (codePoint.isCaseIgnorable()) {
                    j += codePoint.charCount()
                } else {
                    break
                }
            }
            if (j >= length || !codePoint.isCased()) {
                return true
            }
        }
    }
    return false
}

internal fun String.lowercaseImpl(): String {
    var unchangedIndex = 0
    while (unchangedIndex < this.length) {
        val codePoint = codePointAt(unchangedIndex)
        if (codePoint.lowercaseCodePoint() != codePoint) { // '\u0130' and '\u03A3' have lowercase corresponding mapping in UnicodeData.txt, no need to check them separately
            break
        }
        unchangedIndex += codePoint.charCount()
    }
    if (unchangedIndex == this.length) {
        return this
    }

    val sb = StringBuilder(this.length)
    sb.appendRange(this, 0, unchangedIndex)

    var index = unchangedIndex

    while (index < this.length) {
        if (this[index] == '\u0130') {
            sb.append("\u0069\u0307")
            index++
            continue
        }
        if (isFinalSigmaAt(index)) {
            sb.append('\u03C2')
            index++
            continue
        }
        val codePoint = codePointAt(index)
        val lowercaseCodePoint = codePoint.lowercaseCodePoint()
        sb.appendCodePoint(lowercaseCodePoint)
        index += codePoint.charCount()
    }

    return sb.toString()
}
