class EnjoyRender implements Render {
    static final Logger log = LoggerFactory.getLogger(EnjoyRender.class);

    private static EnjoyRender _global;

    public static EnjoyRender global() {
        if (_global == null) {
            _global = new EnjoyRender();
        }

        return _global;
    }



    private final ClassLoader classLoader;
    private final String viewPrefix;

    private Engine provider = null;
    private Engine providerOfDebug = null;

    /**
     * 引擎提供者
     * */
    public Engine getProvider() {
        return provider;
    }

    /**
     * 引擎提供者（调试模式）
     * */
    public Engine getProviderOfDebug() {
        return providerOfDebug;
    }

    //不要要入参，方便后面多视图混用
    //
    public EnjoyRender() {
        this(AppClassLoader.global(), null);
    }

    public EnjoyRender(ClassLoader classLoader) {
        this(classLoader, null);
    }

    public EnjoyRender(ClassLoader classLoader, String viewPrefix) {
        this.classLoader = classLoader;
        if(viewPrefix == null){
            this.viewPrefix = ViewConfig.getViewPrefix();
        }else {
            this.viewPrefix = viewPrefix;
        }

        //开始中文支持
        Engine.setChineseExpression(true);

        forDebug();
        forRelease();

        Solon.app().shared().forEach((k, v) -> {
            putVariable(k, v);
        });

        Solon.app().onSharedAdd((k, v) -> {
            putVariable(k, v);
        });
    }


    private void forDebug() {
        if (Solon.cfg().isDebugMode() == false) {
            return;
        }

        if (Solon.cfg().isFilesMode() == false) {
            return;
        }

        if (providerOfDebug != null) {
            return;
        }

        //添加调试模式
        URL rooturi = ResourceUtil.getResource(classLoader,"/");
        if (rooturi == null) {
            return;
        }

        providerOfDebug = Engine.create("debug");
        providerOfDebug.setDevMode(true);


        String rootdir = rooturi.toString().replace("target/classes/", "");
        File dir = null;

        if (rootdir.startsWith("file:")) {
            String dir_str = rootdir + "src/main/resources" + viewPrefix;
            dir = new File(URI.create(dir_str));
            if (!dir.exists()) {
                dir_str = rootdir + "src/main/webapp" + viewPrefix;
                dir = new File(URI.create(dir_str));
            }
        }

        try {
            if (dir != null && dir.exists()) {
                providerOfDebug.setBaseTemplatePath(dir.getPath());
                providerOfDebug.setSourceFactory(new FileSourceFactory());
            }

            //通过事件扩展
            EventBus.publish(providerOfDebug);
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
    }

    private void forRelease() {
        if (provider != null) {
            return;
        }

        provider = Engine.use();
        provider.setDevMode(Solon.cfg().isDebugMode());

        try {
            provider.setBaseTemplatePath(viewPrefix);
            provider.setSourceFactory(new ClassPathSourceFactory2(classLoader));

            //通过事件扩展
            EventBus.publish(provider);
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
    }

    /**
     * 添加共享指令（自定义标签）
     */
    public void putDirective(String name, Class<? extends Directive> clz) {
        try {
            provider.addDirective(name, clz);

            if (providerOfDebug != null) {
                providerOfDebug.addDirective(name, clz);
            }
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
    }

    /**
     * 添加共享变量
     */
    public void putVariable(String name, Object value) {
        try {
            provider.addSharedObject(name, value);

            if (providerOfDebug != null) {
                providerOfDebug.addSharedObject(name, value);
            }
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
    }

    /**
     * 添加共享模板函数
     */
    public void putFunction(String path) {
        try {
            provider.addSharedFunction(path);

            if (providerOfDebug != null) {
                providerOfDebug.addSharedFunction(path);
            }
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
    }

    @Override
    public void render(Object obj, Context ctx) throws Throwable {
        if (obj == null) {
            return;
        }

        if (obj instanceof ModelAndView) {
            render_mav((ModelAndView) obj, ctx, () -> ctx.outputStream());
        } else {
            ctx.output(obj.toString());
        }
    }

    @Override
    public String renderAndReturn(Object obj, Context ctx) throws Throwable {
        if (obj == null) {
            return null;
        }

        if (obj instanceof ModelAndView) {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            render_mav((ModelAndView) obj, ctx, () -> outputStream);

            return outputStream.toString();
        } else {
            return obj.toString();
        }
    }

    public void render_mav(ModelAndView mv, Context ctx, SupplierEx<OutputStream> outputStream) throws Throwable {<FILL_FUNCTION_BODY>}
}

        if (ctx.contentTypeNew() == null) {
            ctx.contentType("text/html;charset=utf-8");
        }

        if (ViewConfig.isOutputMeta()) {
            ctx.headerSet(ViewConfig.HEADER_VIEW_META, "EnjoyRender");
        }

        //添加 context 变量
        mv.putIfAbsent("context", ctx);

        Template template = null;

        if (providerOfDebug != null) {
            try {
                template = providerOfDebug.getTemplate(mv.view());
            } catch (Exception e) {
                //忽略不计
            }
        }

        if (template == null) {
            template = provider.getTemplate(mv.view());
        }

        // 输出流
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream.get(), ServerProps.response_encoding));
        template.render(mv.model(), writer);
        writer.flush();
    
<no_super_class>