#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::{
    borrow::Borrow,
    fmt,
    hash::{Hash, Hasher},
    mem::MaybeUninit,
    ops::{Deref, DerefMut},
    slice,
};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CapacityError;

pub struct InlineArray<T, const N: usize> {
    buf: [MaybeUninit<T>; N],
    len: usize,
}

impl<T, const N: usize> InlineArray<T, N> {
    #[inline]
    pub const fn new() -> Self {
        Self {
            buf: [const { MaybeUninit::uninit() }; N],
            len: 0,
        }
    }

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

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

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

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

    #[inline]
    pub fn as_slice(&self) -> &[T] {
        // SAFETY: only the first `len` elements are initialized.
        unsafe { slice::from_raw_parts(self.buf.as_ptr().cast::<T>(), self.len) }
    }

    #[inline]
    pub fn as_mut_slice(&mut self) -> &mut [T] {
        // SAFETY: only the first `len` elements are initialized.
        unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast::<T>(), self.len) }
    }

    #[inline]
    pub fn push(&mut self, value: T) -> Result<(), T> {
        if self.len == N {
            return Err(value);
        }

        self.buf[self.len].write(value);
        self.len += 1;
        Ok(())
    }

    #[inline]
    pub fn pop(&mut self) -> Option<T> {
        if self.len == 0 {
            return None;
        }

        self.len -= 1;
        // SAFETY: element at `len` was initialized and is now being moved out exactly once.
        Some(unsafe { self.buf[self.len].assume_init_read() })
    }

    #[inline]
    pub fn clear(&mut self) {
        self.truncate(0);
    }

    #[inline]
    pub fn truncate(&mut self, new_len: usize) {
        let new_len = new_len.min(self.len);
        while self.len > new_len {
            self.len -= 1;
            // SAFETY: indices below old `len` and >= new `len` were initialized.
            unsafe { self.buf[self.len].assume_init_drop() };
        }
    }

    #[inline]
    pub fn try_extend<I>(&mut self, iter: I) -> Result<(), CapacityError>
    where
        I: IntoIterator<Item = T>,
    {
        for item in iter {
            if self.push(item).is_err() {
                return Err(CapacityError);
            }
        }

        Ok(())
    }

    #[inline]
    pub fn try_from_iter<I>(iter: I) -> Result<Self, CapacityError>
    where
        I: IntoIterator<Item = T>,
    {
        let mut out = Self::new();
        out.try_extend(iter)?;
        Ok(out)
    }
}

impl<T, const N: usize> Default for InlineArray<T, N> {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl<T, const N: usize> Drop for InlineArray<T, N> {
    fn drop(&mut self) {
        self.clear();
    }
}

impl<T: Clone, const N: usize> Clone for InlineArray<T, N> {
    fn clone(&self) -> Self {
        let mut out = Self::new();
        for item in self.as_slice() {
            assert!(
                out.push(item.clone()).is_ok(),
                "stx::InlineArray clone capacity exceeded"
            );
        }
        out
    }
}

impl<T: fmt::Debug, const N: usize> fmt::Debug for InlineArray<T, N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_list().entries(self.as_slice()).finish()
    }
}

impl<T: PartialEq, const N: usize> PartialEq for InlineArray<T, N> {
    fn eq(&self, other: &Self) -> bool {
        self.as_slice() == other.as_slice()
    }
}

impl<T: Eq, const N: usize> Eq for InlineArray<T, N> {}

impl<T: PartialOrd, const N: usize> PartialOrd for InlineArray<T, N> {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        self.as_slice().partial_cmp(other.as_slice())
    }
}

impl<T: Ord, const N: usize> Ord for InlineArray<T, N> {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        self.as_slice().cmp(other.as_slice())
    }
}

impl<T: Hash, const N: usize> Hash for InlineArray<T, N> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_slice().hash(state);
    }
}

impl<T, const N: usize> Borrow<[T]> for InlineArray<T, N> {
    fn borrow(&self) -> &[T] {
        self.as_slice()
    }
}

impl<T, const N: usize> AsRef<[T]> for InlineArray<T, N> {
    fn as_ref(&self) -> &[T] {
        self.as_slice()
    }
}

impl<T, const N: usize> AsMut<[T]> for InlineArray<T, N> {
    fn as_mut(&mut self) -> &mut [T] {
        self.as_mut_slice()
    }
}

impl<T, const N: usize> Deref for InlineArray<T, N> {
    type Target = [T];

    fn deref(&self) -> &Self::Target {
        self.as_slice()
    }
}

impl<T, const N: usize> DerefMut for InlineArray<T, N> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.as_mut_slice()
    }
}

impl<'a, T, const N: usize> IntoIterator for &'a InlineArray<T, N> {
    type Item = &'a T;
    type IntoIter = slice::Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.as_slice().iter()
    }
}

impl<'a, T, const N: usize> IntoIterator for &'a mut InlineArray<T, N> {
    type Item = &'a mut T;
    type IntoIter = slice::IterMut<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        self.as_mut_slice().iter_mut()
    }
}

impl<T, const N: usize> Extend<T> for InlineArray<T, N> {
    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
        for item in iter {
            assert!(
                self.push(item).is_ok(),
                "stx::InlineArray capacity exceeded"
            );
        }
    }
}

impl<T, const N: usize> FromIterator<T> for InlineArray<T, N> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        let mut out = Self::new();
        out.extend(iter);
        out
    }
}

impl<T: Clone, const N: usize> TryFrom<&[T]> for InlineArray<T, N> {
    type Error = CapacityError;

    fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
        Self::try_from_iter(slice.iter().cloned())
    }
}

// The macro "inline_array!"
#[macro_export]
macro_rules! inline_array {
    ($($elem:expr),* $(,)?) => {{
        let mut arr = stx::InlineArray::new();
        $(arr.push($elem).unwrap();)*
        arr
    }};
}
#[cfg(feature = "alloc")]
impl<T, const N: usize> TryFrom<Vec<T>> for InlineArray<T, N> {
    type Error = CapacityError;

    fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
        Self::try_from_iter(value)
    }
}

#[cfg(test)]
mod tests;
