use {
    std::{sync::{Arc, RwLock}, any::Any},
    cgmath::{Vector2, Point2},
    r3vi::{
        view::{View, ViewPort, OuterViewPort, AnyOuterViewPort, singleton::*, sequence::*},
        buffer::{singleton::*}
    },
    crate::{
        type_system::{ReprTree, Context, TypeTerm},
        terminal::{TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult, TerminalAtom},
        diagnostics::{Diagnostics, Message},
        tree::{TreeNav, TreeCursor, TreeNavResult, TreeHeightOp},
        editors::list::{ListCursorMode},
        commander::ObjCommander,
    }
};

/* TODO: refactoring proposal

struct NestedNodeDisplay {
    /// display view
    pub view: Option< OuterViewPort<dyn TerminalView> >,

    /// diagnostics
    pub diag: Option< OuterViewPort<dyn SequenceView<Item = Message>> >,

    /// depth
    pub depth: SingletonBuffer< usize >,    
}

struct NestedNodeEdit {
    /// abstract editor
    pub editor: SingletonBuffer<
                    Option< Arc<dyn Any + Send + Sync> >
>,

    pub input_buf:: VecBuffer< NestedNode >,
    pub output_buf: VecBuffer< NestedNode >,

    /// commander & navigation
    pub cmd: SingletonBuffer<
                 Option< Arc<RwLock<dyn ObjCommander + Send + Sync>> >
             >,
    pub close_char: SingletonBuffer<
                        Option< char >
            >,

    // could be replaced by cmd when TreeNav -CmdObjects are used
    pub tree_nav: SingletonBuffer<
                      Option< Arc<RwLock<dyn TreeNav + Send + Sync>> >
                  >,    
}

pub struct NewNestedNode {
    /// context
    pub ctx: Arc<RwLock<Context>>,

    /// abstract data view
    pub data: Arc<RwLock<ReprTree>>,

    /// viewports for terminal display
    pub disp: NestedNodeDisplay,

    /// editor & commander objects
    pub edit: NestedNodeEdit
}

*/

#[derive(Clone)]
pub struct NestedNode {    
    /// context
    pub ctx: Arc<RwLock<Context>>,

    /// abstract data view
    pub data: Arc<RwLock<ReprTree>>,

    /// display view
    pub view: Option< OuterViewPort<dyn TerminalView> >,

    /// diagnostics
    pub diag: Option< OuterViewPort<dyn SequenceView<Item = Message>> >,

    /// depth
    pub depth: OuterViewPort< dyn SingletonView<Item = usize> >,

    /// abstract editor
    pub editor: SingletonBuffer<
                    Option< Arc<dyn Any + Send + Sync> >
                >,

    pub spillbuf: Arc<RwLock<Vec<Arc<RwLock<NestedNode>>>>>,

    /// commander & navigation
    pub cmd: SingletonBuffer<
                 Option< Arc<RwLock<dyn ObjCommander + Send + Sync>> >
             >,
    pub close_char: SingletonBuffer<
                        Option< char >
                    >,
    pub tree_nav: SingletonBuffer<
                      Option< Arc<RwLock<dyn TreeNav + Send + Sync>> >
                  >,
}

impl NestedNode {
    pub fn new(ctx: Arc<RwLock<Context>>, data: Arc<RwLock<ReprTree>>, depth: OuterViewPort<dyn SingletonView<Item = usize>>) -> Self {
        NestedNode {
            ctx,
            data,
            view: None,
            diag: None,
            depth,
            editor: SingletonBuffer::new(None),
            spillbuf: Arc::new(RwLock::new(Vec::new())),
            cmd: SingletonBuffer::new(None),
            close_char: SingletonBuffer::new(None),
            tree_nav: SingletonBuffer::new(None)
        }
    }

    /* TODO: move into separate file/module
    */
    pub fn from_char(ctx: Arc<RwLock<Context>>, c: char) -> NestedNode {
        let buf = r3vi::buffer::singleton::SingletonBuffer::<char>::new(c);

        NestedNode::new(
            ctx.clone(),
            ReprTree::new_leaf(
                (&ctx, "( Char )"),
                buf.get_port().into()
            ),
            SingletonBuffer::new(0).get_port()
        )
            .set_view(buf.get_port()
                      .map(|c| TerminalAtom::from(c))
                      .to_index()
                      .map_key(
                          |_x| {
                              Point2::new(0, 0)
                          },
                          |p| {
                              if *p == Point2::new(0,0) { Some(()) } else { None }
                          })
            )
            .set_editor(Arc::new(RwLock::new(buf)))
    }

    
    //\\//\\

    pub fn morph(self, ty: TypeTerm) -> NestedNode {
        Context::morph_node(self, ty)
    }

    pub fn get_type(&self) -> TypeTerm {
        self.data.read().unwrap().get_type().clone()
    }

    //\\//\\
    
    pub fn set_editor(mut self, editor: Arc<dyn Any + Send + Sync>) -> Self {
        self.editor.set(Some(editor));
        self
    }

    pub fn set_view(mut self, view: OuterViewPort<dyn TerminalView>) -> Self {
        self.view = Some(view);
        self
    }

    pub fn set_cmd(mut self, cmd: Arc<RwLock<dyn ObjCommander + Send + Sync>>) -> Self {
        self.cmd.set(Some(cmd));
        self
    }

    pub fn set_nav(mut self, nav: Arc<RwLock<dyn TreeNav + Send + Sync>>) -> Self {
        self.tree_nav.set(Some(nav));
        self
    }

    pub fn set_diag(mut self, diag: OuterViewPort<dyn SequenceView<Item = Message>>) -> Self {
        self.diag = Some(diag);
        self
    }

    //\\//\\

    pub fn get_diag(&self) -> OuterViewPort<dyn SequenceView<Item = Message>> {
        self.diag.clone().unwrap_or(ViewPort::new().into_outer())
    }

    pub fn get_view(&self) -> OuterViewPort<dyn TerminalView> {
        self.view.clone().unwrap_or(ViewPort::new().into_outer())
    }
    
    pub fn get_data_port<'a, V: View + ?Sized + 'static>(&'a self, type_str: impl Iterator<Item = &'a str>) -> Option<OuterViewPort<V>>
    where V::Msg: Clone {
        let ctx = self.ctx.clone();
        let type_ladder = type_str.map(|s| ((&ctx, s)).into());

        let repr_tree = ReprTree::descend_ladder(&self.data, type_ladder)?;
        repr_tree.clone().read().unwrap()
            .get_port::<V>().clone()
    }

    pub fn get_data_view<'a, V: View + ?Sized + 'static>(&'a self, type_str: impl Iterator<Item = &'a str>) -> Option<Arc<V>>
    where V::Msg: Clone {
        self.get_data_port::<V>(type_str)?.get_view()
    }

    /* TODO
    pub fn get_seq_view<'a, T: Clone>(&self, type_str: impl Iterator<Item = &'a str>) -> Option<OuterViewPort<dyn SingletonView<Item = T>>> {
        self.get_data_view::<dyn SequenceView<Item = NestedNode>>(type_str)
            .unwrap()
            .map({
                move |node| {
                    node.get_data_view::<dyn SingletonView<Item = T>>().get()
                }
            })
    }
     */
    
    pub fn get_edit<T: Send + Sync + 'static>(&self) -> Option<Arc<RwLock<T>>> {
        if let Some(edit) = self.editor.get() {
            if let Ok(edit) = edit.downcast::<RwLock<T>>() {
                Some(edit)
            } else {
                None
            }
        } else {
            None
        }
    }
}

/*
impl TreeType for NestedNode {
    fn get_type(&self, addr: &TreeAddr) -> TypeLadder {
        if let Some(editor) = self.editor {
            editor.read().unwrap().get_type(addr)
        } else {
            vec![]
        }
    }
}
 */

/* TODO: remove that at some point
*/
impl TerminalEditor for NestedNode {
    fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
        self.get_view()
    }

    fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
        let buf = SingletonBuffer::new(event.clone());

        if let Some(cmd) = self.cmd.get() {
            cmd.write().unwrap().send_cmd_obj(
                ReprTree::new_leaf(
                    self.ctx.read().unwrap().type_term_from_str("( TerminalEvent )").unwrap(),
                    AnyOuterViewPort::from(buf.get_port())
                ));
        }

        TerminalEditorResult::Continue
    }
}

impl TreeNav for NestedNode {
    fn get_cursor(&self) -> TreeCursor {
        if let Some(tn) = self.tree_nav.get() {
            tn.read().unwrap().get_cursor()
        } else {
            TreeCursor::default()
        }
    }

    fn get_addr_view(&self) -> OuterViewPort<dyn SequenceView<Item = isize>> {
        if let Some(tn) = self.tree_nav.get() {
            tn.read().unwrap().get_addr_view()
        } else {
            OuterViewPort::default()
        }
    }

    fn get_mode_view(&self) -> OuterViewPort<dyn SingletonView<Item = ListCursorMode>> {
        if let Some(tn) = self.tree_nav.get() {
            tn.read().unwrap().get_mode_view()
        } else {
            OuterViewPort::default()
        }        
    }

    fn get_cursor_warp(&self) -> TreeCursor {
        if let Some(tn) = self.tree_nav.get() {
            tn.read().unwrap().get_cursor_warp()
        } else {
            TreeCursor::default()
        }
    }

    fn get_height(&self, op: &TreeHeightOp) -> usize {
        if let Some(tn) = self.tree_nav.get() {
            tn.read().unwrap().get_height( op )
        } else {
            0
        }
    }

    fn goby(&mut self, direction: Vector2<isize>) -> TreeNavResult {
        if let Some(tn) = self.tree_nav.get() {
            tn.write().unwrap().goby(direction)
        } else {
            TreeNavResult::Exit
        }
    }

    fn goto(&mut self, new_cursor: TreeCursor) -> TreeNavResult {
        if let Some(tn) = self.tree_nav.get() {
            tn.write().unwrap().goto(new_cursor)
        } else {
            TreeNavResult::Exit
        }
    }
}

impl ObjCommander for NestedNode {
    fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
        if let Some(cmd) = self.cmd.get() {
            // todo: filter out tree-nav cmds and send them to tree_nav
            cmd.write().unwrap().send_cmd_obj(cmd_obj)
        } else {
            TreeNavResult::Exit
        }
    }
}

impl Diagnostics for NestedNode {
    fn get_msg_port(&self) -> OuterViewPort<dyn SequenceView<Item = Message>> {
        self.get_diag()
    }
}