use crate::context::Context;
use crate::error::RenderError;
use crate::json::value::ScopedJson;
use crate::output::Output;
use crate::registry::Registry;
use crate::render::{do_escape, Helper, RenderContext};
pub use self::helper_each::EACH_HELPER;
pub use self::helper_if::{IF_HELPER, UNLESS_HELPER};
pub use self::helper_log::LOG_HELPER;
pub use self::helper_lookup::LOOKUP_HELPER;
pub use self::helper_raw::RAW_HELPER;
pub use self::helper_with::WITH_HELPER;
pub type HelperResult = Result<(), RenderError>;
pub trait HelperDef {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        _: &Helper<'reg, 'rc>,
        _: &'reg Registry<'reg>,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        Ok(None)
    }
    fn call<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        r: &'reg Registry<'reg>,
        ctx: &'rc Context,
        rc: &mut RenderContext<'reg, 'rc>,
        out: &mut dyn Output,
    ) -> HelperResult {
        if let Some(result) = self.call_inner(h, r, ctx, rc)? {
            if r.strict_mode() && result.is_missing() {
                return Err(RenderError::strict_error(None));
            } else {
                
                let output = do_escape(r, rc, result.render());
                out.write(output.as_ref())?;
            }
        }
        Ok(())
    }
}
impl<
        F: for<'reg, 'rc> Fn(
            &Helper<'reg, 'rc>,
            &'reg Registry<'reg>,
            &'rc Context,
            &mut RenderContext<'reg, 'rc>,
            &mut dyn Output,
        ) -> HelperResult,
    > HelperDef for F
{
    fn call<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        r: &'reg Registry<'reg>,
        ctx: &'rc Context,
        rc: &mut RenderContext<'reg, 'rc>,
        out: &mut dyn Output,
    ) -> HelperResult {
        (*self)(h, r, ctx, rc, out)
    }
}
mod block_util;
pub(crate) mod helper_boolean;
mod helper_each;
mod helper_if;
mod helper_log;
mod helper_lookup;
mod helper_raw;
mod helper_with;
#[cfg(feature = "script_helper")]
pub(crate) mod scripting;
#[cfg(test)]
mod test {
    use std::collections::BTreeMap;
    use crate::context::Context;
    use crate::error::RenderError;
    use crate::helpers::HelperDef;
    use crate::json::value::JsonRender;
    use crate::output::Output;
    use crate::registry::Registry;
    use crate::render::{Helper, RenderContext, Renderable};
    #[derive(Clone, Copy)]
    struct MetaHelper;
    impl HelperDef for MetaHelper {
        fn call<'reg: 'rc, 'rc>(
            &self,
            h: &Helper<'reg, 'rc>,
            r: &'reg Registry<'reg>,
            ctx: &'rc Context,
            rc: &mut RenderContext<'reg, 'rc>,
            out: &mut dyn Output,
        ) -> Result<(), RenderError> {
            let v = h.param(0).unwrap();
            if !h.is_block() {
                let output = format!("{}:{}", h.name(), v.value().render());
                out.write(output.as_ref())?;
            } else {
                let output = format!("{}:{}", h.name(), v.value().render());
                out.write(output.as_ref())?;
                out.write("->")?;
                h.template().unwrap().render(r, ctx, rc, out)?;
            };
            Ok(())
        }
    }
    #[test]
    fn test_meta_helper() {
        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("t0", "{{foo this}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t1", "{{#bar this}}nice{{/bar}}")
            .is_ok());
        let meta_helper = MetaHelper;
        handlebars.register_helper("helperMissing", Box::new(meta_helper));
        handlebars.register_helper("blockHelperMissing", Box::new(meta_helper));
        let r0 = handlebars.render("t0", &true);
        assert_eq!(r0.ok().unwrap(), "foo:true".to_string());
        let r1 = handlebars.render("t1", &true);
        assert_eq!(r1.ok().unwrap(), "bar:true->nice".to_string());
    }
    #[test]
    fn test_helper_for_subexpression() {
        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("t2", "{{foo value=(bar 0)}}")
            .is_ok());
        handlebars.register_helper(
            "helperMissing",
            Box::new(
                |h: &Helper<'_, '_>,
                 _: &Registry<'_>,
                 _: &Context,
                 _: &mut RenderContext<'_, '_>,
                 out: &mut dyn Output|
                 -> Result<(), RenderError> {
                    let output = format!("{}{}", h.name(), h.param(0).unwrap().value());
                    out.write(output.as_ref())?;
                    Ok(())
                },
            ),
        );
        handlebars.register_helper(
            "foo",
            Box::new(
                |h: &Helper<'_, '_>,
                 _: &Registry<'_>,
                 _: &Context,
                 _: &mut RenderContext<'_, '_>,
                 out: &mut dyn Output|
                 -> Result<(), RenderError> {
                    let output = format!("{}", h.hash_get("value").unwrap().value().render());
                    out.write(output.as_ref())?;
                    Ok(())
                },
            ),
        );
        let mut data = BTreeMap::new();
        
        
        data.insert("bar0".to_string(), true);
        let r2 = handlebars.render("t2", &data);
        assert_eq!(r2.ok().unwrap(), "bar0".to_string());
    }
}