use core::{
    mem::uninitialized,
    ptr::{copy_nonoverlapping, write},
};
use crate::prelude::{
    Array, DynDenseSto, DynDenseStoMut, DynDenseStoRef, StDenseStoMut, StDenseStoRef,
};

/// A structure just like [`Vec`](Vec), the difference is the inner storage
/// provided by an array.
#[derive(Clone, Debug, Hash, PartialEq, PartialOrd)]
pub struct VecArray<A> {
    array: A,
    len: usize,
}

impl<A> VecArray<A>
where
    A: Array,
{
    /// Creates a new [`VecArray`](CsrMatrix) from an provided array.
    ///
    /// # Arguments
    ///
    /// * `array` - The array storage
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::vec::VecArray;
    /// let _ = VecArray::new([1, 2, 3]);
    /// ```
    pub fn new(array: A) -> Self {
        let len = array.size();
        VecArray { array, len }
    }

    /// Creates a new empty [`VecArray`](VecArray) from an array type.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::vec::VecArray;
    /// let _ = VecArray::<[i32; 10]>::with_capacity();
    /// ```
    pub fn with_capacity() -> Self {
        VecArray {
            array: unsafe { uninitialized() },
            len: 0,
        }
    }

    unsafe fn offset_array_ptr(&mut self, count: usize) -> *mut A::Item {
        self.array.as_mut_slice().as_mut_ptr().add(count)
    }
}

impl<A> StDenseStoRef for VecArray<A>
where
    A: Array,
{
    type Item = A::Item;

    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::vec_array,
    ///     prelude::*,
    /// };
    /// assert_eq!(vec_array().as_slice(), &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
    /// ```
    fn as_slice(&self) -> &[A::Item] {
        let slice = self.array.as_slice();
        &slice[0..self.len]
    }
}

impl<A> StDenseStoMut for VecArray<A>
where
    A: Array,
{
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::vec_array,
    ///     prelude::*,
    /// };
    /// assert_eq!(vec_array().as_mut_slice(), &mut [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
    /// ```
    fn as_mut_slice(&mut self) -> &mut [A::Item] {
        let slice = self.array.as_mut_slice();
        &mut slice[0..self.len]
    }
}

impl<A> DynDenseStoRef for VecArray<A>
where
    A: Array,
{
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::vec_array,
    ///     prelude::*,
    /// };
    /// assert_eq!(vec_array().capacity(), 16);
    /// ```
    fn capacity(&self) -> usize {
        self.array.size()
    }
}

impl<A> DynDenseStoMut for VecArray<A>
where
    A: Array,
{
    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::vec_array,
    ///     prelude::*,
    /// };
    /// let mut a = vec_array();
    /// a.clear();
    /// assert_eq!(a.as_slice(), &[]);
    /// ```
    fn clear(&mut self) {
        self.len = 0;
    }

    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::vec_array,
    ///     prelude::*,
    /// };
    /// let mut a = vec_array();
    /// a.extend(&[1, 2]);
    /// assert_eq!(a.as_slice(), &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2]);
    /// ```
    fn extend(&mut self, other: &[Self::Item])
    where
        Self::Item: Copy,
    {
        let other_len = other.len();
        assert!(self.len + other_len <= self.capacity());
        unsafe {
            copy_nonoverlapping(other.as_ptr(), self.offset_array_ptr(self.len), other_len);
        }
        self.len += other_len;
    }

    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::vec_array,
    ///     prelude::*,
    /// };
    /// let mut a = vec_array();
    /// a.extend_from_clone(&[1, 2]);
    /// assert_eq!(a.as_slice(), &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2]);
    /// ```
    fn extend_from_clone(&mut self, other: &[Self::Item])
    where
        Self::Item: Clone,
    {
        let other_len = other.len();
        assert!(self.len + other_len <= self.capacity());
        other.iter().for_each(|x| unsafe {
            write(self.offset_array_ptr(self.len), x.clone());
            self.len += 1;
        });
    }

    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::vec_array,
    ///     prelude::*,
    /// };
    /// let mut a = vec_array();
    /// a.push(100);
    /// assert_eq!(a.as_slice(), &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100]);
    /// ```
    fn push(&mut self, item: A::Item) {
        assert!(self.len < self.capacity());
        unsafe {
            write(self.offset_array_ptr(self.len), item);
        }
        self.len += 1;
    }

    /// # Examples
    ///
    /// ```rust
    /// use mop_structs::{
    ///     doc_tests::vec_array,
    ///     prelude::*,
    /// };
    /// let mut a = vec_array();
    /// a.truncate(2);
    /// assert_eq!(a.as_slice(), &[1, 2]);
    /// ```
    fn truncate(&mut self, idx: usize) {
        self.len = idx;
    }
}

impl<A> DynDenseSto for VecArray<A>
where
    A: Array,
{
    fn new() -> Self {
        VecArray::<A>::with_capacity()
    }
}
