use {
    std::{sync::{Arc, RwLock}, any::Any},
    cgmath::{Vector2, Point2},
    r3vi::{
        view::{View, ViewPort, OuterViewPort, AnyOuterViewPort, singleton::*, sequence::*},
        buffer::{singleton::*, vec::*}
    },
    laddertypes::{TypeTerm},
    crate::{
        repr_tree::{ReprTree, ReprTreeArc, Context},
        edit_tree::{TreeNav, TreeCursor, TreeNavResult, TreeHeightOp, diagnostics::{Diagnostics, Message}},
        editors::{list::{ListCursorMode}, ObjCommander}
    }
};

#[derive(Clone)]
pub struct EdittreeDisplay {
    /// display view
    pub view: Arc<RwLock<ReprTree>>,

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

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

#[derive(Clone)]
pub struct EdittreeControl {
    /// abstract editor
    pub editor: SingletonBuffer<
                    Option< Arc<dyn Any + Send + Sync> >
                >,

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

    /// commander & navigation
    pub cmd: SingletonBuffer<
                 Option< Arc<RwLock<dyn ObjCommander + Send + Sync>> >
             >,    /// abstract data view

    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>> >
                  >,
}

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

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

    /// editor & commander objects
    pub ctrl: EdittreeControl
}

impl EditTree {
    pub fn new(ctx: Arc<RwLock<Context>>, depth: OuterViewPort<dyn SingletonView<Item = usize>>) -> Self {
        EditTree {
            disp: EdittreeDisplay {
                view: ReprTree::new_arc(Context::parse(&ctx, "Display")),
                diag: None,
                depth,
            },
            ctrl: EdittreeControl {
                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),
            },
            ctx
        }
    }

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

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

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

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

    //\\//\\

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

    pub fn get_edit<T: Send + Sync + 'static>(&self) -> Option<Arc<RwLock<T>>> {
        if let Some(edit) = self.ctrl.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![]
        }
    }
}
*/

impl TreeNav for EditTree {
    fn get_cursor(&self) -> TreeCursor {
        if let Some(tn) = self.ctrl.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.ctrl.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.ctrl.tree_nav.get() {
            tn.read().unwrap().get_mode_view()
        } else {
            OuterViewPort::default()
        }
    }

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

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

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

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

use crate::edit_tree::nav::TreeNavCmd;

impl ObjCommander for EditTree {
    fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {

        if cmd_obj.read().unwrap().get_type() == &Context::parse(&self.ctx, "TreeNavCmd") {
            if let Some(cmd) = cmd_obj.read().unwrap().get_view::<dyn SingletonView<Item = TreeNavCmd>>() {
                match cmd.get() {
                    TreeNavCmd::pxev => self.pxev(),
                    TreeNavCmd::nexd => self.nexd(),
                    TreeNavCmd::qpxev => self.qpxev(),
                    TreeNavCmd::qnexd => self.qnexd(),

                    TreeNavCmd::up => self.up(),
                    TreeNavCmd::dn => self.dn(),

                    _ => TreeNavResult::Continue
                }
            } else {
                TreeNavResult::Exit
            }
        } else if let Some(cmd) = self.ctrl.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 EditTree {
    fn get_msg_port(&self) -> OuterViewPort<dyn SequenceView<Item = Message>> {
        self.get_diag()
    }
}