use core::ops::{Deref, DerefMut, Index, IndexMut};

/// Wrapper around a pointer that supports auto-deref, indexing, Send + Sync
pub struct Pointer<T>(*mut T);

impl<T> Pointer<T> {
    /// # Safety
    ///
    /// Up to the user to enusure that the data pointing to exists and is safe
    pub unsafe fn new(ptr: *const T) -> Self {
        Self(ptr as _)
    }

    /// # Safety
    ///
    /// Up to the user to set this to a valid location
    pub unsafe fn null() -> Self {
        Self(0 as _)
    }

    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T> Index<usize> for Pointer<T> {
    type Output = T;

    fn index(&self, index: usize) -> &Self::Output {
        unsafe { &*self.0.add(index) }
    }
}

impl<T> IndexMut<usize> for Pointer<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        unsafe { &mut *self.0.add(index) }
    }
}

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

    fn deref(&self) -> &T {
        unsafe { &(*self.0) }
    }
}

impl<T> DerefMut for Pointer<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut (*self.0) }
    }
}

unsafe impl<T> Send for Pointer<T> {}
unsafe impl<T> Sync for Pointer<T> {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_index() {
        let vals = [1, 2, 3, 4, 5];
        let ptr = unsafe { Pointer::new(&vals[0]) };

        assert_eq!(ptr[0], 1);
        assert_eq!(ptr[1], 2);
        assert_eq!(ptr[2], 3);
        assert_eq!(ptr[3], 4);
        assert_eq!(ptr[4], 5);
    }
}
