use {
    laddertypes::TypeTerm,
    r3vi::{
        buffer::singleton::SingletonBuffer,
        view::{
            AnyOuterViewPort,
            singleton::*
        }
    },
    crate::{
        repr_tree::{Context, ReprTree, ReprTreeExt, ReprLeaf, GenericReprTreeMorphism},
        editors::digit::DigitEditor,
    },
    std::sync::{Arc, RwLock}
};

pub fn init_ctx( ctx: Arc<RwLock<Context>> ) {
    // todo: proper scoping of Radix variable
    ctx.write().unwrap().add_varname("Radix");

    let digit_make_edittree = GenericReprTreeMorphism::new(
        Context::parse(&ctx, "<Digit Radix>"),
        Context::parse(&ctx, "<Digit Radix>~EditTree"),
        {
                let ctx = ctx.clone();
                move |src_rt, σ| {
                    let radix =
                        match σ.get( &laddertypes::TypeID::Var(ctx.read().unwrap().get_var_typeid("Radix").unwrap()) ) {
                            Some(TypeTerm::Num(n)) => *n as u32,
                            _ => 0
                        };

                    let char_buf = SingletonBuffer::<char>::new('?');

                    /* Create EditTree object
                     */
                    let mut edittree = DigitEditor::new(
                        ctx.clone(),
                        radix,
                        char_buf
                    ).into_node(
                        r3vi::buffer::singleton::SingletonBuffer::<usize>::new(0).get_port()
                    );

                    src_rt.write().unwrap()
                        .insert_branch(
                            ReprTree::from_singleton_buffer(
                                Context::parse(&ctx, "EditTree"),
                                SingletonBuffer::new(Arc::new(RwLock::new(edittree)))
                            )
                        );

                    ctx.read().unwrap().setup_edittree( src_rt );
                }
        }
    );

    let digit_morph_char_to_edittree = GenericReprTreeMorphism::new(
        Context::parse(&ctx, "<Digit Radix>~Char"),
        Context::parse(&ctx, "<Digit Radix>~EditTree"),

        {
                let ctx = ctx.clone();
                move |src_rt, σ| {
                    let radix =
                        match σ.get( &laddertypes::TypeID::Var(ctx.read().unwrap().get_var_typeid("Radix").unwrap()) ) {
                            Some(TypeTerm::Num(n)) => *n as u32,
                            _ => 0
                        };

                    /* Create EditTree object
                     */
                    let mut edittree = DigitEditor::new(
                        ctx.clone(),
                        radix,
                        src_rt
                            .descend( Context::parse(&ctx, "Char") ).unwrap()
                            .singleton_buffer::<char>()
                    ).into_node(
                        r3vi::buffer::singleton::SingletonBuffer::<usize>::new(0).get_port()
                    );

                    src_rt.write().unwrap()
                        .insert_branch(
                            ReprTree::from_singleton_buffer(
                                Context::parse(&ctx, "EditTree"),
                                SingletonBuffer::new(Arc::new(RwLock::new(edittree)))
                            )
                        );

                    ctx.read().unwrap().setup_edittree( src_rt );
                }
        }
    );

    let digit_morph_char_from_edittree = GenericReprTreeMorphism::new(
        Context::parse(&ctx, "<Digit Radix>~EditTree"),
        Context::parse(&ctx, "<Digit Radix>~Char"),

        {
            let ctx = ctx.clone();
            move |src_rt, σ| {
                let edittree = src_rt.edittree( &ctx );
                let port =
                        edittree
                            .get()
                            .read().unwrap()
                            .get_edit::<DigitEditor>().unwrap()
                            .read().unwrap()
                            .get_char_port();

                    src_rt.insert_leaf(
                        Context::parse(&ctx, "Char"),
                        ReprLeaf::from_view( port )
                    )
            }
        }
    );

    let digit_morph_char_to_u64 = GenericReprTreeMorphism::new(
        Context::parse(&ctx, "<Digit Radix>~Char"),
        Context::parse(&ctx, "<Digit Radix>~ℤ_2^64~machine.UInt64"),

        {
            let ctx = ctx.clone();
            move |rt: &mut Arc<RwLock<ReprTree>>, σ: &std::collections::HashMap<laddertypes::TypeID, TypeTerm>| {
                    /* infer radix from type
                     */
                    let radix_typeid = ctx.read().unwrap().get_var_typeid("Radix").unwrap();
                    let radix =
                        match σ.get( &laddertypes::TypeID::Var(radix_typeid) ) {
                            Some(TypeTerm::Num(n)) => (*n) as u32,
                            x => {
                                eprintln!("invalid radix {:?}", x);
                                0
                            }
                        };

                    if radix <= 16 {
                        if let Some(src_rt) = rt.descend(Context::parse(&ctx, "Char")) {
                            /* insert projected view into ReprTree
                             */
                            let u64_view = 
                                    src_rt.view_char()
                                        .map(move |c| c.to_digit(radix).unwrap_or(0) as u64);

                            rt.write().unwrap().attach_leaf_to::<dyn SingletonView<Item = u64>>(
                                Context::parse(&ctx, "ℤ_2^64~machine.UInt64").get_lnf_vec().into_iter(),
                                u64_view
                            );
                        } else {
                            eprintln!("could not find required source representation: <Digit {}>~Char", radix);
                        }
                    } else {
                        eprintln!("radix too large ({})", radix);
                    }
            }
        }
    );


    let digit_morph_u64_to_char = GenericReprTreeMorphism::new(
        Context::parse(&ctx, "<Digit Radix>~ℤ_2^64~machine.UInt64"),
        Context::parse(&ctx, "<Digit Radix>~Char"),
        {
            let ctx = ctx.clone();
            move |rt: &mut Arc<RwLock<ReprTree>>, σ: &std::collections::HashMap<laddertypes::TypeID, TypeTerm>| {
                /* infer radix from type
                 */
                let radix  =
                    match σ.get( &laddertypes::TypeID::Var(ctx.read().unwrap().get_var_typeid("Radix").unwrap()) ) {
                       Some(TypeTerm::Num(n)) => (*n) as u32,
                        _ => 0
                    };

                if radix <= 16 {
                    /* insert projected view into ReprTree
                     */
                    let char_view = 
                        rt.descend(Context::parse(&ctx, "ℤ_2^64~machine.UInt64"))
                            .unwrap()
                            .view_u64()
                            .map(move |digit| char::from_digit((digit%radix as u64) as u32, radix).unwrap_or('?'));

                    rt.write().unwrap().attach_leaf_to::<dyn SingletonView<Item = char>>(
                        Context::parse(&ctx, "Char").get_lnf_vec().into_iter(),
                        char_view
                    );
                } else {
                    eprintln!("radix too large ({})", radix);
                }
            }
        }
    );


    ctx.write().unwrap().morphisms.add_morphism( digit_make_edittree );
    ctx.write().unwrap().morphisms.add_morphism( digit_morph_char_to_edittree );
    ctx.write().unwrap().morphisms.add_morphism( digit_morph_char_from_edittree );
    ctx.write().unwrap().morphisms.add_morphism( digit_morph_char_to_u64 );
    ctx.write().unwrap().morphisms.add_morphism( digit_morph_u64_to_char );
}