use core::{
    cell::UnsafeCell,
    ops::{Deref, DerefMut},
};

/// Similar to [`std::cell::Cell`]
/// Except allows deref and deref_mut
///
/// Also allows easy mutation using interior mutability
pub struct EasyCell<T> {
    inner: UnsafeCell<T>,
}

impl<T> EasyCell<T> {
    pub fn new(val: T) -> Self {
        Self {
            inner: UnsafeCell::new(val),
        }
    }

    pub fn set(&mut self, val: T) {
        *self.deref_mut() = val;
    }

    /// Mutate the inner value
    ///
    /// ```
    /// use gemstone::cell::EasyCell;
    ///
    /// let cell = EasyCell::new(false);
    /// cell.modify(|x| *x ^= true);
    /// assert_eq!(*cell, true);
    /// ```
    pub fn modify<F: Fn(&mut T)>(&self, func: F) {
        func(unsafe { &mut *self.inner.get() })
    }

    /// Replace the inner value
    ///
    /// ```
    /// use gemstone::cell::EasyCell;
    ///
    /// let cell = EasyCell::new(false);
    /// cell.replace(|x| x ^ true);
    /// assert_eq!(*cell, true);
    /// ```
    pub fn replace<F: Fn(T) -> T>(&self, func: F)
    where
        T: Copy,
    {
        unsafe {
            *self.inner.get() = func(*self.inner.get());
        }
    }
}

impl<T> Deref for EasyCell<T> {
    type Target = T;

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

impl<T> DerefMut for EasyCell<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.inner.get_mut()
    }
}
