' UTF-8 encoding/decoding from Unicode codepoint sequences
#COMPILE EXE
#DIM ALL

FUNCTION PBMAIN() AS LONG
    LOCAL i  AS LONG
    LOCAL j  AS LONG
    LOCAL cp  AS LONG
    LOCAL byteCount  AS LONG
    LOCAL b0  AS LONG
    LOCAL b1  AS LONG
    LOCAL b2  AS LONG
    LOCAL b3  AS LONG
    LOCAL result  AS LONG
    LOCAL decoded  AS LONG
    LOCAL ok  AS LONG
    LOCAL utf8Str AS STRING
    LOCAL codepoints(20) AS LONG
    LOCAL numCP AS LONG
    LOCAL outBytes(10) AS LONG
    LOCAL testVal AS LONG

    PRINT "=== UTF-8 Encoding / Decoding ==="
    PRINT

    numCP = 12
    codepoints(0) = 65
    codepoints(1) = 8364
    codepoints(2) = 1025
    codepoints(3) = 1040
    codepoints(4) = 1071
    codepoints(5) = 1488
    codepoints(6) = 3585
    codepoints(7) = 12450
    codepoints(8) = 65533
    codepoints(9) = 66666
    codepoints(10) = 119070
    codepoints(11) = 128169

    PRINT "--- Encoding ---"
    PRINT
    utf8Str = ""

    FOR i = 0 TO numCP - 1
        cp = codepoints(i)
        byteCount = 0

        IF cp < 128 THEN
            outBytes(0) = cp
            byteCount = 1
        ELSE
            IF cp < 2048 THEN
                outBytes(0) = 192 + (cp / 64)
                outBytes(1) = 128 + (cp AND 63)
                byteCount = 2
            ELSE
                IF cp < 65536 THEN
                    outBytes(0) = 224 + (cp / 4096)
                    outBytes(1) = 128 + ((cp / 64) AND 63)
                    outBytes(2) = 128 + (cp AND 63)
                    byteCount = 3
                ELSE
                    outBytes(0) = 240 + (cp / 262144)
                    outBytes(1) = 128 + ((cp / 4096) AND 63)
                    outBytes(2) = 128 + ((cp / 64) AND 63)
                    outBytes(3) = 128 + (cp AND 63)
                    byteCount = 4
                END IF
            END IF
        END IF

        PRINT "U+" & FORMAT$(cp) & " -> "
        FOR j = 0 TO byteCount - 1
            PRINT FORMAT$(outBytes(j))
            IF j < byteCount - 1 THEN PRINT " ";
            utf8Str = utf8Str + CHR$(outBytes(j))
        NEXT j
        PRINT " (" & STR$(byteCount) & " bytes)"
    NEXT i

    PRINT
    PRINT "UTF-8 string length:" & STR$(LEN(utf8Str)) & " bytes"
    PRINT

    PRINT "--- Decoding ---"
    PRINT
    i = 1
    ok = 1

    WHILE i <= LEN(utf8Str) AND ok = 1
        b0 = ASC(MID$(utf8Str, i, 1))
        IF b0 < 128 THEN
            decoded = b0
            i = i + 1
        ELSE
            IF b0 < 224 THEN
                IF i + 1 <= LEN(utf8Str) THEN
                    b1 = ASC(MID$(utf8Str, i + 1, 1))
                    decoded = (b0 AND 31) * 64 + (b1 AND 63)
                    i = i + 2
                ELSE
                    ok = 0
                    GOTO decodeError
                END IF
            ELSE
                IF b0 < 240 THEN
                    IF i + 2 <= LEN(utf8Str) THEN
                        b1 = ASC(MID$(utf8Str, i + 1, 1))
                        b2 = ASC(MID$(utf8Str, i + 2, 1))
                        decoded = (b0 AND 15) * 4096 + (b1 AND 63) * 64 + (b2 AND 63)
                        i = i + 3
                    ELSE
                        ok = 0
                        GOTO decodeError
                    END IF
                ELSE
                    IF i + 3 <= LEN(utf8Str) THEN
                        b1 = ASC(MID$(utf8Str, i + 1, 1))
                        b2 = ASC(MID$(utf8Str, i + 2, 1))
                        b3 = ASC(MID$(utf8Str, i + 3, 1))
                        decoded = (b0 AND 7) * 262144 + (b1 AND 63) * 4096 + (b2 AND 63) * 64 + (b3 AND 63)
                        i = i + 4
                    ELSE
                        ok = 0
                        GOTO decodeError
                    END IF
                END IF
            END IF
        END IF
        PRINT "  U+" & FORMAT$(decoded)
    WEND
    GOTO decodeDone

decodeError:
    PRINT "  [DECODE ERROR at byte" & STR$(i) & "]"

decodeDone:
    IF ok = 1 THEN PRINT "Decoding: OK" ELSE PRINT "Decoding: ERROR"

    PRINT
    PRINT "UTF-8 encode/decode complete."

    FUNCTION = 0
END FUNCTION
