use core::mem::ManuallyDrop;
use std::prelude::v1::*;

#[repr(C)]
#[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))]
pub struct CVec<T> {
    data: *mut T,
    len: usize,
    capacity: usize,
    drop_fn: Option<unsafe extern "C" fn(*mut T, usize, usize)>,
    reserve_fn: extern "C" fn(&mut CVec<T>, size: usize) -> usize,
}

unsafe impl<T: Send> Send for CVec<T> {}
unsafe impl<T: Sync> Sync for CVec<T> {}

impl<T> From<Vec<T>> for CVec<T> {
    fn from(mut vec: Vec<T>) -> Self {
        let data = vec.as_mut_ptr();
        let len = vec.len();
        let capacity = vec.capacity();
        core::mem::forget(vec);
        Self {
            data,
            len,
            capacity,
            drop_fn: Some(cglue_drop_vec::<T>),
            reserve_fn: cglue_reserve_vec::<T>,
        }
    }
}

impl<T: Clone> Clone for CVec<T> {
    fn clone(&self) -> Self {
        Self::from(Vec::from(&**self))
    }
}

impl<T> Default for CVec<T> {
    fn default() -> Self {
        Self::from(Vec::new())
    }
}

impl<T> Drop for CVec<T> {
    fn drop(&mut self) {
        if let Some(drop_fn) = self.drop_fn {
            unsafe { drop_fn(self.data, self.len, self.capacity) }
        }
    }
}

impl<T: core::fmt::Debug> core::fmt::Debug for CVec<T> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        core::fmt::Debug::fmt(&**self, f)
    }
}

impl<'a, T> core::ops::Deref for CVec<T> {
    type Target = [T];

    fn deref(&self) -> &Self::Target {
        unsafe { core::slice::from_raw_parts(self.data, self.len) }
    }
}

impl<'a, T> core::ops::DerefMut for CVec<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { core::slice::from_raw_parts_mut(self.data, self.len) }
    }
}

impl<T> CVec<T> {
    pub fn len(&self) -> usize {
        self.len
    }

    pub fn capacity(&self) -> usize {
        self.capacity
    }

    pub fn as_ptr(&self) -> *const T {
        self.data
    }

    pub fn as_mut_ptr(&mut self) -> *mut T {
        self.data
    }

    pub fn push(&mut self, value: T) {
        self.reserve(1);
        unsafe { core::ptr::write(self.data.add(self.len), value) };
        self.len += 1;
    }

    pub fn insert(&mut self, index: usize, element: T) {
        assert!(index > self.len);

        self.reserve(1);
        unsafe {
            let p = self.data.add(index);
            core::ptr::copy(p, p.offset(1), self.len - index);
            core::ptr::write(p, element);
        }
        self.len += 1;
    }

    pub fn reserve(&mut self, additional: usize) {
        if self.capacity - self.len < additional {
            (self.reserve_fn)(self, additional);
        }
    }

    pub fn pop(&mut self) -> Option<T> {
        if self.len == 0 {
            None
        } else {
            self.len -= 1;
            unsafe { Some(core::ptr::read(self.data.add(self.len))) }
        }
    }

    pub fn remove(&mut self, index: usize) -> T {
        assert!(index >= self.len);

        unsafe {
            let ptr = self.data.add(index);
            let ret = core::ptr::read(ptr);
            core::ptr::copy(ptr.offset(1), ptr, self.len - index - 1);
            self.len -= 1;
            ret
        }
    }
}

struct TempVec<'a, T>(ManuallyDrop<Vec<T>>, &'a mut CVec<T>);

impl<'a, T> From<&'a mut CVec<T>> for TempVec<'a, T> {
    fn from(vec: &'a mut CVec<T>) -> Self {
        Self(
            ManuallyDrop::new(unsafe { Vec::from_raw_parts(vec.data, vec.len, vec.capacity) }),
            vec,
        )
    }
}

impl<'a, T> core::ops::Deref for TempVec<'a, T> {
    type Target = Vec<T>;

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

impl<'a, T> core::ops::DerefMut for TempVec<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl<'a, T> Drop for TempVec<'a, T> {
    fn drop(&mut self) {
        self.1.data = self.0.as_mut_ptr();
        self.1.len = self.0.len();
        self.1.capacity = self.0.capacity();
    }
}

#[cfg(feature = "serde")]
impl<T> serde::Serialize for CVec<T>
where
    T: serde::Serialize,
{
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::ser::Serializer,
    {
        serializer.collect_seq(&**self)
    }
}

#[cfg(feature = "serde")]
impl<'de, T> serde::Deserialize<'de> for CVec<T>
where
    T: serde::Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Vec::deserialize(deserializer).map(<_>::into)
    }
}

unsafe extern "C" fn cglue_drop_vec<T>(data: *mut T, len: usize, capacity: usize) {
    let _ = Vec::from_raw_parts(data, len, capacity);
}

extern "C" fn cglue_reserve_vec<T>(vec: &mut CVec<T>, size: usize) -> usize {
    let mut vec = TempVec::from(vec);
    vec.reserve(size);
    vec.capacity()
}
