use core::ptr::NonNull;

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
use core::{
    cell::UnsafeCell,
    ops::{Deref, DerefMut},
    sync::atomic::{AtomicBool, Ordering},
};

pub enum Assert<const T: bool> {}
pub trait IsTrue {}
impl IsTrue for Assert<true> {}

pub trait NonNullExt<T> {
    unsafe fn add_ext(self, offset: usize) -> Self;
}

impl<T> NonNullExt<T> for NonNull<T> {
    unsafe fn add_ext(self, offset: usize) -> Self {
        NonNull::new_unchecked(self.as_ptr().add(offset))
    }
}

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
pub struct Spinlock<T: ?Sized> {
    state: AtomicBool,
    data: UnsafeCell<T>,
}

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
pub struct SpinlockGuard<'a, T: ?Sized> {
    lock: &'a Spinlock<T>,
}

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
impl<T: ?Sized> Spinlock<T> {
    pub const fn new(value: T) -> Self
    where
        T: Sized,
    {
        Self {
            state: AtomicBool::new(false),
            data: UnsafeCell::new(value),
        }
    }

    pub fn lock<'a>(&'a self) -> SpinlockGuard<'a, T> {
        while self
            .state
            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
            .is_err()
        {
            core::hint::spin_loop();
        }

        // Locked
        SpinlockGuard { lock: self }
    }
}

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.lock.data.get() }
    }
}

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { &mut *self.lock.data.get() }
    }
}

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
    fn drop(&mut self) {
        self.lock.state.store(false, Ordering::Release);
    }
}

#[cfg(any(not(feature = "dep-of-kernel"), rust_analyzer))]
unsafe impl<T: ?Sized> Sync for Spinlock<T> {}
