use crate::{
    memory_reclaim::memory_state::MemoryState, Node, NodeDataLazyClose, NodeRefNone, NodeRefSingle,
    NodeRefsArray, NodeRefsVec, SelfRefColMut, Variant,
};
use orx_split_vec::prelude::PinnedVec;
pub trait MemoryReclaimPolicy: Default + Clone + Copy {
    fn reclaim_closed_nodes<'rf, 'a, V, T, P>(vec_mut: &mut SelfRefColMut<'rf, 'a, V, T, P>)
    where
        T: 'a,
        V: Variant<'a, T, Storage = NodeDataLazyClose<T>>,
        P: PinnedVec<Node<'a, V, T>> + 'a,
        SelfRefColMut<'rf, 'a, V, T, P>: Reclaim<V::Prev, V::Next>;
    fn at_the_same_state_as(&self, collection: &Self) -> bool;
    fn successor_state(&self) -> Self;
}
#[derive(Default, Clone, Copy)]
pub struct MemoryReclaimNever(pub(crate) MemoryState);
impl MemoryReclaimPolicy for MemoryReclaimNever {
    #[inline(always)]
    fn reclaim_closed_nodes<'rf, 'a, V, T, P>(_: &mut SelfRefColMut<'rf, 'a, V, T, P>)
    where
        T: 'a,
        V: Variant<'a, T, Storage = NodeDataLazyClose<T>>,
        P: PinnedVec<Node<'a, V, T>> + 'a,
        SelfRefColMut<'rf, 'a, V, T, P>: Reclaim<V::Prev, V::Next>,
    {
    }
    #[inline(always)]
    fn at_the_same_state_as(&self, collection: &Self) -> bool {
        self.0 == collection.0
    }
    #[inline(always)]
    fn successor_state(&self) -> Self {
        Self(self.0.successor_state())
    }
}
pub(crate) type MemoryReclaimAlways = MemoryReclaimOnThreshold<32>;
#[derive(Default, Clone, Copy)]
pub struct MemoryReclaimOnThreshold<const D: usize>(pub(crate) MemoryState);
impl<const D: usize> MemoryReclaimPolicy for MemoryReclaimOnThreshold<D> {
    #[inline(always)]
    fn reclaim_closed_nodes<'rf, 'a, V, T, P>(vec_mut: &mut SelfRefColMut<'rf, 'a, V, T, P>)
    where
        T: 'a,
        V: Variant<'a, T, Storage = NodeDataLazyClose<T>>,
        P: PinnedVec<Node<'a, V, T>> + 'a,
        SelfRefColMut<'rf, 'a, V, T, P>: Reclaim<V::Prev, V::Next>,
    {
        if vec_mut.need_to_reclaim_vacant_nodes::<D>() {
            vec_mut.reclaim();
        }
    }
    #[inline(always)]
    fn at_the_same_state_as(&self, collection: &Self) -> bool {
        self.0 == collection.0
    }
    #[inline(always)]
    fn successor_state(&self) -> Self {
        Self(self.0.successor_state())
    }
}
pub trait Reclaim<Prev, Next> {
    fn reclaim(&mut self);
}
macro_rules! impl_reclaim_unidirectional {
    ($prev:ty, $next:ty) => {
        impl<'rf, 'a, V, T, P> Reclaim<$prev, $next> for SelfRefColMut<'rf, 'a, V, T, P>
        where
            T: 'a,
            V: Variant<'a, T, Storage = NodeDataLazyClose<T>, Prev = $prev, Next = $next>,
            P: PinnedVec<Node<'a, V, T>> + 'a,
        {
            #[inline(always)]
            fn reclaim(&mut self) {
                crate::memory_reclaim::lazy_unidirectional::reclaim_closed_nodes(self);
            }
        }
    };
    ($const1:ident, $prev:ty, $next:ty) => {
        impl<'rf, 'a, const $const1: usize, V, T, P> Reclaim<$prev, $next>
            for SelfRefColMut<'rf, 'a, V, T, P>
        where
            T: 'a,
            V: Variant<'a, T, Storage = NodeDataLazyClose<T>, Prev = $prev, Next = $next>,
            P: PinnedVec<Node<'a, V, T>> + 'a,
        {
            #[inline(always)]
            fn reclaim(&mut self) {
                crate::memory_reclaim::lazy_unidirectional::reclaim_closed_nodes(self);
            }
        }
    };
}
macro_rules! impl_reclaim_bidirectional {
    ($prev:ty, $next:ty) => {
        impl<'rf, 'a, V, T, P> Reclaim<$prev, $next> for SelfRefColMut<'rf, 'a, V, T, P>
        where
            T: 'a,
            V: Variant<'a, T, Storage = NodeDataLazyClose<T>, Prev = $prev, Next = $next>,
            P: PinnedVec<Node<'a, V, T>> + 'a,
        {
            #[inline(always)]
            fn reclaim(&mut self) {
                crate::memory_reclaim::lazy_bidirectional::reclaim_closed_nodes(self);
            }
        }
    };
    ($const1:ident, $prev:ty, $next:ty) => {
        impl<'rf, 'a, const $const1: usize, V, T, P> Reclaim<$prev, $next>
            for SelfRefColMut<'rf, 'a, V, T, P>
        where
            T: 'a,
            V: Variant<'a, T, Storage = NodeDataLazyClose<T>, Prev = $prev, Next = $next>,
            P: PinnedVec<Node<'a, V, T>> + 'a,
        {
            #[inline(always)]
            fn reclaim(&mut self) {
                crate::memory_reclaim::lazy_bidirectional::reclaim_closed_nodes(self);
            }
        }
    };
    ($const1:ident, $const2:ident, $prev:ty, $next:ty) => {
        impl<'rf, 'a, const $const1: usize, const $const2: usize, V, T, P> Reclaim<$prev, $next>
            for SelfRefColMut<'rf, 'a, V, T, P>
        where
            T: 'a,
            V: Variant<'a, T, Storage = NodeDataLazyClose<T>, Prev = $prev, Next = $next>,
            P: PinnedVec<Node<'a, V, T>> + 'a,
        {
            #[inline(always)]
            fn reclaim(&mut self) {
                crate::memory_reclaim::lazy_bidirectional::reclaim_closed_nodes(self);
            }
        }
    };
}
impl_reclaim_unidirectional!(NodeRefNone, NodeRefSingle<'a, V, T>);
impl_reclaim_unidirectional!(NodeRefNone, NodeRefsVec<'a, V, T>);
impl_reclaim_unidirectional!(N, NodeRefNone, NodeRefsArray<'a, N, V, T>);
impl_reclaim_unidirectional!(NodeRefSingle<'a, V, T>, NodeRefNone);
impl_reclaim_unidirectional!(NodeRefsVec<'a, V, T>, NodeRefNone);
impl_reclaim_unidirectional!(N, NodeRefsArray<'a, N, V, T>, NodeRefNone);
impl_reclaim_bidirectional!(NodeRefSingle<'a, V, T>, NodeRefSingle<'a, V, T>);
impl_reclaim_bidirectional!(NodeRefSingle<'a, V, T>, NodeRefsVec<'a, V, T>);
impl_reclaim_bidirectional!(N, NodeRefSingle<'a, V, T>, NodeRefsArray<'a, N, V, T>);
impl_reclaim_bidirectional!(NodeRefsVec<'a, V, T>, NodeRefSingle<'a, V, T>);
impl_reclaim_bidirectional!(NodeRefsVec<'a, V, T>, NodeRefsVec<'a, V, T>);
impl_reclaim_bidirectional!(N, NodeRefsVec<'a, V, T>, NodeRefsArray<'a, N, V, T>);
impl_reclaim_bidirectional!(N, NodeRefsArray<'a, N, V, T>, NodeRefSingle<'a, V, T>);
impl_reclaim_bidirectional!(N, NodeRefsArray<'a, N, V, T>, NodeRefsVec<'a, V, T>);
impl_reclaim_bidirectional!(N, M, NodeRefsArray<'a, N, V, T>, NodeRefsArray<'a, M, V, T>);