use std::borrow::Borrow;
use std::cell::RefCell;
use std::rc::Rc;
use std::thread::{self, ThreadId};

use crate::Element;
use iced::{Length, Rectangle, Size};
use iced_core::id::Id;
use iced_core::widget::tree;
use iced_core::{Widget, widget};

#[derive(Debug)]
pub struct RcWrapper<T> {
    pub(crate) data: Rc<RefCell<T>>,
    pub(crate) thread_id: ThreadId,
}

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

impl<T> Clone for RcWrapper<T> {
    fn clone(&self) -> Self {
        Self {
            data: self.data.clone(),
            thread_id: self.thread_id,
        }
    }
}

unsafe impl<M: 'static> Send for RcWrapper<M> {}
unsafe impl<M: 'static> Sync for RcWrapper<M> {}

impl<T> RcWrapper<T> {
    pub fn new(element: T) -> Self {
        Self {
            data: Rc::new(RefCell::new(element)),
            thread_id: thread::current().id(),
        }
    }

    /// # Panics
    ///
    /// Will panic if used outside of original thread.
    pub fn with_data<O>(&self, f: impl FnOnce(&T) -> O) -> O {
        assert_eq!(self.thread_id, thread::current().id());
        let my_ref: &T = &RefCell::borrow(self.data.as_ref());
        f(my_ref)
    }

    /// # Panics
    ///
    /// Will panic if used outside of original thread.
    pub fn with_data_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
        assert_eq!(self.thread_id, thread::current().id());
        let my_refmut: &mut T = &mut RefCell::borrow_mut(self.data.as_ref());
        f(my_refmut)
    }
}

#[derive(Clone)]
pub struct RcElementWrapper<M> {
    pub(crate) element: RcWrapper<Element<'static, M>>,
}

impl<M> RcElementWrapper<M> {
    #[must_use]
    pub fn new(element: Element<'static, M>) -> Self {
        RcElementWrapper {
            element: RcWrapper::new(element),
        }
    }
}

impl<M: 'static> Borrow<dyn Widget<M, crate::Theme, crate::Renderer>> for RcElementWrapper<M> {
    fn borrow(&self) -> &(dyn Widget<M, crate::Theme, crate::Renderer> + 'static) {
        self
    }
}

impl<M> Widget<M, crate::Theme, crate::Renderer> for RcElementWrapper<M> {
    fn size(&self) -> Size<Length> {
        self.element.with_data(|e| e.as_widget().size())
    }

    fn size_hint(&self) -> Size<Length> {
        self.element.with_data(move |e| e.as_widget().size_hint())
    }

    fn layout(
        &mut self,
        tree: &mut tree::Tree,
        renderer: &crate::Renderer,
        limits: &iced_core::layout::Limits,
    ) -> iced_core::layout::Node {
        self.element
            .with_data_mut(|e| e.as_widget_mut().layout(tree, renderer, limits))
    }

    fn draw(
        &self,
        tree: &tree::Tree,
        renderer: &mut crate::Renderer,
        theme: &crate::Theme,
        style: &iced_core::renderer::Style,
        layout: iced_core::Layout<'_>,
        cursor: iced_core::mouse::Cursor,
        viewport: &Rectangle,
    ) {
        self.element.with_data(move |e| {
            e.as_widget()
                .draw(tree, renderer, theme, style, layout, cursor, viewport);
        });
    }

    fn tag(&self) -> tree::Tag {
        self.element.with_data(|e| e.as_widget().tag())
    }

    fn state(&self) -> tree::State {
        self.element.with_data(|e| e.as_widget().state())
    }

    fn children(&self) -> Vec<tree::Tree> {
        self.element.with_data(|e| e.as_widget().children())
    }

    fn diff(&mut self, tree: &mut tree::Tree) {
        self.element.with_data_mut(|e| e.as_widget_mut().diff(tree));
    }

    fn operate(
        &mut self,
        state: &mut tree::Tree,
        layout: iced_core::Layout<'_>,
        renderer: &crate::Renderer,
        operation: &mut dyn widget::Operation,
    ) {
        self.element.with_data_mut(|e| {
            e.as_widget_mut()
                .operate(state, layout, renderer, operation);
        });
    }

    fn update(
        &mut self,
        state: &mut tree::Tree,
        event: &crate::iced::Event,
        layout: iced_core::Layout<'_>,
        cursor: iced_core::mouse::Cursor,
        renderer: &crate::Renderer,
        clipboard: &mut dyn iced_core::Clipboard,
        shell: &mut iced_core::Shell<'_, M>,
        viewport: &Rectangle,
    ) {
        self.element.with_data_mut(|e| {
            e.as_widget_mut().update(
                state, event, layout, cursor, renderer, clipboard, shell, viewport,
            )
        })
    }

    fn mouse_interaction(
        &self,
        state: &tree::Tree,
        layout: iced_core::Layout<'_>,
        cursor: iced_core::mouse::Cursor,
        viewport: &Rectangle,
        renderer: &crate::Renderer,
    ) -> iced_core::mouse::Interaction {
        self.element.with_data(|e| {
            e.as_widget()
                .mouse_interaction(state, layout, cursor, viewport, renderer)
        })
    }

    fn overlay<'a>(
        &'a mut self,
        state: &'a mut tree::Tree,
        layout: iced_core::Layout<'a>,
        renderer: &crate::Renderer,
        viewport: &Rectangle,
        translation: iced_core::Vector,
    ) -> Option<iced_core::overlay::Element<'a, M, crate::Theme, crate::Renderer>> {
        assert_eq!(self.element.thread_id, thread::current().id());
        Rc::get_mut(&mut self.element.data).and_then(|e| {
            e.get_mut()
                .as_widget_mut()
                .overlay(state, layout, renderer, viewport, translation)
        })
    }

    fn id(&self) -> Option<Id> {
        self.element.with_data_mut(|e| e.as_widget_mut().id())
    }

    fn set_id(&mut self, id: Id) {
        self.element.with_data_mut(|e| e.as_widget_mut().set_id(id));
    }

    fn drag_destinations(
        &self,
        state: &tree::Tree,
        layout: iced_core::Layout<'_>,
        renderer: &crate::Renderer,
        dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
    ) {
        self.element.with_data_mut(|e| {
            e.as_widget_mut()
                .drag_destinations(state, layout, renderer, dnd_rectangles);
        });
    }
}

impl<Message: 'static> From<RcElementWrapper<Message>> for Element<'static, Message> {
    fn from(wrapper: RcElementWrapper<Message>) -> Self {
        Element::new(wrapper)
    }
}

impl<Message: 'static> From<Element<'static, Message>> for RcElementWrapper<Message> {
    fn from(e: Element<'static, Message>) -> Self {
        RcElementWrapper::new(e)
    }
}
