use alloc::alloc::Layout;
use alloc::rc::Rc;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ops::Deref;
use core::ptr::{self, drop_in_place, NonNull};
use core::borrow::Borrow;
use core::cell::Cell;
use core::fmt::{self, Debug, Display, Formatter, Pointer};
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::panic::{RefUnwindSafe, UnwindSafe};
#[cfg(feature = "nightly")]
use core::{
    marker::CoercePointee,
    ptr::{metadata, DynMetadata},
};
use crate::counter_marker::{CounterMarker, Mark};
use crate::state::{replace_state_field, state, State, try_state};
use crate::trace::{Context, ContextInner, Finalize, Trace};
use crate::utils::*;
use crate::POSSIBLE_CYCLES;
#[cfg(feature = "weak-ptrs")]
use crate::weak::weak_counter_marker::WeakCounterMarker;
#[cfg_attr(feature = "nightly", derive(CoercePointee))]
#[repr(transparent)]
pub struct Cc<#[cfg_attr(feature = "nightly", pointee)] T: ?Sized + Trace + 'static> {
    inner: NonNull<CcBox<T>>,
    _phantom: PhantomData<Rc<T>>, }
impl<T: Trace> Cc<T> {
    #[must_use = "newly created Cc is immediately dropped"]
    #[track_caller]
    pub fn new(t: T) -> Cc<T> {
        state(|state| {
            #[cfg(debug_assertions)]
            if state.is_tracing() {
                panic!("Cannot create a new Cc while tracing!");
            }
            #[cfg(feature = "auto-collect")]
            super::trigger_collection(state);
            Cc {
                inner: CcBox::new(t, state),
                _phantom: PhantomData,
            }
        })
    }
    #[inline]
    #[track_caller]
    pub fn try_unwrap(self) -> Result<T, Self> {
        let cc = ManuallyDrop::new(self); if cc.strong_count() != 1 {
            return Err(ManuallyDrop::into_inner(cc));
        }
        let res = try_state(|state| {
            if state.is_collecting() || state.is_dropping() {
                return None;
            }
            #[cfg(feature = "finalization")]
            if state.is_finalizing() {
                return None;
            }
            remove_from_list(cc.inner.cast());
            let t = unsafe { ptr::read(cc.inner().get_elem()) };
            let layout = cc.inner().layout();
            #[cfg(feature = "weak-ptrs")]
            cc.inner().drop_metadata();
            unsafe {
                cc_dealloc(cc.inner, layout, state);
            }
            Some(t)
        });
        match res {
            Ok(Some(t)) => Ok(t),
            _ => Err(ManuallyDrop::into_inner(cc)),
        }
    }
}
impl<T: ?Sized + Trace> Cc<T> {
    #[inline]
    pub fn ptr_eq(this: &Cc<T>, other: &Cc<T>) -> bool {
        ptr::eq(this.inner.as_ptr() as *const (), other.inner.as_ptr() as *const ())
    }
    #[inline]
    pub fn strong_count(&self) -> u32 {
        self.counter_marker().counter() as u32
    }
    #[cfg(feature = "finalization")]
    #[inline]
    #[track_caller]
    pub fn finalize_again(&mut self) {
        assert!(
            state(|state| !state.is_collecting() && !state.is_finalizing() && !state.is_dropping()),
            "Cc::finalize_again cannot be called while collecting"
        );
        self.counter_marker().set_finalized(false);
    }
    #[cfg(feature = "finalization")]
    #[inline]
    pub fn already_finalized(&self) -> bool {
        !self.counter_marker().needs_finalization()
    }
    #[inline]
    pub fn mark_alive(&self) {
        remove_from_list(self.inner.cast());
    }
    #[inline(always)]
    fn counter_marker(&self) -> &CounterMarker {
        &self.inner().counter_marker
    }
    #[inline(always)]
    pub(crate) fn inner(&self) -> &CcBox<T> {
        unsafe { self.inner.as_ref() }
    }
    #[cfg(feature = "weak-ptrs")]
    #[inline(always)]
    pub(crate) fn inner_ptr(&self) -> NonNull<CcBox<T>> {
        self.inner
    }
    #[cfg(feature = "weak-ptrs")] #[inline(always)]
    #[must_use]
    pub(crate) fn __new_internal(inner: NonNull<CcBox<T>>) -> Cc<T> {
        Cc {
            inner,
            _phantom: PhantomData,
        }
    }
}
impl<T: ?Sized + Trace> Clone for Cc<T> {
    #[inline]
    #[track_caller]
    fn clone(&self) -> Self {
        #[cfg(debug_assertions)]
        if state(|state| state.is_tracing()) {
            panic!("Cannot clone while tracing!");
        }
        if self.counter_marker().increment_counter().is_err() {
            panic!("Too many references has been created to a single Cc");
        }
        self.mark_alive();
        Cc {
            inner: self.inner,
            _phantom: PhantomData,
        }
    }
}
impl<T: ?Sized + Trace> Deref for Cc<T> {
    type Target = T;
    #[inline]
    #[track_caller]
    fn deref(&self) -> &Self::Target {
        #[cfg(debug_assertions)]
        if state(|state| state.is_tracing()) {
            panic!("Cannot deref while tracing!");
        }
        self.inner().get_elem()
    }
}
impl<T: ?Sized + Trace> Drop for Cc<T> {
    fn drop(&mut self) {
        #[cfg(debug_assertions)]
        if state(|state| state.is_tracing()) {
            panic!("Cannot drop while tracing!");
        }
        #[inline]
        fn decrement_counter<T: ?Sized + Trace>(cc: &Cc<T>) {
            let res = cc.counter_marker().decrement_counter();
            debug_assert!(res.is_ok());
        }
        #[inline]
        fn handle_possible_cycle<T: ?Sized + Trace>(cc: &Cc<T>) {
            decrement_counter(cc);
            add_to_list(cc.inner.cast());
        }
        if self.counter_marker().is_in_list_or_queue() {
            decrement_counter(self);
            return;
        }
        if self.counter_marker().counter() == 1 {
            state(|state| {
                #[cfg(feature = "finalization")]
                if self.counter_marker().needs_finalization() {
                    let _finalizing_guard = replace_state_field!(finalizing, true, state);
                    self.counter_marker().set_finalized(true);
                    self.inner().get_elem().finalize();
                    if self.counter_marker().counter() != 1 {
                        handle_possible_cycle(self);
                        return;
                    }
                    }
                decrement_counter(self);
                remove_from_list(self.inner.cast());
                let _dropping_guard = replace_state_field!(dropping, true, state);
                let layout = self.inner().layout();
                #[cfg(feature = "weak-ptrs")]
                {
                    self.counter_marker().set_dropped(true);
                }
                unsafe {
                    drop_in_place(self.inner().get_elem_mut());
                    #[cfg(feature = "pedantic-debug-assertions")]
                    debug_assert_eq!(
                        0, self.counter_marker().counter(),
                        "Trying to deallocate a CcBox with a reference counter > 0"
                    );
                    #[cfg(feature = "weak-ptrs")]
                    self.inner().drop_metadata();
                    cc_dealloc(self.inner, layout, state);
                }
                });
        } else {
            handle_possible_cycle(self);
        }
    }
}
unsafe impl<T: ?Sized + Trace> Trace for Cc<T> {
    #[inline]
    #[track_caller]
    fn trace(&self, ctx: &mut Context<'_>) {
        CcBox::trace(self.inner.cast(), ctx);
    }
}
impl<T: ?Sized + Trace> Finalize for Cc<T> {}
#[repr(C)]
pub(crate) struct CcBox<T: ?Sized + Trace + 'static> {
    next: UnsafeCell<Option<NonNull<CcBox<()>>>>,
    prev: UnsafeCell<Option<NonNull<CcBox<()>>>>,
    metadata: Cell<Metadata>,
    counter_marker: CounterMarker,
    _phantom: PhantomData<Rc<()>>, elem: UnsafeCell<T>,
}
impl<T: Trace> CcBox<T> {
    #[must_use]
    fn new(t: T, state: &State) -> NonNull<CcBox<T>> {
        let layout = Layout::new::<CcBox<T>>();
        #[cfg(feature = "finalization")]
        let already_finalized = state.is_finalizing();
        #[cfg(not(feature = "finalization"))]
        let already_finalized = false;
        unsafe {
            let ptr: NonNull<CcBox<T>> = cc_alloc(layout, state);
            ptr::write(
                ptr.as_ptr(),
                CcBox {
                    next: UnsafeCell::new(None),
                    prev: UnsafeCell::new(None),
                    metadata: Metadata::new(ptr),
                    counter_marker: CounterMarker::new_with_counter_to_one(already_finalized),
                    _phantom: PhantomData,
                    elem: UnsafeCell::new(t),
                },
            );
            ptr
        }
    }
    #[cfg(all(test, feature = "std"))] #[must_use]
    pub(crate) fn new_for_tests(t: T) -> NonNull<CcBox<T>> {
        state(|state| CcBox::new(t, state))
    }
}
impl<T: ?Sized + Trace> CcBox<T> {
    #[inline]
    pub(crate) fn get_elem(&self) -> &T {
        unsafe { &*self.elem.get() }
    }
    #[inline]
    pub(crate) fn get_elem_mut(&self) -> *mut T {
        self.elem.get()
    }
    #[inline]
    pub(crate) fn counter_marker(&self) -> &CounterMarker {
        &self.counter_marker
    }
    #[inline]
    pub(crate) fn layout(&self) -> Layout {
        #[cfg(feature = "nightly")]
        {
            self.vtable().vtable.layout()
        }
        #[cfg(not(feature = "nightly"))]
        unsafe {
            Layout::for_value(self.vtable().fat_ptr.as_ref())
        }
    }
    #[inline]
    fn vtable(&self) -> VTable {
        #[cfg(feature = "weak-ptrs")]
        unsafe {
            if self.counter_marker.has_allocated_for_metadata() {
                self.metadata.get().boxed_metadata.as_ref().vtable
            } else {
                self.metadata.get().vtable
            }
        }
        #[cfg(not(feature = "weak-ptrs"))]
        unsafe {
            self.metadata.get().vtable
        }
    }
    #[cfg(feature = "weak-ptrs")]
    #[inline]
    pub(crate) fn get_or_init_metadata(&self) -> NonNull<BoxedMetadata> {
        unsafe {
            if self.counter_marker.has_allocated_for_metadata() {
                self.metadata.get().boxed_metadata
            } else {
                let vtable = self.metadata.get().vtable;
                let ptr = BoxedMetadata::new(vtable, WeakCounterMarker::new(true));
                self.metadata.set(Metadata {
                    boxed_metadata: ptr,
                });
                self.counter_marker.set_allocated_for_metadata(true);
                ptr
            }
        }
    }
    #[cfg(feature = "weak-ptrs")]
    #[inline(always)]
    pub(crate) unsafe fn get_metadata_unchecked(&self) -> NonNull<BoxedMetadata> {
        self.metadata.get().boxed_metadata
    }
    #[cfg(feature = "weak-ptrs")]
    #[inline]
    pub(crate) fn drop_metadata(&self) {
        if self.counter_marker.has_allocated_for_metadata() {
            unsafe {
                let boxed = self.get_metadata_unchecked();
                if boxed.as_ref().weak_counter_marker.counter() == 0 {
                    dealloc_other(boxed);
                } else {
                    boxed.as_ref().weak_counter_marker.set_accessible(false);
                }
            }
        }
    }
    #[inline]
    pub(super) fn get_next(&self) -> *mut Option<NonNull<CcBox<()>>> {
        self.next.get()
    }
    #[inline]
    pub(super) fn get_prev(&self) -> *mut Option<NonNull<CcBox<()>>> {
        self.prev.get()
    }
}
unsafe impl<T: ?Sized + Trace> Trace for CcBox<T> {
    #[inline(always)]
    fn trace(&self, ctx: &mut Context<'_>) {
        self.get_elem().trace(ctx);
    }
}
impl<T: ?Sized + Trace> Finalize for CcBox<T> {
    #[inline(always)]
    fn finalize(&self) {
        self.get_elem().finalize();
    }
}
#[inline]
pub(crate) fn remove_from_list(ptr: NonNull<CcBox<()>>) {
    let counter_marker = unsafe { ptr.as_ref() }.counter_marker();
    if counter_marker.is_in_possible_cycles() {
        let _ = POSSIBLE_CYCLES.try_with(|pc| {
            #[cfg(feature = "pedantic-debug-assertions")]
            debug_assert!(pc.iter().contains(ptr));
            counter_marker.mark(Mark::NonMarked);
            pc.remove(ptr);
        });
    } else {
        #[cfg(feature = "pedantic-debug-assertions")]
        debug_assert! {
            POSSIBLE_CYCLES.try_with(|pc| {
                !pc.iter().contains(ptr)
            }).unwrap_or(true)
        };
    }
}
#[inline]
pub(crate) fn add_to_list(ptr: NonNull<CcBox<()>>) {
    let counter_marker = unsafe { ptr.as_ref() }.counter_marker();
    if !counter_marker.is_in_possible_cycles() {
        let _ = POSSIBLE_CYCLES.try_with(|pc| {
            #[cfg(feature = "pedantic-debug-assertions")]
            debug_assert!(!pc.iter().contains(ptr));
            debug_assert!(counter_marker.is_not_marked());
            pc.add(ptr);
            counter_marker.reset_tracing_counter();
            counter_marker.mark(Mark::PossibleCycles);
        });
    } else {
        #[cfg(feature = "pedantic-debug-assertions")]
        debug_assert! {
            POSSIBLE_CYCLES.try_with(|pc| {
                pc.iter().contains(ptr)
            }).unwrap_or(true)
        };
    }
}
impl CcBox<()> {
    #[inline]
    pub(super) fn trace_inner(ptr: NonNull<Self>, ctx: &mut Context<'_>) {
        unsafe {
            CcBox::get_traceable(ptr).as_ref().trace(ctx);
        }
    }
    #[cfg(feature = "finalization")]
    #[inline]
    pub(super) fn finalize_inner(ptr: NonNull<Self>) -> bool {
        unsafe {
            if ptr.as_ref().counter_marker().needs_finalization() {
                ptr.as_ref().counter_marker().set_finalized(true);
                CcBox::get_traceable(ptr).as_ref().finalize_elem();
                true
            } else {
                false
            }
        }
    }
    #[inline]
    pub(super) unsafe fn drop_inner(ptr: NonNull<Self>) {
        #[cfg(feature = "weak-ptrs")]
        {
            ptr.as_ref().counter_marker().set_dropped(true);
        }
        CcBox::get_traceable(ptr).as_mut().drop_elem();
    }
    #[inline]
    fn get_traceable(ptr: NonNull<Self>) -> NonNull<dyn InternalTrace> {
        #[cfg(feature = "nightly")]
        unsafe {
            let vtable = ptr.as_ref().vtable().vtable;
            NonNull::from_raw_parts(ptr.cast(), vtable)
        }
        #[cfg(not(feature = "nightly"))]
        unsafe {
            ptr.as_ref().vtable().fat_ptr
        }
    }
    #[inline(never)] fn trace(ptr: NonNull<Self>, ctx: &mut Context<'_>) {
        let counter_marker = unsafe { ptr.as_ref() }.counter_marker();
        match ctx.inner() {
            ContextInner::Counting {
                root_list,
                non_root_list,
                queue,
            } => {
                if counter_marker.is_in_list_or_queue() {
                    debug_assert!(counter_marker.tracing_counter() < counter_marker.counter());
                    let res = counter_marker.increment_tracing_counter();
                    debug_assert!(res.is_ok());
                    if counter_marker.is_in_list() && counter_marker.counter() == counter_marker.tracing_counter() {
                        #[cfg(feature = "pedantic-debug-assertions")]
                        debug_assert!(root_list.iter().contains(ptr));
                        root_list.remove(ptr);
                        non_root_list.add(ptr);
                    }
                } else {
                    if counter_marker.is_in_possible_cycles() {
                        let res = counter_marker.increment_tracing_counter();
                        debug_assert!(res.is_ok());
                        return;
                    }
                    counter_marker.reset_tracing_counter();
                    let res = counter_marker.increment_tracing_counter();
                    debug_assert!(res.is_ok());
                    queue.add(ptr);
                    counter_marker.mark(Mark::InQueue);
                }
            },
            ContextInner::RootTracing { non_root_list, queue } => {
                if counter_marker.is_in_list() && counter_marker.counter() == counter_marker.tracing_counter() {
                    #[cfg(feature = "pedantic-debug-assertions")]
                    debug_assert!(non_root_list.iter().contains(ptr));
                    counter_marker.mark(Mark::NonMarked);
                    non_root_list.remove(ptr);
                    queue.add(ptr);
                    counter_marker.mark(Mark::InQueue);
                }
            },
        }
    }
}
#[derive(Copy, Clone)]
union Metadata {
    vtable: VTable,
    #[cfg(feature = "weak-ptrs")]
    boxed_metadata: NonNull<BoxedMetadata>,
}
impl Metadata {
    #[inline]
    fn new<T: Trace>(cc_box: NonNull<CcBox<T>>) -> Cell<Metadata> {
        #[cfg(feature = "nightly")]
        let vtable = VTable {
            vtable: metadata(cc_box.as_ptr() as *mut dyn InternalTrace),
        };
        #[cfg(not(feature = "nightly"))]
        let vtable = VTable {
            fat_ptr: unsafe { NonNull::new_unchecked(cc_box.as_ptr() as *mut dyn InternalTrace)
            },
        };
        Cell::new(Metadata {
            vtable
        })
    }
}
#[derive(Copy, Clone)]
struct VTable {
    #[cfg(feature = "nightly")]
    vtable: DynMetadata<dyn InternalTrace>,
    #[cfg(not(feature = "nightly"))]
    fat_ptr: NonNull<dyn InternalTrace>,
}
#[cfg(feature = "weak-ptrs")]
pub(crate) struct BoxedMetadata {
    vtable: VTable,
    pub(crate) weak_counter_marker: WeakCounterMarker,
}
#[cfg(feature = "weak-ptrs")]
impl BoxedMetadata {
    #[inline]
    fn new(vtable: VTable, weak_counter_marker: WeakCounterMarker) -> NonNull<BoxedMetadata> {
        unsafe {
            let ptr: NonNull<BoxedMetadata> = alloc_other();
            ptr::write(
                ptr.as_ptr(),
                BoxedMetadata {
                    vtable,
                    weak_counter_marker,
                },
            );
            ptr
        }
    }
}
trait InternalTrace: Trace {
    #[cfg(feature = "finalization")]
    fn finalize_elem(&self);
    unsafe fn drop_elem(&self);
}
impl<T: ?Sized + Trace> InternalTrace for CcBox<T> {
    #[cfg(feature = "finalization")]
    fn finalize_elem(&self) {
        self.get_elem().finalize();
    }
    unsafe fn drop_elem(&self) {
        drop_in_place(self.get_elem_mut());
    }
}
impl<T: Trace + Default> Default for Cc<T> {
    #[inline]
    fn default() -> Self {
        Cc::new(<T as Default>::default())
    }
}
impl<T: ?Sized + Trace> AsRef<T> for Cc<T> {
    #[inline(always)]
    fn as_ref(&self) -> &T {
        self
    }
}
impl<T: ?Sized + Trace> Borrow<T> for Cc<T> {
    #[inline(always)]
    fn borrow(&self) -> &T {
        self
    }
}
impl<T: Trace> From<T> for Cc<T> {
    #[inline]
    fn from(value: T) -> Self {
        Cc::new(value)
    }
}
impl<T: ?Sized + Trace + Debug> Debug for Cc<T> {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        Debug::fmt(&**self, f)
    }
}
impl<T: ?Sized + Trace + Display> Display for Cc<T> {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        Display::fmt(&**self, f)
    }
}
impl<T: ?Sized + Trace> Pointer for Cc<T> {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        Pointer::fmt(&ptr::addr_of!(**self), f)
    }
}
impl<T: ?Sized + Trace + PartialEq> PartialEq for Cc<T> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        **self == **other
    }
}
impl<T: ?Sized + Trace + Eq> Eq for Cc<T> {}
impl<T: ?Sized + Trace + Ord> Ord for Cc<T> {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        (**self).cmp(&**other)
    }
}
impl<T: ?Sized + Trace + PartialOrd> PartialOrd for Cc<T> {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        (**self).partial_cmp(&**other)
    }
    #[inline]
    fn lt(&self, other: &Self) -> bool {
        **self < **other
    }
    #[inline]
    fn le(&self, other: &Self) -> bool {
        **self <= **other
    }
    #[inline]
    fn gt(&self, other: &Self) -> bool {
        **self > **other
    }
    #[inline]
    fn ge(&self, other: &Self) -> bool {
        **self >= **other
    }
}
impl<T: ?Sized + Trace + Hash> Hash for Cc<T> {
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        (**self).hash(state);
    }
}
impl<T: ?Sized + Trace + UnwindSafe> UnwindSafe for Cc<T> {}
impl<T: ?Sized + Trace + RefUnwindSafe> RefUnwindSafe for Cc<T> {}