use std::{
    alloc::Layout,
    marker::PhantomData,
    mem::MaybeUninit,
    ops::{Deref, DerefMut},
};

use crate::{backend::CpuBackend, mem::CopyDirection, Allocator, Backend};

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Init<T, A = CpuBackend> {
    inner: T,
    _marker: PhantomData<A>,
}

impl<T, A: Allocator> Init<T, A> {
    #[inline]
    pub const fn as_ptr(&self) -> *const T {
        &self.inner
    }

    #[inline]
    pub fn as_mut_ptr(&mut self) -> *mut T {
        &mut self.inner
    }

    pub fn copy_into_host(&self, alloc: &A) -> T
    where
        A: Backend,
        T: Copy,
    {
        let mut value = MaybeUninit::<T>::uninit();
        let layout = Layout::new::<T>();
        unsafe {
            alloc
                .copy_nonoverlapping(
                    self.as_ptr() as *const u8,
                    value.as_mut_ptr() as *mut u8,
                    layout.size(),
                    CopyDirection::DeviceToHost,
                )
                .unwrap();

            value.assume_init()
        }
    }
}

impl<T> Deref for Init<T, CpuBackend> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T> DerefMut for Init<T, CpuBackend> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

impl<T: Clone> Clone for Init<T, CpuBackend> {
    fn clone(&self) -> Self {
        Self { inner: self.inner.clone(), _marker: PhantomData }
    }
}

impl<T: Copy> Copy for Init<T, CpuBackend> {}
