use {
    crate::view::{NotifyFnObserver, Observer, ObserverBroadcast, ResetFnObserver, View},
    std::any::Any,
    std::sync::{Arc, RwLock}
};

pub trait UpdateTask: Send + Sync {
    fn update(&self);
}

                    /*\
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
                 View Port
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
                    \*/
pub struct ViewPort<V: View + ?Sized> {
    view: Arc<RwLock<Option<Arc<V>>>>,
    cast: Arc<RwLock<ObserverBroadcast<V>>>,
    pub update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>,
}

impl<V: View + ?Sized> ViewPort<V>
where
    V::Msg: Clone,
{
    pub fn new() -> Self {
        ViewPort {
            view: Arc::new(RwLock::new(None)),
            cast: Arc::new(RwLock::new(ObserverBroadcast::new())),
            update_hooks: Arc::new(RwLock::new(Vec::new())),
        }
    }

    pub fn with_view(view: Arc<V>) -> Self {
        let port = ViewPort::new();
        port.set_view(Some(view));
        port
    }

    pub fn set_view(&self, view: Option<Arc<V>>) {
        let mut v = self.view.write().unwrap();
        *v = view.clone();
        self.cast.write().unwrap().reset(view);
    }

    pub fn get_cast(&self) -> Arc<RwLock<ObserverBroadcast<V>>> {
        self.cast.clone()
    }

    pub fn add_observer(&self, observer: Arc<RwLock<dyn Observer<V>>>) {
        self.update();

        let mut obs = observer.write().unwrap();
        let mut cst = self.cast.write().unwrap();

        obs.reset(self.view.read().unwrap().clone());
        cst.add_observer(Arc::downgrade(&observer));
    }

    pub fn add_update_hook(&self, hook_cast: Arc<dyn UpdateTask>) {
        self.update_hooks.write().unwrap().push(hook_cast);
    }

    pub fn inner(&self) -> InnerViewPort<V> {
        InnerViewPort(ViewPort {
            view: self.view.clone(),
            cast: self.cast.clone(),
            update_hooks: self.update_hooks.clone(),
        })
    }

    pub fn outer(&self) -> OuterViewPort<V> {
        OuterViewPort(ViewPort {
            view: self.view.clone(),
            cast: self.cast.clone(),
            update_hooks: self.update_hooks.clone(),
        })
    }

    pub fn into_inner(self) -> InnerViewPort<V> {
        InnerViewPort(ViewPort {
            view: self.view,
            cast: self.cast,
            update_hooks: self.update_hooks,
        })
    }

    pub fn into_outer(self) -> OuterViewPort<V> {
        OuterViewPort(ViewPort {
            view: self.view,
            cast: self.cast,
            update_hooks: self.update_hooks,
        })
    }
}

impl<V: View + ?Sized + 'static> ViewPort<V>
where V::Msg: Clone
{
    // make the view of `other_port` accessible from `self`
    pub fn attach_to(&mut self, other_port: OuterViewPort<V>) -> Arc<RwLock<InnerViewPort<V>>> {
        self.attach_to_port(other_port.0)
    }

    pub fn attach_to_port(&mut self, other_port: ViewPort<V>) -> Arc<RwLock<InnerViewPort<V>>> {
        /* 1 . replace broad cast to remove it as observer
         *     from other port when re-attaching a port
         */
        let keepalive = Arc::new(RwLock::new( self.inner() ));

        other_port.update(); // todo: required?
        self.update_hooks.write().unwrap().clear();
        other_port.add_observer( keepalive.clone() );
        self.set_view( other_port.view.read().unwrap().clone() );
        self.add_update_hook( Arc::new(other_port) );
        self.update();
        keepalive
    }

    pub fn detach(&self) {
        self.update_hooks.write().unwrap().clear();
        self.set_view(None);
    }
}

impl<V: View + ?Sized + 'static> Observer<V> for InnerViewPort<V> where V::Msg: Clone + Send + Sync + 'static {
    fn reset(&mut self, new_view: Option<Arc<V>>) {
         self.set_view(new_view);
    }

    fn notify(&mut self, msg: &V::Msg) {
         self.0.cast.write().unwrap().notify(msg);
    }
}

impl<V: View + ?Sized> UpdateTask for ViewPort<V>
where
    V::Msg: Clone + Send + Sync,
{
    fn update(&self) {
        let v = {
            let t = self.update_hooks.read().unwrap();
            t.iter().cloned().collect::<Vec<_>>()
        };

        for hook in v {
            hook.update();
        }
        self.cast.read().unwrap().update();
    }
}

impl<V: View + ?Sized> Clone for ViewPort<V>
where
    V::Msg: Clone,
{
    fn clone(&self) -> Self {
        ViewPort {
            view: self.view.clone(),
            cast: self.cast.clone(),
            update_hooks: self.update_hooks.clone(),
        }
    }
}

//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>

pub struct InnerViewPort<V: View + ?Sized>(pub ViewPort<V>)
where
    V::Msg: Clone;
pub struct OuterViewPort<V: View + ?Sized>(pub ViewPort<V>)
where
    V::Msg: Clone;

//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>

impl<V: View + ?Sized> InnerViewPort<V>
where
    V::Msg: Clone,
{
    pub fn get_broadcast(&self) -> Arc<RwLock<ObserverBroadcast<V>>> {
        self.0.cast.clone()
    }

    pub fn set_view(&self, view: Option<Arc<V>>) -> Arc<RwLock<ObserverBroadcast<V>>> {
        self.0.set_view(view);
        self.get_broadcast()
    }

    pub fn get_view(&self) -> Option<Arc<V>> {
        self.0.view.read().unwrap().clone()
    }

    pub fn notify(&self, msg: &V::Msg) {
        self.0.cast.write().unwrap().notify(msg);
    }
}

impl<V: View + ?Sized> Clone for InnerViewPort<V>
where
    V::Msg: Clone,
{
    fn clone(&self) -> Self {
        InnerViewPort(self.0.clone())
    }
}

//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>

impl<V: View + ?Sized + 'static> OuterViewPort<V>
where
    V::Msg: Clone,
{
    pub fn get_view(&self) -> Option<Arc<V>> {
        self.0.view.read().unwrap().clone()
    }

    pub fn get_view_arc(&self) -> Arc<RwLock<Option<Arc<V>>>> {
        self.0.view.clone()
    }

    pub fn add_observer(
        &self,
        observer: Arc<RwLock<dyn Observer<V>>>,
    ) -> Arc<RwLock<Option<Arc<V>>>> {
        self.0.add_observer(observer);
        self.get_view_arc()
    }

    pub fn add_reset_fn<F: Fn(Option<Arc<V>>) + Send + Sync + 'static>(
        &self,
        reset: F,
    ) -> Arc<RwLock<ResetFnObserver<V, F>>> {
        let obs = Arc::new(RwLock::new(ResetFnObserver::new(reset)));
        self.add_observer(obs.clone());
        obs
    }

    pub fn add_notify_fn<F: Fn(&V::Msg) + Send + Sync + 'static>(
        &self,
        notify: F,
    ) -> Arc<RwLock<NotifyFnObserver<V, F>>> {
        let obs = Arc::new(RwLock::new(NotifyFnObserver::new(notify)));
        self.add_observer(obs.clone());
        obs
    }
}

impl<V: View + ?Sized> Clone for OuterViewPort<V>
where
    V::Msg: Clone,
{
    fn clone(&self) -> Self {
        OuterViewPort(self.0.clone())
    }
}

impl<V: View + ?Sized> Default for OuterViewPort<V>
where V::Msg: Clone
{
    fn default() -> Self {
        ViewPort::new().into_outer()
    }
}

/*
impl<V: View + ?Sized + 'static> OuterViewPort<V>
where V::Msg: Clone {
    pub fn into_stream<Data>(
        self,
        reset: impl Fn(Option<Arc<V>>, ChannelSender<Data>) + Send + Sync + 'static
    ) -> ChannelReceiver<Data>
    where Data: ChannelData<Item = V::Msg> + 'static,
          Data::IntoIter: Send + Sync + 'static
    {
        let (s, r) = crate::core::channel::channel::<Data>();
        self.add_observer(Arc::new(s.clone()));
        self.add_reset_fn(
            move |view| { reset(view, s.clone()); }
        );
        r
    }
}
*/

//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>

#[derive(Clone)]
pub struct AnyViewPort {
    view: Arc<dyn Any + Send + Sync + 'static>,
    cast: Arc<dyn Any + Send + Sync + 'static>,
    update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>,
}

impl AnyViewPort {
    pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<ViewPort<V>, AnyViewPort> {
        match (
            self.view.clone().downcast::<RwLock<Option<Arc<V>>>>(),
            self.cast.clone().downcast::<RwLock<ObserverBroadcast<V>>>(),
            self.update_hooks.clone(),
        ) {
            (Ok(view), Ok(cast), update_hooks) => Ok(ViewPort {
                view,
                cast,
                update_hooks,
            }),
            _ => Err(self),
        }
    }
}

impl<V: View + ?Sized + 'static> From<ViewPort<V>> for AnyViewPort {
    fn from(port: ViewPort<V>) -> Self {
        AnyViewPort {
            view: port.view as Arc<dyn Any + Send + Sync + 'static>,
            cast: port.cast as Arc<dyn Any + Send + Sync + 'static>,
            update_hooks: port.update_hooks,
        }
    }
}

//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>

#[derive(Clone)]
pub struct AnyOuterViewPort(pub AnyViewPort);

#[derive(Clone)]
pub struct AnyInnerViewPort(pub AnyViewPort);

impl AnyOuterViewPort {
    pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<OuterViewPort<V>, AnyViewPort>
    where
        V::Msg: Clone,
    {
        Ok(OuterViewPort(self.0.downcast::<V>()?))
    }
}

impl<V: View + ?Sized + 'static> From<OuterViewPort<V>> for AnyOuterViewPort
where
    V::Msg: Clone,
{
    fn from(port: OuterViewPort<V>) -> Self {
        AnyOuterViewPort(AnyViewPort {
            view: port.0.view as Arc<dyn Any + Send + Sync + 'static>,
            cast: port.0.cast as Arc<dyn Any + Send + Sync + 'static>,
            update_hooks: port.0.update_hooks,
        })
    }
}

impl AnyInnerViewPort {
    pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<InnerViewPort<V>, AnyViewPort>
    where
        V::Msg: Clone,
    {
        Ok(InnerViewPort(self.0.downcast::<V>()?))
    }
}

impl<V: View + ?Sized + 'static> From<InnerViewPort<V>> for AnyInnerViewPort
where
    V::Msg: Clone,
{
    fn from(port: InnerViewPort<V>) -> Self {
        AnyInnerViewPort(AnyViewPort {
            view: port.0.view as Arc<dyn Any + Send + Sync + 'static>,
            cast: port.0.cast as Arc<dyn Any + Send + Sync + 'static>,
            update_hooks: port.0.update_hooks,
        })
    }
}

//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>