use std::{
    marker::PhantomData,
    sync::{Arc, Weak},
};
use parking_lot::{RwLock, RwLockReadGuard};
use super::{types::WeakContextInitHandle, *};
#[cfg(feature = "dyn_closure")]
macro_rules! ActualLock {
    ($T:ident) => {
        parking_lot::RwLock<Vec<(WeakContextInitHandle, Box<dyn Any + Send + Sync>)>>
    }
}
#[cfg(not(feature = "dyn_closure"))]
macro_rules! ActualLock {
    ($T:ident) => {
        parking_lot::RwLock<Vec<(WeakContextInitHandle, BoxedVar<$T>)>>
    }
}
#[cfg(feature = "dyn_closure")]
macro_rules! ActualInit {
    ($T:ident) => {
        Arc<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>
    }
}
#[cfg(not(feature = "dyn_closure"))]
macro_rules! ActualInit {
    ($T:ident) => {
        Arc<dyn Fn() -> BoxedVar<$T> + Send + Sync>
    }
}
#[cfg(feature = "dyn_closure")]
macro_rules! ActualReadGuard {
    ($a:tt, $T:ident) => {
        parking_lot::MappedRwLockReadGuard<$a, Box<dyn Any + Send + Sync>>
    }
}
#[cfg(not(feature = "dyn_closure"))]
macro_rules! ActualReadGuard {
    ($a:tt, $T:ident) => {
        parking_lot::MappedRwLockReadGuard<$a, BoxedVar<$T>>
    }
}
pub struct ContextualizedVar<T> {
    _type: PhantomData<T>,
    init: ActualInit![T],
    actual: ActualLock![T],
}
#[allow(clippy::extra_unused_type_parameters)]
fn borrow_init_impl<'a, T>(
    actual: &'a ActualLock![T],
    init: &ActualInit![T],
    #[cfg(debug_assertions)] type_name: &'static str,
) -> ActualReadGuard!['a, T] {
    let current_ctx = ContextInitHandle::current();
    let current_ctx = current_ctx.downgrade();
    let act = actual.read_recursive();
    if let Some(i) = act.iter().position(|(h, _)| h == ¤t_ctx) {
        return RwLockReadGuard::map(act, move |m| &m[i].1);
    }
    drop(act);
    let mut actual_mut = actual.write();
    actual_mut.retain(|(h, _)| h.is_alive());
    let i = actual_mut.len();
    #[cfg(debug_assertions)]
    if i == 200 {
        tracing::debug!("variable of type `{type_name}` actualized >200 times");
    }
    if !actual_mut.iter().any(|(c, _)| c == ¤t_ctx) {
        actual_mut.push((current_ctx.clone(), init()));
    }
    drop(actual_mut);
    let actual = actual.read_recursive();
    RwLockReadGuard::map(actual, move |m| {
        if i < m.len() && m[i].0 == current_ctx {
            &m[i].1
        } else if let Some(i) = m.iter().position(|(h, _)| h == ¤t_ctx) {
            &m[i].1
        } else {
            unreachable!()
        }
    })
}
impl<T: VarValue> ContextualizedVar<T> {
    pub fn new<V: Var<T>>(init: impl Fn() -> V + Send + Sync + 'static) -> Self {
        Self {
            _type: PhantomData,
            #[cfg(feature = "dyn_closure")]
            init: Arc::new(move || Box::new(init().boxed())),
            #[cfg(not(feature = "dyn_closure"))]
            init: Arc::new(move || init().boxed()),
            actual: RwLock::new(Vec::with_capacity(1)),
        }
    }
    pub fn new_value(init: impl Fn() -> T + Send + Sync + 'static) -> Self {
        Self::new(move || init().into_var())
    }
    pub fn borrow_init(&self) -> parking_lot::MappedRwLockReadGuard<BoxedVar<T>> {
        #[cfg(feature = "dyn_closure")]
        {
            parking_lot::MappedRwLockReadGuard::map(
                borrow_init_impl::<()>(
                    &self.actual,
                    &self.init,
                    #[cfg(debug_assertions)]
                    std::any::type_name::<T>(),
                ),
                |v| v.downcast_ref().unwrap(),
            )
        }
        #[cfg(not(feature = "dyn_closure"))]
        {
            borrow_init_impl(&self.actual, &self.init)
        }
    }
    pub fn into_init(self) -> BoxedVar<T> {
        let mut act = self.actual.into_inner();
        let current_ctx = ContextInitHandle::current().downgrade();
        if let Some(i) = act.iter().position(|(h, _)| h == ¤t_ctx) {
            #[cfg(feature = "dyn_closure")]
            {
                *act.swap_remove(i).1.downcast().unwrap()
            }
            #[cfg(not(feature = "dyn_closure"))]
            {
                act.swap_remove(i).1
            }
        } else {
            #[cfg(feature = "dyn_closure")]
            {
                *(self.init)().downcast().unwrap()
            }
            #[cfg(not(feature = "dyn_closure"))]
            {
                (self.init)()
            }
        }
    }
}
pub struct WeakContextualizedVar<T> {
    _type: PhantomData<T>,
    #[cfg(feature = "dyn_closure")]
    init: Weak<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>,
    #[cfg(not(feature = "dyn_closure"))]
    init: Weak<dyn Fn() -> BoxedVar<T> + Send + Sync>,
}
impl<T: VarValue> Clone for ContextualizedVar<T> {
    fn clone(&self) -> Self {
        let current_ctx_id = ContextInitHandle::current().downgrade();
        let act = self.actual.read_recursive();
        if let Some(i) = act.iter().position(|(id, _)| *id == current_ctx_id) {
            return Self {
                _type: PhantomData,
                init: self.init.clone(),
                #[cfg(feature = "dyn_closure")]
                actual: RwLock::new(vec![(
                    act[i].0.clone(),
                    Box::new(act[i].1.downcast_ref::<BoxedVar<T>>().unwrap().clone()),
                )]),
                #[cfg(not(feature = "dyn_closure"))]
                actual: RwLock::new(vec![act[i].clone()]),
            };
        }
        Self {
            _type: PhantomData,
            init: self.init.clone(),
            actual: RwLock::default(),
        }
    }
}
impl<T: VarValue> Clone for WeakContextualizedVar<T> {
    fn clone(&self) -> Self {
        Self {
            _type: PhantomData,
            init: self.init.clone(),
        }
    }
}
impl<T: VarValue> crate::private::Sealed for ContextualizedVar<T> {}
impl<T: VarValue> crate::private::Sealed for WeakContextualizedVar<T> {}
impl<T: VarValue> AnyVar for ContextualizedVar<T> {
    fn clone_any(&self) -> BoxedAnyVar {
        Box::new(self.clone())
    }
    fn as_any(&self) -> &dyn Any {
        self
    }
    fn as_unboxed_any(&self) -> &dyn Any {
        self
    }
    fn double_boxed_any(self: Box<Self>) -> Box<dyn Any> {
        let me: BoxedVar<T> = self;
        Box::new(me)
    }
    fn var_type_id(&self) -> TypeId {
        TypeId::of::<T>()
    }
    fn get_any(&self) -> Box<dyn AnyVarValue> {
        Box::new(self.get())
    }
    fn with_any(&self, read: &mut dyn FnMut(&dyn AnyVarValue)) {
        self.borrow_init().with_any(read)
    }
    fn with_new_any(&self, read: &mut dyn FnMut(&dyn AnyVarValue)) -> bool {
        self.borrow_init().with_new_any(read)
    }
    fn set_any(&self, value: Box<dyn AnyVarValue>) -> Result<(), VarIsReadOnlyError> {
        self.modify(var_set_any(value))
    }
    fn last_update(&self) -> VarUpdateId {
        self.borrow_init().last_update()
    }
    fn is_contextual(&self) -> bool {
        true
    }
    fn capabilities(&self) -> VarCapability {
        self.borrow_init().capabilities()
    }
    fn hook_any(&self, pos_modify_action: Box<dyn Fn(&AnyVarHookArgs) -> bool + Send + Sync>) -> VarHandle {
        self.borrow_init().hook_any(pos_modify_action)
    }
    fn hook_animation_stop(&self, handler: Box<dyn FnOnce() + Send>) -> Result<(), Box<dyn FnOnce() + Send>> {
        self.borrow_init().hook_animation_stop(handler)
    }
    fn strong_count(&self) -> usize {
        Arc::strong_count(&self.init)
    }
    fn weak_count(&self) -> usize {
        Arc::weak_count(&self.init)
    }
    fn actual_var_any(&self) -> BoxedAnyVar {
        self.borrow_init().actual_var_any()
    }
    fn downgrade_any(&self) -> BoxedAnyWeakVar {
        Box::new(self.downgrade())
    }
    fn is_animating(&self) -> bool {
        self.borrow_init().is_animating()
    }
    fn modify_importance(&self) -> usize {
        self.borrow_init().modify_importance()
    }
    fn var_ptr(&self) -> VarPtr {
        VarPtr::new_arc(&self.init)
    }
    fn get_debug(&self) -> Txt {
        self.with(var_debug)
    }
    fn update(&self) -> Result<(), VarIsReadOnlyError> {
        Var::modify(self, var_update)
    }
    fn map_debug(&self) -> BoxedVar<Txt> {
        Var::map(self, var_debug).boxed()
    }
}
impl<T: VarValue> AnyWeakVar for WeakContextualizedVar<T> {
    fn clone_any(&self) -> BoxedAnyWeakVar {
        Box::new(self.clone())
    }
    fn strong_count(&self) -> usize {
        self.init.strong_count()
    }
    fn weak_count(&self) -> usize {
        self.init.weak_count()
    }
    fn upgrade_any(&self) -> Option<BoxedAnyVar> {
        self.upgrade().map(|c| Box::new(c) as _)
    }
    fn as_any(&self) -> &dyn Any {
        self
    }
}
impl<T: VarValue> IntoVar<T> for ContextualizedVar<T> {
    type Var = Self;
    fn into_var(self) -> Self::Var {
        self
    }
}
impl<T: VarValue> Var<T> for ContextualizedVar<T> {
    type ReadOnly = types::ReadOnlyVar<T, Self>;
    type ActualVar = BoxedVar<T>;
    type Downgrade = WeakContextualizedVar<T>;
    type Map<O: VarValue> = contextualized::ContextualizedVar<O>;
    type MapBidi<O: VarValue> = contextualized::ContextualizedVar<O>;
    type FlatMap<O: VarValue, V: Var<O>> = contextualized::ContextualizedVar<O>;
    type FilterMap<O: VarValue> = contextualized::ContextualizedVar<O>;
    type FilterMapBidi<O: VarValue> = contextualized::ContextualizedVar<O>;
    type MapRef<O: VarValue> = types::MapRef<T, O, Self>;
    type MapRefBidi<O: VarValue> = types::MapRefBidi<T, O, Self>;
    type Easing = types::ContextualizedVar<T>;
    fn with<R, F>(&self, read: F) -> R
    where
        F: FnOnce(&T) -> R,
    {
        self.borrow_init().with(read)
    }
    fn modify<F>(&self, modify: F) -> Result<(), VarIsReadOnlyError>
    where
        F: FnOnce(&mut VarModify<T>) + Send + 'static,
    {
        self.borrow_init().modify(modify)
    }
    fn actual_var(self) -> Self::ActualVar {
        self.into_init().actual_var()
    }
    fn downgrade(&self) -> Self::Downgrade {
        WeakContextualizedVar {
            _type: PhantomData,
            init: Arc::downgrade(&self.init),
        }
    }
    fn into_value(self) -> T {
        self.into_init().into_value()
    }
    fn read_only(&self) -> Self::ReadOnly {
        types::ReadOnlyVar::new(self.clone())
    }
    fn map<O, M>(&self, map: M) -> Self::Map<O>
    where
        O: VarValue,
        M: FnMut(&T) -> O + Send + 'static,
    {
        var_map_ctx(self, map)
    }
    fn map_bidi<O, M, B>(&self, map: M, map_back: B) -> Self::MapBidi<O>
    where
        O: VarValue,
        M: FnMut(&T) -> O + Send + 'static,
        B: FnMut(&O) -> T + Send + 'static,
    {
        var_map_bidi_ctx(self, map, map_back)
    }
    fn flat_map<O, V, M>(&self, map: M) -> Self::FlatMap<O, V>
    where
        O: VarValue,
        V: Var<O>,
        M: FnMut(&T) -> V + Send + 'static,
    {
        var_flat_map_ctx(self, map)
    }
    fn filter_map<O, M, I>(&self, map: M, fallback: I) -> Self::FilterMap<O>
    where
        O: VarValue,
        M: FnMut(&T) -> Option<O> + Send + 'static,
        I: Fn() -> O + Send + Sync + 'static,
    {
        var_filter_map_ctx(self, map, fallback)
    }
    fn filter_map_bidi<O, M, B, I>(&self, map: M, map_back: B, fallback: I) -> Self::FilterMapBidi<O>
    where
        O: VarValue,
        M: FnMut(&T) -> Option<O> + Send + 'static,
        B: FnMut(&O) -> Option<T> + Send + 'static,
        I: Fn() -> O + Send + Sync + 'static,
    {
        var_filter_map_bidi_ctx(self, map, map_back, fallback)
    }
    fn map_ref<O, M>(&self, map: M) -> Self::MapRef<O>
    where
        O: VarValue,
        M: Fn(&T) -> &O + Send + Sync + 'static,
    {
        var_map_ref(self, map)
    }
    fn map_ref_bidi<O, M, B>(&self, map: M, map_mut: B) -> Self::MapRefBidi<O>
    where
        O: VarValue,
        M: Fn(&T) -> &O + Send + Sync + 'static,
        B: Fn(&mut T) -> &mut O + Send + Sync + 'static,
    {
        var_map_ref_bidi(self, map, map_mut)
    }
    fn easing<F>(&self, duration: Duration, easing: F) -> Self::Easing
    where
        T: Transitionable,
        F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
    {
        var_easing_ctx(self, duration, easing)
    }
    fn easing_with<F, SE>(&self, duration: Duration, easing: F, sampler: SE) -> Self::Easing
    where
        T: Transitionable,
        F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
        SE: Fn(&animation::Transition<T>, EasingStep) -> T + Send + Sync + 'static,
    {
        var_easing_with_ctx(self, duration, easing, sampler)
    }
}
impl<T: VarValue> WeakVar<T> for WeakContextualizedVar<T> {
    type Upgrade = ContextualizedVar<T>;
    fn upgrade(&self) -> Option<Self::Upgrade> {
        Some(ContextualizedVar {
            _type: PhantomData,
            init: self.init.upgrade()?,
            actual: RwLock::default(),
        })
    }
}