use super::*;
use std::alloc::{alloc_zeroed, dealloc, Layout};

// This is necessary to sidestep the stacked borrows model (theoretically): a raw pointer is
// required so that a borrow might be derived from it, but `Box<T>` receives special treatment.
pub struct BoxBytes<const LEN: usize>(*mut Bytes<LEN>);

impl<const LEN: usize> Clone for BoxBytes<LEN> {
    fn clone(&self) -> Self {
        let ret = BoxBytes::new_zeroed();
        unsafe {
            std::ptr::copy(self.as_ptr(), ret.as_mut_ptr(), LEN);
        }
        ret
    }
}

impl<const LEN: usize> BoxBytes<LEN> {
    /// # Safety
    /// The given pointer must point to a valid value of type `Bytes<LEN>` and be valid for reads
    /// and writes for the entire lifetime of this value.
    #[inline]
    pub unsafe fn new(ptr: *mut Bytes<LEN>) -> Self {
        BoxBytes(ptr)
    }

    #[inline]
    pub fn new_zeroed() -> Self {
        unsafe { BoxBytes(alloc_zeroed(Layout::new::<Bytes<LEN>>()) as *mut Bytes<LEN>) }
    }

    #[inline]
    pub const fn len(&self) -> usize {
        LEN
    }

    #[inline]
    pub const fn is_empty(&self) -> bool {
        self.len() == 0
    }

    #[inline]
    pub fn into_inner(self) -> *mut Bytes<LEN> {
        self.0
    }

    #[inline]
    pub fn as_ptr(&self) -> *const u8 {
        self.0 as *const u8
    }

    pub fn as_mut_ptr(&self) -> *mut u8 {
        self.0 as *mut u8
    }

    #[inline]
    pub fn as_arr_ptr(&self) -> *mut [u8; LEN] {
        self.0 as *mut [u8; LEN]
    }

    #[inline]
    pub fn as_bytes_ptr(&self) -> *mut Bytes<LEN> {
        self.0
    }

    #[inline]
    pub fn as_slice_ptr(&self) -> *mut [u8] {
        core::ptr::slice_from_raw_parts_mut(self.0 as *mut u8, LEN)
    }

    /// # Safety
    /// The lifetime of the returned value must not intersect with those of other unique references
    /// to the slice.
    #[inline]
    pub unsafe fn as_byte_slice(&self) -> ByteSlice {
        ByteSlice::new(&*self.as_slice_ptr())
    }

    /// # Safety
    /// The lifetime of the returned value must not intersect with those of other references to the
    /// slice.
    #[inline]
    pub unsafe fn as_byte_mut_slice(&self) -> ByteMutSlice {
        ByteMutSlice::new(&mut *self.as_slice_ptr())
    }
}

impl<const LEN: usize> Drop for BoxBytes<LEN> {
    #[inline]
    fn drop(&mut self) {
        unsafe { dealloc(self.0 as *mut u8, Layout::new::<Bytes<LEN>>()) }
    }
}

impl<const LEN: usize> From<Box<Bytes<LEN>>> for BoxBytes<LEN> {
    #[inline]
    fn from(other: Box<Bytes<LEN>>) -> Self {
        BoxBytes(Box::into_raw(other))
    }
}

impl<const LEN: usize> From<BoxBytes<LEN>> for Box<Bytes<LEN>> {
    #[inline]
    fn from(other: BoxBytes<LEN>) -> Self {
        let result = unsafe { Box::from_raw(other.0) };
        mem::forget(other);
        result
    }
}

impl_byteorder!(BoxBytes<const LEN: usize>);
