#![allow(unused_variables)]

use core::{
    ffi::{c_char, c_int, c_long, c_size_t, c_uchar, c_void},
    ptr::null_mut,
};

use super::alloc::__xkbc_malloc;

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memset(
    dest_original: *mut c_void,
    c: c_int,
    n: c_size_t,
) -> *mut c_void {
    if dest_original.is_null() {
        if n > 0 {
            panic!("Attempted to memset a nullptr.");
        } else {
            return null_mut();
        }
    }

    let mut dest = dest_original as *mut u8;
    let c = c as u8;
    for _ in 0..n {
        unsafe {
            *dest = c;
            dest = dest.add(1);
        }
    }

    dest_original
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __spre_memset(
    dest_original: *mut c_void,
    c: c_int,
    n: c_size_t,
) -> *mut c_void {
    unsafe { __xkbc_memset(dest_original, c, n) }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memmove(
    dst: *mut c_void,
    src: *const c_void,
    count: c_size_t,
) -> *mut c_void {
    unsafe { core::ptr::copy(src, dst, count) };
    dst
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memcpy(
    dst: *mut c_void,
    src: *const c_void,
    count: c_size_t,
) -> *mut c_void {
    unsafe {
        core::ptr::copy_nonoverlapping(src, dst, count);
    }
    dst
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __spre_memcpy(
    dst: *mut c_void,
    src: *const c_void,
    count: c_size_t,
) -> *mut c_void {
    unsafe { __xkbc_memcpy(dst, src, count) }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memchr(s: *const c_void, c: c_int, n: c_size_t) -> *mut c_void {
    let mut s = s as *const c_uchar;
    let c = c as c_uchar;

    for _ in 0..n {
        unsafe {
            if *s == c {
                return s as *mut c_void;
            }

            s = s.add(1);
        }
    }

    null_mut()
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memcmp(s1: *const c_char, s2: *const c_char, n: c_size_t) -> c_int {
    unsafe {
        for i in 0..n as isize {
            let s1_i = s1.offset(i);
            let s2_i = s2.offset(i);
            let val = *s1_i as c_int - *s2_i as c_int;

            if val != 0 {
                return val;
            }
        }

        0
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __spre_memcmp(s1: *const c_char, s2: *const c_char, n: c_size_t) -> c_int {
    unsafe { __xkbc_memcmp(s1, s2, n) }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strpbrk(s: *const c_char, accept: *const c_char) -> *mut c_char {
    todo!()
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strerror(errnum: c_int) -> *mut c_char {
    todo!()
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __spre_strerror(errnum: c_int) -> *mut c_char {
    unsafe { __xkbc_strerror(errnum) }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strdup(string: *const c_char) -> *mut c_char {
    strndup_inner(string, unsafe { __xkbc_strlen(string) })
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strndup(string: *const c_char, max_len: c_size_t) -> *mut c_char {
    strndup_inner(string, unsafe { __xkbc_strnlen(string, max_len) })
}

fn strndup_inner(string: *const c_char, computed_len: c_size_t) -> *mut c_char {
    let new = unsafe { __xkbc_malloc(computed_len + 1) } as *mut c_char;

    if new.is_null() {
        return null_mut();
    }

    unsafe {
        *new.add(computed_len) = b'\0';

        __xkbc_memcpy(new as *mut c_void, string as *const c_void, computed_len) as *mut c_char
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strlen(mut s: *const c_char) -> c_size_t {
    let mut result = 0;
    unsafe {
        while *s != 0 {
            s = s.offset(1);
            result += 1;
        }
    }
    result
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __spre_strlen(s: *const c_char) -> c_size_t {
    unsafe { __xkbc_strlen(s) }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strnlen(s: *const c_char, maxlen: c_size_t) -> c_size_t {
    let found: *const c_char =
        unsafe { __xkbc_memchr(s as *const c_void, b'\0' as c_int, maxlen) } as *const c_char;
    if !found.is_null() {
        unsafe { found.offset_from(s) as c_size_t }
    } else {
        maxlen
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strncmp(
    s1: *const c_char,
    s2: *const c_char,
    n: c_size_t,
) -> c_int {
    unsafe {
        for i in 0..n as isize {
            let s1_i = s1.offset(i);
            let s2_i = s2.offset(i);
            let val = *s1_i as c_int - *s2_i as c_int;

            if val != 0 || *s1_i == 0 {
                return val;
            }
        }

        0
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __spre_strncmp(
    s1: *const c_char,
    s2: *const c_char,
    n: c_size_t,
) -> c_int {
    unsafe { __xkbc_strncmp(s1, s2, n) }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strchr(cs: *const c_char, c: c_int) -> *mut c_char {
    todo!()
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strcmp(s1: *const c_char, s2: *const c_char) -> c_int {
    unsafe {
        for i in 0_isize.. {
            let s1_i = s1.offset(i);
            let s2_i = s2.offset(i);
            let val = *s1_i as c_int - *s2_i as c_int;

            if val != 0 || *s1_i == 0 {
                return val;
            }
        }

        0
    }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strtol(
    nptr: *const c_char,
    endptr: *mut *mut c_char,
    base: c_int,
) -> c_long {
    todo!()
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn __spre_strtol(
    nptr: *const c_char,
    endptr: *mut *mut c_char,
    base: c_int,
) -> c_long {
    unsafe { __xkbc_strtol(nptr, endptr, base) }
}
