use {
    laddertypes::TypeTerm,
    r3vi::{
        buffer::singleton::SingletonBuffer,
        view::{
            AnyOuterViewPort,
            singleton::*
        }
    },
    crate::{
        repr_tree::{Context, ReprTree, ReprTreeExt, ReprLeaf},
        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 morphtype =
            crate::repr_tree::MorphismType {
                src_type: Context::parse(&ctx, "<Digit Radix>"),
                dst_type: Context::parse(&ctx, "<Digit Radix>~EditTree")
            };

    ctx.write().unwrap()
        .morphisms
        .add_morphism(
            morphtype,
            {
                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
                        };

                    /* get char representation or create it if not available
                     */
                    let char_rt =
                        if let Some(crt) = src_rt.descend(Context::parse(&ctx, "Char")) {
                            crt
                        } else {
                            let crt = ReprTree::from_singleton_buffer(
                                Context::parse(&ctx, "Char"),
                                SingletonBuffer::new('\0')
                            );
                            src_rt.insert_branch(crt.clone());
                            crt
                        };

                    /* 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(edittree)
                            )
                        );
                }
            }
        );

    let morphtype =
            crate::repr_tree::MorphismType {
                src_type: Context::parse(&ctx, "<Digit Radix>~Char"),
                dst_type: Context::parse(&ctx, "<Digit Radix>~ℤ_2^64~machine.UInt64")
            };

    ctx.write().unwrap()
        .morphisms
        .add_morphism(
            morphtype,
            {
                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 morphtype =
            crate::repr_tree::MorphismType {
                src_type: Context::parse(&ctx, "<Digit Radix>~ℤ_2^64~machine.UInt64"),
                dst_type: Context::parse(&ctx, "<Digit Radix>~Char")
            };

    ctx.write().unwrap().morphisms
        .add_morphism(morphtype, {
            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);
                }
            }
        });
}