use raui_core::{
    application::ChangeNotifier,
    widget::{component::WidgetComponent, context::*, node::WidgetNode, FnWidget, WidgetRef},
    Lifetime, LifetimeLazy, Managed, ValueReadAccess, ValueWriteAccess,
};
use std::{
    any::Any,
    marker::PhantomData,
    ops::{Deref, DerefMut},
    ptr::NonNull,
};

#[allow(unused_variables)]
pub trait ViewState: Any + Send + Sync {
    fn on_mount(&mut self, context: WidgetMountOrChangeContext) {}
    fn on_unmount(&mut self, context: WidgetUnmountContext) {}
    fn on_change(&mut self, context: WidgetMountOrChangeContext) {}
    fn on_render(&self, context: WidgetContext) -> WidgetNode;
    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;
}

pub struct View<T: ViewState> {
    inner: Box<dyn ViewState>,
    lifetime: Lifetime,
    _phantom: PhantomData<fn() -> T>,
}

impl<T: ViewState + Default> Default for View<T> {
    fn default() -> Self {
        Self::new(T::default())
    }
}

impl<T: ViewState> View<T> {
    pub fn new(state: T) -> Self
    where
        T: 'static,
    {
        Self {
            inner: Box::new(state),
            lifetime: Default::default(),
            _phantom: Default::default(),
        }
    }

    pub fn into_inner(self) -> Box<dyn ViewState> {
        self.inner
    }

    pub fn as_dyn(&self) -> Option<ValueReadAccess<dyn ViewState>> {
        self.lifetime.read(&*self.inner)
    }

    pub fn as_dyn_mut(&mut self) -> Option<ValueWriteAccess<dyn ViewState>> {
        self.lifetime.write(&mut *self.inner)
    }

    pub fn read(&self) -> Option<ValueReadAccess<T>> {
        self.lifetime.read(self.inner.as_any().downcast_ref::<T>()?)
    }

    pub fn write(&mut self) -> Option<ValueWriteAccess<T>> {
        self.lifetime
            .write(self.inner.as_any_mut().downcast_mut::<T>()?)
    }

    pub fn lazy(&self) -> LazyView<T> {
        unsafe {
            let ptr = self.inner.as_any().downcast_ref::<T>().unwrap() as *const T as *mut T;
            LazyView {
                inner: NonNull::new_unchecked(ptr),
                lifetime: self.lifetime.lazy(),
            }
        }
    }

    pub fn component(&self) -> WidgetComponent {
        WidgetComponent::new(self.widget(), std::any::type_name::<Self>())
    }

    pub fn widget(&self) -> FnWidget {
        let this = self.lazy();
        FnWidget::closure(move |context| {
            let this_mount = this.clone();
            let this_unmount = this.clone();
            let this_change = this.clone();
            context.life_cycle.mount(move |context| {
                if let Some(mut this) = this_mount.write() {
                    this.on_mount(context);
                }
            });
            context.life_cycle.unmount(move |context| {
                if let Some(mut this) = this_unmount.write() {
                    this.on_unmount(context);
                }
            });
            context.life_cycle.change(move |context| {
                if let Some(mut this) = this_change.write() {
                    this.on_change(context);
                }
            });
            this.write()
                .map(|this| this.on_render(context))
                .unwrap_or_default()
        })
    }
}

pub struct LazyView<T: ViewState> {
    inner: NonNull<T>,
    lifetime: LifetimeLazy,
}

unsafe impl<T: ViewState> Send for LazyView<T> {}
unsafe impl<T: ViewState> Sync for LazyView<T> {}

impl<T: ViewState> Clone for LazyView<T> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner,
            lifetime: self.lifetime.clone(),
        }
    }
}

impl<T: ViewState> LazyView<T> {
    pub fn as_dyn(&self) -> Option<ValueReadAccess<dyn ViewState>> {
        unsafe { self.lifetime.read(self.inner.as_ref()) }
    }

    pub fn as_dyn_mut(&self) -> Option<ValueWriteAccess<dyn ViewState>> {
        unsafe { self.lifetime.write(self.inner.as_ptr().as_mut()?) }
    }

    pub fn read(&self) -> Option<ValueReadAccess<T>> {
        unsafe { self.lifetime.read(self.inner.as_ref()) }
    }

    pub fn write(&self) -> Option<ValueWriteAccess<T>> {
        unsafe { self.lifetime.write(self.inner.as_ptr().as_mut()?) }
    }
}

pub struct ViewValue<T> {
    inner: T,
    notifier: Option<ChangeNotifier>,
    id: WidgetRef,
}

impl<T> ViewValue<T> {
    pub fn new(value: T) -> Self {
        Self {
            inner: value,
            notifier: None,
            id: Default::default(),
        }
    }

    pub fn with_notifier(mut self, notifier: ChangeNotifier) -> Self {
        self.bind_notifier(notifier);
        self
    }

    pub fn bind_notifier(&mut self, notifier: ChangeNotifier) {
        if self.notifier.is_none() {
            if let Some(id) = self.id.read() {
                notifier.notify(id);
            }
        }
        self.notifier = Some(notifier);
    }

    pub fn unbind_notifier(&mut self) {
        self.notifier = None;
    }

    pub fn bound_notifier(&self) -> Option<&ChangeNotifier> {
        self.notifier.as_ref()
    }

    pub fn widget_ref(&self) -> WidgetRef {
        self.id.clone()
    }
}

impl<T> Deref for ViewValue<T> {
    type Target = T;

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

impl<T> DerefMut for ViewValue<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        if let Some(notifier) = self.notifier.as_ref() {
            if let Some(id) = self.id.read() {
                notifier.notify(id);
            }
        }
        &mut self.inner
    }
}

pub struct SharedView<T: ViewState> {
    inner: Managed<Option<View<T>>>,
}

impl<T: ViewState> Default for SharedView<T> {
    fn default() -> Self {
        Self {
            inner: Default::default(),
        }
    }
}

impl<T: ViewState> SharedView<T> {
    pub fn new(view: View<T>) -> Self {
        Self {
            inner: Managed::new(Some(view)),
        }
    }

    pub fn replace(&mut self, view: View<T>) {
        if let Some(mut inner) = self.inner.write() {
            *inner = Some(view);
        }
    }

    pub fn clear(&mut self) {
        if let Some(mut inner) = self.inner.write() {
            *inner = None;
        }
    }

    pub fn read(&self) -> Option<ValueReadAccess<View<T>>> {
        self.inner.read()?.remap(|inner| inner.as_ref()).ok()
    }

    pub fn write(&mut self) -> Option<ValueWriteAccess<View<T>>> {
        self.inner.write()?.remap(|inner| inner.as_mut()).ok()
    }
}

impl<T: ViewState> ViewState for SharedView<T> {
    fn on_render(&self, context: WidgetContext) -> WidgetNode {
        self.inner
            .read()
            .and_then(|inner| {
                inner
                    .as_ref()
                    .map(|inner| inner.component().key(context.key).into())
            })
            .unwrap_or_default()
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}
