use core::{
    alloc::{AllocError, Allocator, GlobalAlloc, Layout},
    ptr::{null_mut, NonNull},
};

use crate::{allocator::BucketAllocator, sys::OsPageProvider, util::Spinlock};

pub struct GlobalAllocator;

unsafe impl GlobalAlloc for GlobalAllocator {
    #[inline]
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        let ptr = GLOBAL_ALLOCATOR.lock().allocate(layout);

        if let Some(ptr) = ptr {
            ptr.as_ptr()
        } else {
            null_mut()
        }
    }

    #[inline]
    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        let ptr = NonNull::new(ptr).expect("Invalid pointer");
        GLOBAL_ALLOCATOR.lock().free(ptr, layout);
    }
}

unsafe impl Allocator for GlobalAllocator {
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        let ptr = GLOBAL_ALLOCATOR.lock().allocate(layout).ok_or(AllocError)?;
        Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
    }

    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
        GLOBAL_ALLOCATOR.lock().free(ptr, layout);
    }
}

static GLOBAL_ALLOCATOR: Spinlock<BucketAllocator<OsPageProvider>> =
    Spinlock::new(BucketAllocator::new());
