// This module implements the interface defined in uniprops.h
// in terms of ICU functions.
//
// The ICU documentation is at <https://unicode-org.github.io/icu/>.
// Includes:
//
//   * user guide: https://unicode-org.github.io/icu/userguide/
//   * API: https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/index.html
//
// This file is part of Beastie <https://purl.org/nxg/dist/beastie>
// SPDX-FileCopyrightText: 2025 Norman Gray <https://nxg.me.uk>
// SPDX-License-Identifier: BSD-2-Clause

#define HAVE_ICU 1              // ...by hypothesis
#include "uniprops.h"
#include "unicode/uchar.h"

int icu_is_icu_p(void)
{
    return 1;
}


#define PROPFUNC(fn, prop)                      \
    int fn(const codepoint_t cp) {              \
        return u_hasBinaryProperty(cp, prop);   \
    }

// Lowercase + Uppercase + Lt + Lm + Lo + Nl + Other_Alphabetic
// (note: this is slightly different from u_isalpha, which is all 'L')
//
// I seem to have defined the following two functions differently in mycu.c:
// is that a misunderstanding, or some distinction I've now forgotten
PROPFUNC(icu_letter_p, UCHAR_ALPHABETIC);
PROPFUNC(icu_alphabetic_p, UCHAR_ALPHABETIC);    // or u_isUAlphabetic(cp)


PROPFUNC(icu_uppercase_letter_p, UCHAR_UPPERCASE);
PROPFUNC(icu_lowercase_letter_p, UCHAR_LOWERCASE);
int icu_titlecase_letter_p(const codepoint_t cp)
{
    return u_istitle(cp);
}

int icu_number_p(const codepoint_t cp)
{
    return u_isdigit(cp);
}

int icu_alnum_p(const codepoint_t cp)
{
    return u_isalnum(cp);
}

int icu_punctuation_p(const codepoint_t cp)
{
    return u_ispunct(cp);
}

int icu_cntrl_p(const codepoint_t cp)
{
    //return u_charType(cp) == U_CONTROL_CHAR;
    return u_iscntrl(cp);
}

int icu_symbol_p(const codepoint_t cp)
{
    int8_t cat = u_charType(cp);
    return cat == U_MATH_SYMBOL
        || cat == U_CURRENCY_SYMBOL
        || cat == U_MODIFIER_SYMBOL
        || cat == U_OTHER_SYMBOL;
}

int icu_mark_p(const codepoint_t cp)
{
    int8_t cat = u_charType(cp);
    return cat == U_NON_SPACING_MARK
        || cat == U_ENCLOSING_MARK
        || cat == U_COMBINING_SPACING_MARK;
}

int icu_space_p(const codepoint_t cp)
{
    if (cp < 0x20) {
        // Unicode regards all of the codepoints below U+0020 as class
        // 'Cc' (‘a C0 or C1 control code’).
        // As an exception, deem characters in this range to be spaces
        // if they are spaces in the POSIX locale
        // (this should match isspace(cp))
        return (cp == ' ')      // space
            || (cp >= 0x09 && cp <= 0x0d); // <tab> to <carriage-return>

    } else {
        // Test whether the codepoint has the Unicode White_Space property.
        // This is different from u_isspace (which matches POSIX)
        // and u_isWhitespace
        // (which matches Java, and excludes non-breaking spaces)
        return u_isUWhiteSpace(cp);
    }
}

int icu_whitespace_p(const codepoint_t cp)
{
    return u_isWhitespace(cp);
}

int icu_nonbreakingspace_p(const codepoint_t cp)
{
    return (cp == 0x00a0 || cp == 0x2007 || cp == 0x202f);
}

int icu_wordcharacter_p(const codepoint_t cp)
{
    // fast path
    if (cp < 0x80) {
        return icu_alphabetic_p(cp);
    } else {
        return icu_alphabetic_p(cp)
            || u_hasBinaryProperty(cp, UCHAR_DIACRITIC)
            || u_hasBinaryProperty(cp, UCHAR_EXTENDER)
            || u_hasBinaryProperty(cp, UCHAR_JOIN_CONTROL);
        // return icu_alphabetic_p(cp)
        //     || icu_diacritic_p(cp)
        //     || icu_extender_p(cp)
        //     || icu_joincontrol_p(cp);
    }
}


codepoint_t icu_uppercase_character(const codepoint_t cp)
{
    return u_toupper(cp);
}

codepoint_t icu_lowercase_character(const codepoint_t cp)
{
    return u_tolower(cp);
}

codepoint_t icu_titlecase_character(const codepoint_t cp)
{
    return u_totitle(cp);
}


