package app.termora.terminal


val Char.Companion.Null get() = 0x0000.toChar()
val Char.Companion.Space get() = 0x0020.toChar()
val Char.Companion.SoftHyphen get() = 0x00ad.toChar()

val Char.isNull get() = this == Char.Null
val Char.isSoftHyphen get() = this == Char.SoftHyphen


// https://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_subr/utf_width.c


/* sorted list of non-overlapping intervals of non-spacing characters */
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
private val combining = arrayOf(
    Pair((0x0300).toChar(), (0x036F).toChar()),
    Pair((0x0483).toChar(), (0x0486).toChar()),
    Pair((0x0488).toChar(), (0x0489).toChar()),
    Pair((0x0591).toChar(), (0x05BD).toChar()),
    Pair((0x05BF).toChar(), (0x05BF).toChar()),
    Pair((0x05C1).toChar(), (0x05C2).toChar()),
    Pair((0x05C4).toChar(), (0x05C5).toChar()),
    Pair((0x05C7).toChar(), (0x05C7).toChar()),
    Pair((0x0600).toChar(), (0x0603).toChar()),
    Pair((0x0610).toChar(), (0x0615).toChar()),
    Pair((0x064B).toChar(), (0x065E).toChar()),
    Pair((0x0670).toChar(), (0x0670).toChar()),
    Pair((0x06D6).toChar(), (0x06E4).toChar()),
    Pair((0x06E7).toChar(), (0x06E8).toChar()),
    Pair((0x06EA).toChar(), (0x06ED).toChar()),
    Pair((0x070F).toChar(), (0x070F).toChar()),
    Pair((0x0711).toChar(), (0x0711).toChar()),
    Pair((0x0730).toChar(), (0x074A).toChar()),
    Pair((0x07A6).toChar(), (0x07B0).toChar()),
    Pair((0x07EB).toChar(), (0x07F3).toChar()),
    Pair((0x0901).toChar(), (0x0902).toChar()),
    Pair((0x093C).toChar(), (0x093C).toChar()),
    Pair((0x0941).toChar(), (0x0948).toChar()),
    Pair((0x094D).toChar(), (0x094D).toChar()),
    Pair((0x0951).toChar(), (0x0954).toChar()),
    Pair((0x0962).toChar(), (0x0963).toChar()),
    Pair((0x0981).toChar(), (0x0981).toChar()),
    Pair((0x09BC).toChar(), (0x09BC).toChar()),
    Pair((0x09C1).toChar(), (0x09C4).toChar()),
    Pair((0x09CD).toChar(), (0x09CD).toChar()),
    Pair((0x09E2).toChar(), (0x09E3).toChar()),
    Pair((0x0A01).toChar(), (0x0A02).toChar()),
    Pair((0x0A3C).toChar(), (0x0A3C).toChar()),
    Pair((0x0A41).toChar(), (0x0A42).toChar()),
    Pair((0x0A47).toChar(), (0x0A48).toChar()),
    Pair((0x0A4B).toChar(), (0x0A4D).toChar()),
    Pair((0x0A70).toChar(), (0x0A71).toChar()),
    Pair((0x0A81).toChar(), (0x0A82).toChar()),
    Pair((0x0ABC).toChar(), (0x0ABC).toChar()),
    Pair((0x0AC1).toChar(), (0x0AC5).toChar()),
    Pair((0x0AC7).toChar(), (0x0AC8).toChar()),
    Pair((0x0ACD).toChar(), (0x0ACD).toChar()),
    Pair((0x0AE2).toChar(), (0x0AE3).toChar()),
    Pair((0x0B01).toChar(), (0x0B01).toChar()),
    Pair((0x0B3C).toChar(), (0x0B3C).toChar()),
    Pair((0x0B3F).toChar(), (0x0B3F).toChar()),
    Pair((0x0B41).toChar(), (0x0B43).toChar()),
    Pair((0x0B4D).toChar(), (0x0B4D).toChar()),
    Pair((0x0B56).toChar(), (0x0B56).toChar()),
    Pair((0x0B82).toChar(), (0x0B82).toChar()),
    Pair((0x0BC0).toChar(), (0x0BC0).toChar()),
    Pair((0x0BCD).toChar(), (0x0BCD).toChar()),
    Pair((0x0C3E).toChar(), (0x0C40).toChar()),
    Pair((0x0C46).toChar(), (0x0C48).toChar()),
    Pair((0x0C4A).toChar(), (0x0C4D).toChar()),
    Pair((0x0C55).toChar(), (0x0C56).toChar()),
    Pair((0x0CBC).toChar(), (0x0CBC).toChar()),
    Pair((0x0CBF).toChar(), (0x0CBF).toChar()),
    Pair((0x0CC6).toChar(), (0x0CC6).toChar()),
    Pair((0x0CCC).toChar(), (0x0CCD).toChar()),
    Pair((0x0CE2).toChar(), (0x0CE3).toChar()),
    Pair((0x0D41).toChar(), (0x0D43).toChar()),
    Pair((0x0D4D).toChar(), (0x0D4D).toChar()),
    Pair((0x0DCA).toChar(), (0x0DCA).toChar()),
    Pair((0x0DD2).toChar(), (0x0DD4).toChar()),
    Pair((0x0DD6).toChar(), (0x0DD6).toChar()),
    Pair((0x0E31).toChar(), (0x0E31).toChar()),
    Pair((0x0E34).toChar(), (0x0E3A).toChar()),
    Pair((0x0E47).toChar(), (0x0E4E).toChar()),
    Pair((0x0EB1).toChar(), (0x0EB1).toChar()),
    Pair((0x0EB4).toChar(), (0x0EB9).toChar()),
    Pair((0x0EBB).toChar(), (0x0EBC).toChar()),
    Pair((0x0EC8).toChar(), (0x0ECD).toChar()),
    Pair((0x0F18).toChar(), (0x0F19).toChar()),
    Pair((0x0F35).toChar(), (0x0F35).toChar()),
    Pair((0x0F37).toChar(), (0x0F37).toChar()),
    Pair((0x0F39).toChar(), (0x0F39).toChar()),
    Pair((0x0F71).toChar(), (0x0F7E).toChar()),
    Pair((0x0F80).toChar(), (0x0F84).toChar()),
    Pair((0x0F86).toChar(), (0x0F87).toChar()),
    Pair((0x0F90).toChar(), (0x0F97).toChar()),
    Pair((0x0F99).toChar(), (0x0FBC).toChar()),
    Pair((0x0FC6).toChar(), (0x0FC6).toChar()),
    Pair((0x102D).toChar(), (0x1030).toChar()),
    Pair((0x1032).toChar(), (0x1032).toChar()),
    Pair((0x1036).toChar(), (0x1037).toChar()),
    Pair((0x1039).toChar(), (0x1039).toChar()),
    Pair((0x1058).toChar(), (0x1059).toChar()),
    Pair((0x1160).toChar(), (0x11FF).toChar()),
    Pair((0x135F).toChar(), (0x135F).toChar()),
    Pair((0x1712).toChar(), (0x1714).toChar()),
    Pair((0x1732).toChar(), (0x1734).toChar()),
    Pair((0x1752).toChar(), (0x1753).toChar()),
    Pair((0x1772).toChar(), (0x1773).toChar()),
    Pair((0x17B4).toChar(), (0x17B5).toChar()),
    Pair((0x17B7).toChar(), (0x17BD).toChar()),
    Pair((0x17C6).toChar(), (0x17C6).toChar()),
    Pair((0x17C9).toChar(), (0x17D3).toChar()),
    Pair((0x17DD).toChar(), (0x17DD).toChar()),
    Pair((0x180B).toChar(), (0x180D).toChar()),
    Pair((0x18A9).toChar(), (0x18A9).toChar()),
    Pair((0x1920).toChar(), (0x1922).toChar()),
    Pair((0x1927).toChar(), (0x1928).toChar()),
    Pair((0x1932).toChar(), (0x1932).toChar()),
    Pair((0x1939).toChar(), (0x193B).toChar()),
    Pair((0x1A17).toChar(), (0x1A18).toChar()),
    Pair((0x1B00).toChar(), (0x1B03).toChar()),
    Pair((0x1B34).toChar(), (0x1B34).toChar()),
    Pair((0x1B36).toChar(), (0x1B3A).toChar()),
    Pair((0x1B3C).toChar(), (0x1B3C).toChar()),
    Pair((0x1B42).toChar(), (0x1B42).toChar()),
    Pair((0x1B6B).toChar(), (0x1B73).toChar()),
    Pair((0x1DC0).toChar(), (0x1DCA).toChar()),
    Pair((0x1DFE).toChar(), (0x1DFF).toChar()),
    Pair((0x200B).toChar(), (0x200F).toChar()),
    Pair((0x202A).toChar(), (0x202E).toChar()),
    Pair((0x2060).toChar(), (0x2063).toChar()),
    Pair((0x206A).toChar(), (0x206F).toChar()),
    Pair((0x20D0).toChar(), (0x20EF).toChar()),
    Pair((0x302A).toChar(), (0x302F).toChar()),
    Pair((0x3099).toChar(), (0x309A).toChar()),
    Pair((0xA806).toChar(), (0xA806).toChar()),
    Pair((0xA80B).toChar(), (0xA80B).toChar()),
    Pair((0xA825).toChar(), (0xA826).toChar()),
    Pair((0xFB1E).toChar(), (0xFB1E).toChar()),
    Pair((0xFE00).toChar(), (0xFE0F).toChar()),
    Pair((0xFE20).toChar(), (0xFE23).toChar()),
    Pair((0xFEFF).toChar(), (0xFEFF).toChar()),
    Pair((0xFFF9).toChar(), (0xFFFB).toChar()),
    Pair((0x10A01).toChar(), (0x10A03).toChar()),
    Pair((0x10A05).toChar(), (0x10A06).toChar()),
    Pair((0x10A0C).toChar(), (0x10A0F).toChar()),
    Pair((0x10A38).toChar(), (0x10A3A).toChar()),
    Pair((0x10A3F).toChar(), (0x10A3F).toChar()),
    Pair((0x1D167).toChar(), (0x1D169).toChar()),
    Pair((0x1D173).toChar(), (0x1D182).toChar()),
    Pair((0x1D185).toChar(), (0x1D18B).toChar()),
    Pair((0x1D1AA).toChar(), (0x1D1AD).toChar()),
    Pair((0x1D242).toChar(), (0x1D244).toChar()),
    Pair((0xE0001).toChar(), (0xE0001).toChar()),
    Pair((0xE0020).toChar(), (0xE007F).toChar()),
    Pair((0xE0100).toChar(), (0xE01EF).toChar())
)


/* auxiliary function for binary search in interval table */
private fun bisearch(ucs: Char): Int {
    var max = combining.size - 1
    var min = 0
    var mid: Int

    if (ucs < combining[0].first || ucs > combining[max].second) return 0

    while (max >= min) {
        mid = (min + max) / 2
        if (ucs > combining[mid].second) min = mid + 1
        else if (ucs < combining[mid].first) max = mid - 1
        else return 1
    }

    return 0
}


fun mk_wcwidth(ucs: Char): Int {
    return mk_wcwidth(ucs.code)
}

// cache
private val mk_wcwidth_cache = hashMapOf<Int, Int>()

fun mk_wcwidth(ucs: Int): Int {
    if (mk_wcwidth_cache.containsKey(ucs)) {
        return mk_wcwidth_cache.getValue(ucs)
    }
    val w = mk_wcwidth0(ucs)
    mk_wcwidth_cache[ucs] = w
    return w
}

private fun mk_wcwidth0(ucs: Int): Int {


    if (ucs == 0) return 0
    if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1

    /* binary search in table of non-spacing characters */
    if (bisearch(ucs.toChar()) > 0) return 0

    /* if we arrive here, ucs is not a combining or C0/C1 control character */

    return 1 + (if ((ucs >= 0x1100 &&
                (ucs <= 0x115f ||  /* Hangul Jamo init. consonants */
                        ucs == 0x2329 || ucs == 0x232a ||
                        (ucs in 0x2e80..0xa4cf
                                && ucs != 0x303f) ||  /* CJK ... Yi */
                        (ucs in 0xac00..0xd7a3) ||  /* Hangul Syllables */
                        (ucs in 0xf900..0xfaff) ||  /* CJK Compatibility Ideographs */
                        (ucs in 0xfe10..0xfe19) ||  /* Vertical forms */
                        (ucs in 0xfe30..0xfe6f) ||  /* CJK Compatibility Forms */
                        (ucs in 0xff00..0xff60) ||  /* Fullwidth Forms */
                        (ucs in 0xffe0..0xffe6) ||
                        (ucs in 0x20000..0x2fffd) ||
                        (ucs in 0x30000..0x3fffd)))
    ) 1 else 0)
}