// Some text-handling functions
#include "n64dd.h"
#include "versions.h"

void (*D_801D2EC0)(void*, uintptr_t, size_t) = NULL;

s32 B_801E0F70;

/**
 * Seems to work out if a pair of bytes is a valid EUC-JP character, although there may be additions to the font that
 * make the strange first check make more sense.
 *
 * @param bytes Array 2 bytes to test
 * @return boolean
 */
int func_801C9440(u8* bytes) {
    // This is every possible first byte. May make more sense what was intended with the font files?
    if (((*bytes >= 0x8E) && (*bytes <= 0xFE)) || ((*bytes != 0x8F) && (*bytes != 0xA0))) {
        bytes++;
        return (*bytes >= 0xA0) && (*bytes <= 0xFE);
    }
    return false;
}

/**
 * A crude check for a valid 2-byte Shift-JIS character
 *
 * @param bytes Array containing a pair of bytes to test
 * @return boolean
 */
int func_801C9494(u8* bytes) {
    // Allowable first bytes.
    if (((*bytes >= 0x81) && (*bytes <= 0x9F)) || ((*bytes >= 0xE0) && (*bytes <= 0xFC))) {
        bytes++;
        // Allowable second bytes.
        return (*bytes >= 0x40) && (*bytes <= 0xFC) && *bytes != 0x7F;
    }
    return false;
}

// Extracts 2 bytes from a bytepacked big-endian short.
void func_801C94F8(u8* arg0, u16 arg1) {
    arg0[0] = arg1 >> 8;
    arg0[1] = arg1 & 0xFF;
}

// Convert EUC-JP to JIS X 0208
u16 func_801C9514(u16 eucjpCh) {
    return eucjpCh - 0x8080;
}

/**
 * Converts a JIS X 0208 codepoint to a Shift-JIS one.
 *
 * @param jisCodepoint Two bytes, each between 0x21 and 0x7E, packed big-endian into a short.
 * @return u16 Shift-JIS character representation (expected to be big-endian)
 */
u16 func_801C9534(u16 jisCodepoint) {
    u8 hiByte = (jisCodepoint >> 8) & 0xFF;
    u8 loByte = jisCodepoint & 0xFF;

    if (hiByte & 1) {
        loByte += 0x1F;
        if (loByte >= 0x7F) {
            loByte++;
        }
    } else {
        loByte += 0x7E;
    }

    hiByte = (hiByte - 0x21) / 2 + 0x81;
    if (hiByte >= 0xA0) {
        hiByte += 0x40;
    }

    return (hiByte << 8) + loByte;
}

void func_801C95C0(void* arg0, uintptr_t arg1, size_t arg2) {
    if (D_801D2EC0 != NULL) {
        D_801D2EC0(arg0, arg1, arg2);
    }
}

s32 func_801C95F0(u8* arg0) {
    return LeoGetKAdr(func_801C9534(func_801C9514((arg0[0] << 8) | arg0[1]))) + DDROM_FONT_START;
}

s32 func_801C963C(s32* arg0, int* dx, int* dy, int* cy, u8 arg4) {
    s32 temp_v0;
    s32 temp_v1;

    u16 temp = arg4 - 0x20;
    temp += 0xC0 * B_801E0F70;

    temp_v0 = LeoGetAAdr(temp, dx, dy, cy);
    temp_v1 = temp_v0 & 0xF;
    *arg0 = (temp_v0 - temp_v1) + DDROM_FONT_START;
    return temp_v1;
}

s32 func_801C969C(void* arg0, int* dx, int* dy, int* cy, u8* arg4) {
    s32 sp24;
    s32 phi_v1;

    if (func_801C9440(arg4)) {
        sp24 = func_801C95F0(arg4);
        *dx = 16;
        *dy = 16;
        *cy = 11;
        phi_v1 = 0;
    } else {
        phi_v1 = func_801C963C(&sp24, dx, dy, cy, *arg4);
    }

    func_801C95C0(arg0, sp24, 0x80);
    return phi_v1;
}

// return boolean
s32 func_801C9740(u8* arg0, s32 arg1, u8 arg2) {
    if (arg1 == 1) {
        *arg0 = arg2 << 4;
        return false;
    } else {
        *arg0 |= arg2;
        return true;
    }
}

u8* func_801C9778(u8* arg0, s32* arg1, s32 arg2) {
    arg0 += (arg2 >> 1);

    if (((*arg1 == 1) && !(arg2 & 1)) || ((*arg1 == 0) && (arg2 & 1))) {
        arg0 += arg2 & 1;
        *arg1 = 1;
    } else {
        *arg1 = 0;
    }
    return arg0;
}

s32 func_801C97C4(u8** arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4, u8* arg5, s32 dx, s32 dy, s32 cy) {
    u8* var_fp;
    u8* var_s0;
    s32 var_s1;
    s32 x;
    s32 sp4C;
    s32 var_s4;
    s32 sp44;

    sp4C = arg2;
    sp44 = dx + arg3 + arg4;
    var_s4 = 1;
    var_fp = func_801C9778(*arg0, &sp4C, arg3);
    arg1 >>= 1;
    var_fp += ((11 - cy) * arg1);
    if (dx & 1) {
        dx++;
    }
    while (dy--) {
        var_s0 = var_fp;
        var_s1 = sp4C;
        for (x = 0; x < dx; x++) {
            if (var_s4 == 1) {
                if (func_801C9740(var_s0, var_s1, *arg5 >> 4)) {
                    var_s0++;
                }
            } else {
                if (func_801C9740(var_s0, var_s1, *arg5 & 0xF)) {
                    var_s0++;
                }
                arg5++;
            }
            var_s4 ^= 1;
            var_s1 ^= 1;
        }
        var_fp += arg1;
    }
    *arg0 = func_801C9778(*arg0, &arg2, sp44);
    return arg2;
}

void func_801C9954(u8* bytes, s32* arg1, s32* arg2) {
    u8 prevCh;
    u8 nextCh;

    if (func_801C9440(bytes)) {
        *arg1 = *arg2 = 0;
        return;
    }

    *arg1 = *arg2 = 0;

    prevCh = bytes[-1];
    nextCh = bytes[1];

    switch (bytes[0]) {
        case ' ':
            *arg1 = *arg2 = 0;
            return;

        case 'I':
        case 'i':
#if OOT_NTSC
            *arg2 = 2;
            if (1) {}
#endif

            *arg1 = 2;
            *arg2 = 2;

            if (prevCh == ' ') {
                *arg1 = 0;
            }
            if (nextCh == ' ') {
                *arg2 = 0;
            }
            return;
    }

#if OOT_NTSC
    *arg2 = 1;
    if (1) {}
#endif

    *arg1 = 1;
    *arg2 = 1;
}

void func_801C9A10(u8* arg0, s32 arg1, u8* str) {
    u8 sp80[0xA0];
    u8* temp_s1;
    int dx;
    int dy;
    int cy;
    s32 var_s2;
    s32 sp68;
    s32 sp64;
    s32 temp_v1;

    temp_s1 = (u8*)((((uintptr_t)&sp80 + 0xF) / 0x10) * 0x10);
    var_s2 = 1;
    if (str != NULL) {
        while (*str != '\0') {
            func_801C9954(str, &sp68, &sp64);
            temp_v1 = func_801C969C(temp_s1, &dx, &dy, &cy, str);
            if (dx & 1) {
                dx++;
            }
            var_s2 = func_801C97C4(&arg0, arg1, var_s2, sp68, sp64, &temp_s1[temp_v1], dx, dy, cy);
            if (func_801C9440(str)) {
                str++;
            }
            str++;
        }
    }
}

void func_801C9B50(s32 arg0, void (*arg1)(void*, uintptr_t, size_t)) {
    B_801E0F70 = arg0;
    D_801D2EC0 = arg1;
}
