#![cfg_attr(doc, doc(cfg(not(feature = "vmem"))))]
#![cfg(any(not(feature = "vmem"), doc))]

pub mod rb;

use core::ops::Index;

use crate::iterators::{ConsIter, ProdIter, WorkIter};
use crate::ring_buffer::storage::{MRBIndex, Storage};
use crate::{MutRB, UnsafeSyncCell};

/// Stack-allocated storage.
pub struct StackStorage<T, const N: usize> {
    inner: [UnsafeSyncCell<T>; N],
}

impl<T, const N: usize> From<[T; N]> for StackStorage<T, N> {
    fn from(value: [T; N]) -> StackStorage<T, N> {
        let value = core::mem::ManuallyDrop::new(value);
        let ptr = &value as *const _ as *const [UnsafeSyncCell<T>; N];

        Self {
            inner: unsafe { ptr.read() },
        }
    }
}

impl<T, const N: usize> From<[UnsafeSyncCell<T>; N]> for StackStorage<T, N> {
    fn from(value: [UnsafeSyncCell<T>; N]) -> StackStorage<T, N> {
        Self { inner: value }
    }
}

impl<T, const N: usize> Index<usize> for StackStorage<T, N> {
    type Output = UnsafeSyncCell<T>;

    #[inline]
    fn index(&self, index: usize) -> &Self::Output {
        assert!(
            index < N,
            "index out of bounds: the len is {N} but the index is {index}"
        );
        unsafe { self.inner.get_unchecked(index) }
    }
}

impl<T, const N: usize> MRBIndex<usize> for StackStorage<T, N> {
    type Output = UnsafeSyncCell<T>;

    #[inline]
    fn _index(&self, index: usize) -> &Self::Output {
        unsafe { self.inner.get_unchecked(index) }
    }
}

impl<T, const N: usize> Storage for StackStorage<T, N> {
    type Item = T;

    #[inline]
    fn as_ptr(&self) -> *const UnsafeSyncCell<Self::Item> {
        self.inner.as_ptr()
    }

    #[inline]
    fn as_mut_ptr(&mut self) -> *mut UnsafeSyncCell<Self::Item> {
        self.inner.as_mut_ptr()
    }

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

/// Trait needed to call `split` method on a stack-allocated buffer.
pub trait StackSplit<B: MutRB> {
    /// Borrows the buffer, yielding two iterators. See:
    /// - [`ProdIter`];
    /// - [`ConsIter`].
    fn split(&'_ mut self) -> (ProdIter<'_, B>, ConsIter<'_, B, false>);

    /// Borrows the buffer, yielding three iterators. See:
    /// - [`ProdIter`];
    /// - [`WorkIter`];
    /// - [`ConsIter`].
    fn split_mut(&'_ mut self) -> (ProdIter<'_, B>, WorkIter<'_, B>, ConsIter<'_, B, true>);
}

pub mod test {
    #[test]
    fn from_tests() {
        use crate::{StackStorage, UnsafeSyncCell};

        let _ = StackStorage::from([0; 100]);
        let _: StackStorage<i32, 100> =
            StackStorage::from(core::array::from_fn(|_| UnsafeSyncCell::new(0)));
    }
}
