//! Traits and implementations for buffers used by this crate.
//!
//! <div class="warning">
//!
//! This trait is exposed only to show implementations on foreign types or
//! implementors. Please use [`encode()`] / [`decode()`] /  [`Hex::encode_hex`]
//! / [`Hex::decode_hex`] instead.
//!
//! </div>
//!
//! [`encode()`]: crate::encode()
//! [`decode()`]: crate::decode()
//! [`Hex::encode_hex`]: crate::traits::Hex::encode_hex
//! [`Hex::decode_hex`]: crate::traits::Hex::decode_hex

#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::mem::MaybeUninit;
use core::slice;

use crate::error::InvalidInput;

#[allow(private_bounds, reason = "Sealed trait.")]
/// [`OwnedBuf`], but uninitialized.
pub(crate) trait OwnedUninitBuf<T> {
    #[doc(hidden)]
    /// Returns a mutable reference to the uninitialized buffer.
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>];

    #[allow(unsafe_code, reason = "XXX")]
    #[doc(hidden)]
    /// Converts the uninitialized buffer into an initialized one.
    ///
    /// ## Safety
    ///
    /// Calling this when the content is not yet fully initialized causes
    /// immediate undefined behavior.
    unsafe fn assume_init(self) -> T;
}

impl<const N: usize> OwnedUninitBuf<[u8; N]> for [MaybeUninit<u8>; N] {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        self
    }

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn assume_init(self) -> [u8; N] {
        unsafe { *((&raw const self).cast()) }
    }
}

#[cfg(feature = "alloc")]
impl OwnedUninitBuf<Vec<u8>> for Box<[MaybeUninit<u8>]> {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        &mut self[..]
    }

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn assume_init(self) -> Vec<u8> {
        unsafe { self.assume_init().into_vec() }
    }
}

#[cfg(feature = "alloc")]
impl OwnedUninitBuf<Box<[u8]>> for Box<[MaybeUninit<u8>]> {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        &mut self[..]
    }

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn assume_init(self) -> Box<[u8]> {
        unsafe { self.assume_init() }
    }
}

#[cfg(feature = "alloc")]
impl OwnedUninitBuf<Arc<[u8]>> for Arc<[MaybeUninit<u8>]> {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        Self::make_mut(self)
    }

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn assume_init(self) -> Arc<[u8]> {
        unsafe { self.assume_init() }
    }
}

#[cfg(feature = "alloc")]
impl OwnedUninitBuf<Rc<[u8]>> for Rc<[MaybeUninit<u8>]> {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        Self::make_mut(self)
    }

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn assume_init(self) -> Rc<[u8]> {
        unsafe { self.assume_init() }
    }
}

#[allow(private_bounds, reason = "Sealed trait.")]
/// An owned output buffer for encoding or decoding hexadecimal strings.
///
/// <div class="warning">
///
/// This trait is exposed only to show implementations on foreign types or
/// implementors. Please use [`Hex::decode_hex`] instead.
///
/// </div>
///
/// [`Hex::decode_hex`]: crate::traits::Hex::decode_hex
pub trait OwnedBuf: Sealed + Sized {
    #[doc(hidden)]
    type Uninit: OwnedUninitBuf<Self>;

    #[doc(hidden)]
    /// Creates a new uninitialized buffer of the given length.
    fn new_uninit_slice(len: usize) -> Result<Self::Uninit, InvalidInput>;
}

impl<const N: usize> OwnedBuf for [u8; N] {
    type Uninit = [MaybeUninit<u8>; N];

    #[inline]
    fn new_uninit_slice(len: usize) -> Result<Self::Uninit, InvalidInput> {
        if len != N {
            return Err(InvalidInput);
        }

        Ok([MaybeUninit::uninit(); N])
    }
}

#[cfg(feature = "alloc")]
impl OwnedBuf for Vec<u8> {
    type Uninit = Box<[MaybeUninit<u8>]>;

    #[inline]
    fn new_uninit_slice(len: usize) -> Result<Self::Uninit, InvalidInput> {
        Ok(Box::new_uninit_slice(len))
    }
}

#[cfg(feature = "alloc")]
impl OwnedBuf for Box<[u8]> {
    type Uninit = Box<[MaybeUninit<u8>]>;

    #[inline]
    fn new_uninit_slice(len: usize) -> Result<Self::Uninit, InvalidInput> {
        Ok(Self::new_uninit_slice(len))
    }
}

#[cfg(feature = "alloc")]
impl OwnedBuf for Arc<[u8]> {
    type Uninit = Arc<[MaybeUninit<u8>]>;

    #[inline]
    fn new_uninit_slice(len: usize) -> Result<Self::Uninit, InvalidInput> {
        Ok(Self::new_uninit_slice(len))
    }
}

#[cfg(feature = "alloc")]
impl OwnedBuf for Rc<[u8]> {
    type Uninit = Rc<[MaybeUninit<u8>]>;

    #[inline]
    fn new_uninit_slice(len: usize) -> Result<Self::Uninit, InvalidInput> {
        Ok(Self::new_uninit_slice(len))
    }
}

#[allow(private_bounds, reason = "Sealed trait.")]
/// [`OwnedBuf`], but specialized for encoding hexadecimal strings.
///
/// <div class="warning">
///
/// This trait is exposed only to show implementations on foreign types or
/// implementors. Please use [`Hex::encode_hex`] instead.
///
/// </div>
///
/// [`Hex::encode_hex`]: crate::traits::Hex::encode_hex
pub trait StringBuf: Sealed {
    #[doc(hidden)]
    type Buf: OwnedBuf;

    #[allow(unsafe_code, reason = "XXX")]
    #[doc(hidden)]
    /// Converts owned bytes to a string without checking that the string
    /// contains valid UTF-8.
    ///
    /// ## Safety
    ///
    /// `buf` must contain only valid UTF-8 bytes.
    unsafe fn from_utf8_unchecked(buf: Self::Buf) -> Self;
}

impl<T> StringBuf for T
where
    T: OwnedBuf,
{
    type Buf = Self;

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn from_utf8_unchecked(buf: Self::Buf) -> Self {
        buf
    }
}

#[cfg(feature = "alloc")]
impl StringBuf for String {
    type Buf = Vec<u8>;

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn from_utf8_unchecked(buf: Self::Buf) -> Self {
        unsafe { Self::from_utf8_unchecked(buf) }
    }
}

#[cfg(feature = "alloc")]
impl StringBuf for Box<str> {
    type Buf = Box<[u8]>;

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn from_utf8_unchecked(buf: Self::Buf) -> Self {
        unsafe { Self::from_raw(Box::into_raw(buf) as *mut str) }
    }
}

#[cfg(feature = "alloc")]
impl StringBuf for Arc<str> {
    type Buf = Arc<[u8]>;

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn from_utf8_unchecked(buf: Self::Buf) -> Self {
        unsafe { Self::from_raw(Arc::into_raw(buf) as *const str) }
    }
}

#[cfg(feature = "alloc")]
impl StringBuf for Rc<str> {
    type Buf = Rc<[u8]>;

    #[allow(unsafe_code, reason = "XXX")]
    #[inline]
    unsafe fn from_utf8_unchecked(buf: Self::Buf) -> Self {
        unsafe { Self::from_raw(Rc::into_raw(buf) as *const str) }
    }
}

#[allow(private_bounds, reason = "Sealed trait.")]
/// An output buffer for encoding or decoding hexadecimal strings.
///
/// <div class="warning">
///
/// This trait is exposed only to show implementations on foreign types or
/// implementors. Please use [`encode()`] / [`decode()`] instead.
///
/// </div>
///
/// [`encode()`]: crate::encode()
/// [`decode()`]: crate::decode()
pub trait Buf: Sealed {
    #[doc(hidden)]
    /// Returns the uninitialized portion of the buffer where the encoded or
    /// decoded bytes can be written to.
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>];

    #[allow(unsafe_code, reason = "See below.")]
    #[doc(hidden)]
    /// Assumes that the next `additional` bytes (starting from the current
    /// initialized offset) are properly initialized, and returns a reference to
    /// the newly initialized portion of the buffer.
    ///
    /// ## Safety
    ///
    /// 1. `additional` <= `self.as_uninit().len()`.
    /// 1. `additional` bytes starting from the current initialized offset are
    ///    properly initialized.
    unsafe fn assume_init_ref(&mut self, additional: usize) -> &[u8];
}

impl<T> Buf for &mut T
where
    T: Buf,
{
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        Buf::as_uninit(*self)
    }

    #[inline]
    #[allow(unsafe_code, reason = "XXX")]
    unsafe fn assume_init_ref(&mut self, additional: usize) -> &[u8] {
        unsafe { Buf::assume_init_ref(*self, additional) }
    }
}

impl Buf for [MaybeUninit<u8>] {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        self
    }

    #[inline]
    #[allow(unsafe_code, reason = "XXX")]
    unsafe fn assume_init_ref(&mut self, additional: usize) -> &[u8] {
        debug_assert!(additional <= self.len());

        unsafe { slice::from_raw_parts(self.as_ptr().cast(), additional) }
    }
}

impl<const N: usize> Buf for [MaybeUninit<u8>; N] {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        self
    }

    #[inline]
    #[allow(unsafe_code, reason = "XXX")]
    unsafe fn assume_init_ref(&mut self, additional: usize) -> &[u8] {
        debug_assert!(additional <= N);

        unsafe { slice::from_raw_parts(self.as_ptr().cast(), additional) }
    }
}

impl Buf for [u8] {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        #[allow(unsafe_code, reason = "XXX")]
        unsafe {
            &mut *((&raw mut *self) as *mut [MaybeUninit<u8>])
        }
    }

    #[inline]
    #[allow(unsafe_code, reason = "XXX")]
    unsafe fn assume_init_ref(&mut self, additional: usize) -> &[u8] {
        debug_assert!(additional <= self.len());

        unsafe { slice::from_raw_parts(self.as_ptr(), additional) }
    }
}

impl<const N: usize> Buf for [u8; N] {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        #[allow(unsafe_code, reason = "XXX")]
        unsafe {
            &mut *((&raw mut *self).cast::<[MaybeUninit<u8>; N]>())
        }
    }

    #[inline]
    #[allow(unsafe_code, reason = "XXX")]
    unsafe fn assume_init_ref(&mut self, additional: usize) -> &[u8] {
        debug_assert!(additional <= N);

        unsafe { slice::from_raw_parts(self.as_ptr(), additional) }
    }
}

#[cfg(feature = "alloc")]
impl Buf for Vec<u8> {
    #[inline]
    fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
        self.spare_capacity_mut()
    }

    #[inline]
    #[allow(unsafe_code, reason = "XXX")]
    unsafe fn assume_init_ref(&mut self, additional: usize) -> &[u8] {
        let len = self.len();

        unsafe {
            self.set_len(len + additional);
        }

        &self[len..]
    }
}

trait Sealed {}

impl<const N: usize> Sealed for [MaybeUninit<u8>; N] {}

#[cfg(feature = "alloc")]
impl Sealed for Vec<u8> {}

#[cfg(feature = "alloc")]
impl Sealed for Box<[MaybeUninit<u8>]> {}

#[cfg(feature = "alloc")]
impl Sealed for Arc<[MaybeUninit<u8>]> {}

#[cfg(feature = "alloc")]
impl Sealed for Rc<[MaybeUninit<u8>]> {}

impl<const N: usize> Sealed for [u8; N] {}

#[cfg(feature = "alloc")]
impl Sealed for Box<[u8]> {}

#[cfg(feature = "alloc")]
impl Sealed for Arc<[u8]> {}

#[cfg(feature = "alloc")]
impl Sealed for Rc<[u8]> {}

#[cfg(feature = "alloc")]
impl Sealed for String {}

#[cfg(feature = "alloc")]
impl Sealed for Box<str> {}

#[cfg(feature = "alloc")]
impl Sealed for Arc<str> {}

#[cfg(feature = "alloc")]
impl Sealed for Rc<str> {}

impl<T: Sealed> Sealed for &mut T {}

impl Sealed for [MaybeUninit<u8>] {}

impl Sealed for [u8] {}
