// Most functions are borrowed from relibc's wctype
// Because these are extremely boring to implement

use core::ffi::{c_char, c_int, c_uint};

use casecmp::casemap;

use crate::{headers::ctype, util::PointerStrExt};

use super::locale::locale_t;

mod alpha;
mod casecmp;
mod punct;

pub const WCTYPE_ALNUM: wctype_t = 1;
pub const WCTYPE_ALPHA: wctype_t = 2;
pub const WCTYPE_BLANK: wctype_t = 3;
pub const WCTYPE_CNTRL: wctype_t = 4;
pub const WCTYPE_DIGIT: wctype_t = 5;
pub const WCTYPE_GRAPH: wctype_t = 6;
pub const WCTYPE_LOWER: wctype_t = 7;
pub const WCTYPE_PRINT: wctype_t = 8;
pub const WCTYPE_PUNCT: wctype_t = 9;
pub const WCTYPE_SPACE: wctype_t = 10;
pub const WCTYPE_UPPER: wctype_t = 11;
pub const WCTYPE_XDIGIT: wctype_t = 12;

const WCTRANSUP: wctrans_t = 1 as wctrans_t;
const WCTRANSLW: wctrans_t = 2 as wctrans_t;

pub type wint_t = c_uint;
pub type wctype_t = c_int;
pub type wctrans_t = c_int;

pub const WOF: wint_t = -1i32 as wint_t;

#[no_mangle]
unsafe extern "C" fn iswalnum(wc: wint_t) -> c_int {
    c_int::from(iswdigit(wc) != 0 || iswalpha(wc) != 0)
}

#[no_mangle]
unsafe extern "C" fn iswalpha(wc: wint_t) -> c_int {
    c_int::from(alpha::is(wc as usize))
}

#[no_mangle]
unsafe extern "C" fn iswblank(wc: wint_t) -> c_int {
    ctype::isblank(wc as c_int)
}

#[no_mangle]
unsafe extern "C" fn iswcntrl(wc: wint_t) -> c_int {
    c_int::from(
        wc < 32
            || wc.wrapping_sub(0x7f) < 33
            || wc.wrapping_sub(0x2028) < 2
            || wc.wrapping_sub(0xfff9) < 3,
    )
}

#[no_mangle]
unsafe extern "C" fn iswdigit(wc: wint_t) -> c_int {
    c_int::from(wc.wrapping_sub('0' as wint_t) < 10)
}

#[no_mangle]
unsafe extern "C" fn iswgraph(wc: wint_t) -> c_int {
    c_int::from(iswspace(wc) == 0 && iswprint(wc) != 0)
}

#[no_mangle]
unsafe extern "C" fn iswlower(wc: wint_t) -> c_int {
    c_int::from(towupper(wc) != wc)
}

#[no_mangle]
unsafe extern "C" fn iswprint(wc: wint_t) -> c_int {
    if wc < 0xff {
        c_int::from(((wc + 1) & 0x7f) >= 0x21)
    } else if wc < 0x2028
        || wc.wrapping_sub(0x202a) < 0xd800 - 0x202a
        || wc.wrapping_sub(0xe000) < 0xfff9 - 0xe000
    {
        1
    } else if wc.wrapping_sub(0xfffc) > 0x10ffff - 0xfffc || (wc & 0xfffe) == 0xfffe {
        0
    } else {
        1
    }
}

#[no_mangle]
unsafe extern "C" fn iswpunct(wc: wint_t) -> c_int {
    c_int::from(punct::is(wc as usize))
}

#[no_mangle]
unsafe extern "C" fn iswspace(wc: wint_t) -> c_int {
    c_int::from(
        [
            ' ' as wint_t,
            '\t' as wint_t,
            '\n' as wint_t,
            '\r' as wint_t,
            11,
            12,
            0x0085,
            0x2000,
            0x2001,
            0x2002,
            0x2003,
            0x2004,
            0x2005,
            0x2006,
            0x2008,
            0x2009,
            0x200a,
            0x2028,
            0x2029,
            0x205f,
            0x3000,
        ]
        .contains(&wc),
    )
}

#[no_mangle]
unsafe extern "C" fn iswupper(wc: wint_t) -> c_int {
    c_int::from(towlower(wc) != wc)
}

#[no_mangle]
unsafe extern "C" fn iswxdigit(wc: wint_t) -> c_int {
    c_int::from(wc.wrapping_sub('0' as wint_t) < 10 || (wc | 32).wrapping_sub('a' as wint_t) < 6)
}

#[no_mangle]
unsafe extern "C" fn towlower(wc: wint_t) -> wint_t {
    casemap(wc, 0)
}

#[no_mangle]
unsafe extern "C" fn towupper(wc: wint_t) -> wint_t {
    casemap(wc, 1)
}

#[no_mangle]
unsafe extern "C" fn iswctype(wc: wint_t, ctype: wctype_t) -> c_int {
    match ctype {
        WCTYPE_ALNUM => iswalnum(wc),
        WCTYPE_ALPHA => iswalpha(wc),
        WCTYPE_BLANK => iswblank(wc),
        WCTYPE_CNTRL => iswcntrl(wc),
        WCTYPE_DIGIT => iswdigit(wc),
        WCTYPE_GRAPH => iswgraph(wc),
        WCTYPE_LOWER => iswlower(wc),
        WCTYPE_PRINT => iswprint(wc),
        WCTYPE_PUNCT => iswpunct(wc),
        WCTYPE_SPACE => iswspace(wc),
        WCTYPE_UPPER => iswupper(wc),
        WCTYPE_XDIGIT => iswxdigit(wc),
        _ => 0,
    }
}

#[no_mangle]
unsafe extern "C" fn wctype_t(_s: *const c_char) -> wctype_t {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn towctrans(wc: wint_t, trans: wctrans_t) -> wint_t {
    match trans {
        WCTRANSUP => towupper(wc),
        WCTRANSLW => towlower(wc),
        _ => wc,
    }
}

#[no_mangle]
unsafe extern "C" fn wctrans(s: *const c_char) -> wctrans_t {
    let s = s.ensure_str();
    match s {
        "toupper" => WCTRANSUP,
        "tolower" => WCTRANSLW,
        _ => 0 as wctrans_t,
    }
}

#[no_mangle]
unsafe extern "C" fn wctype(s: *const c_char) -> wctype_t {
    let s = s.ensure_str();
    match s {
        "alnum" => WCTYPE_ALNUM,
        "alpha" => WCTYPE_ALPHA,
        "blank" => WCTYPE_BLANK,
        "cntrl" => WCTYPE_CNTRL,
        "digit" => WCTYPE_DIGIT,
        "graph" => WCTYPE_GRAPH,
        "lower" => WCTYPE_LOWER,
        "print" => WCTYPE_PRINT,
        "punct" => WCTYPE_PUNCT,
        "space" => WCTYPE_SPACE,
        "upper" => WCTYPE_UPPER,
        "xdigit" => WCTYPE_XDIGIT,
        _ => 0,
    }
}

// TODO Locales

#[no_mangle]
unsafe extern "C" fn iswalnum_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswalnum(wc)
}

#[no_mangle]
unsafe extern "C" fn iswalpha_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswalpha(wc)
}

#[no_mangle]
unsafe extern "C" fn iswblank_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswblank(wc)
}

#[no_mangle]
unsafe extern "C" fn iswcntrl_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswcntrl(wc)
}

#[no_mangle]
unsafe extern "C" fn iswdigit_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswdigit(wc)
}

#[no_mangle]
unsafe extern "C" fn iswgraph_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswgraph(wc)
}

#[no_mangle]
unsafe extern "C" fn iswlower_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswlower(wc)
}

#[no_mangle]
unsafe extern "C" fn iswprint_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswprint(wc)
}

#[no_mangle]
unsafe extern "C" fn iswpunct_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswpunct(wc)
}

#[no_mangle]
unsafe extern "C" fn iswspace_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswspace(wc)
}

#[no_mangle]
unsafe extern "C" fn iswupper_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswupper(wc)
}

#[no_mangle]
unsafe extern "C" fn iswxdigit_l(wc: wint_t, _locale: locale_t) -> c_int {
    iswxdigit(wc)
}

#[no_mangle]
unsafe extern "C" fn towlower_l(wc: wint_t, _locale: locale_t) -> wint_t {
    towlower(wc)
}

#[no_mangle]
unsafe extern "C" fn towupper_l(wc: wint_t, _locale: locale_t) -> wint_t {
    towupper(wc)
}

#[no_mangle]
unsafe extern "C" fn iswctype_l(wc: wint_t, ctype: wctype_t, _locale: locale_t) -> c_int {
    iswctype(wc, ctype)
}

#[no_mangle]
unsafe extern "C" fn towctrans_l(wc: wint_t, trans: wctrans_t, _locale: locale_t) -> wint_t {
    towctrans(wc, trans)
}

#[no_mangle]
unsafe extern "C" fn wctrans_l(s: *const c_char, _locale: locale_t) -> wctrans_t {
    wctrans(s)
}

#[no_mangle]
unsafe extern "C" fn wctype_l(s: *const c_char, _locale: locale_t) -> wctype_t {
    wctype(s)
}
