use core::{alloc, mem::transmute, ptr, sync::atomic::AtomicPtr};


type FuncGloballMalloc = 
    unsafe extern "C" fn(sz: usize, align: usize) -> *mut u8;

type FuncGloballDealloc = 
    unsafe extern "C" fn(ptr: *mut u8) ;

type FuncGloballRealloc = 
    unsafe extern "C" fn(ptr: *mut u8, new_size: usize)  -> *mut u8;

unsafe extern "C" 
fn def_global_malloc_f(_sz: usize, _align: usize) -> *mut u8
{
    return ptr::null_mut();
}

unsafe extern "C" 
fn def_global_dealloc_f(_ptr: *mut u8) 
{
    return;
}

unsafe extern "C" 
fn def_global_realloc_f(_ptr: *mut u8, _new_size: usize)  -> *mut u8
{
    return ptr::null_mut();
}

pub struct CApiSScramGlobalAlloc
{
    fn_alloc: AtomicPtr<FuncGloballMalloc>,
    fn_alloc_zr: AtomicPtr<FuncGloballMalloc>,
    fn_dealloc: AtomicPtr<FuncGloballDealloc>,
    fn_realloc: AtomicPtr<FuncGloballRealloc>
}

impl CApiSScramGlobalAlloc
{
    const 
    fn init() -> Self
    {
        return 
            CApiSScramGlobalAlloc
            {
                fn_alloc: AtomicPtr::new(def_global_malloc_f as *mut _),
                fn_alloc_zr: AtomicPtr::new(def_global_malloc_f as *mut _),
                fn_dealloc: AtomicPtr::new(def_global_dealloc_f as *mut _),
                fn_realloc: AtomicPtr::new(def_global_realloc_f as *mut _),
            };
    }
    
}

unsafe impl alloc::GlobalAlloc for CApiSScramGlobalAlloc
{
    unsafe 
    fn alloc(&self, layout: alloc::Layout) -> *mut u8 
    {
        let alloc_fn: FuncGloballMalloc = 
            transmute(self.fn_alloc.load(core::sync::atomic::Ordering::Relaxed));

        return (alloc_fn)(layout.size(), layout.align());
    }

    unsafe 
    fn alloc_zeroed(&self, layout: alloc::Layout) -> *mut u8 
    {
        let alloc_zr: FuncGloballMalloc = 
            transmute(self.fn_alloc_zr.load(core::sync::atomic::Ordering::Relaxed));

        return (alloc_zr)(layout.size(), layout.align());
    }

    unsafe 
    fn dealloc(&self, ptr: *mut u8, _layout: alloc::Layout) 
    {
        let dealc: FuncGloballDealloc = 
            transmute(self.fn_dealloc.load(core::sync::atomic::Ordering::Relaxed));

        return dealc(ptr);
    }

    unsafe 
    fn realloc(&self, ptr: *mut u8, layout: alloc::Layout, new_size: usize) -> *mut u8 
    {
        let realc: FuncGloballRealloc = 
            transmute(self.fn_realloc.load(core::sync::atomic::Ordering::Relaxed));

        return (realc)(ptr, new_size);
    }
}

#[global_allocator]
static ALLOCATOR: CApiSScramGlobalAlloc = CApiSScramGlobalAlloc::init(); 

#[unsafe(no_mangle)]
pub unsafe extern "C" 
fn set_allocator(
    fn_alloc: Option<FuncGloballMalloc>, 
    fn_alloc_zr: Option<FuncGloballMalloc>,
    fn_dealloc: Option<FuncGloballDealloc>, 
    fn_realloc: Option<FuncGloballRealloc>
) -> i32
{
    if fn_alloc.is_none() == true
    {
        return -1;
    }
    else if fn_alloc_zr.is_none() == true
    {
        return -2;
    }
    else if fn_dealloc.is_none() == true
    {
        return -3;
    }
    else if fn_realloc.is_none() == true
    {
        return -4;
    }

    ALLOCATOR.fn_alloc.store(fn_alloc.unwrap() as *mut _, core::sync::atomic::Ordering::Relaxed);
    ALLOCATOR.fn_alloc_zr.store(fn_alloc_zr.unwrap() as *mut _, core::sync::atomic::Ordering::Relaxed);
    ALLOCATOR.fn_dealloc.store(fn_dealloc.unwrap() as *mut _, core::sync::atomic::Ordering::Relaxed);
    ALLOCATOR.fn_realloc.store(fn_realloc.unwrap() as *mut _, core::sync::atomic::Ordering::Relaxed);

    return 0;
}

