package xyz.wbsite.frame.utils;

import xyz.wbsite.frame.base.LocalData;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;

public class FreeMarkerUtil {

    /**
     * 获取自动装配的Configuration，配置涉及视图层，谨慎修改！！！
     *
     * @return 配置
     */
    public static Configuration getDefaultConfiguration() {
        return LocalData.getBean(Configuration.class);
    }

    /**
     * 获取自动装配配置工厂，一般用于创建独立Configuration，且复用配置
     * 支持修改默认配置，并且不会影响视图层
     *
     * @return 配置工厂
     */
    public static FreeMarkerConfigurer getDefaultConfigurer() {
        return LocalData.getBean(FreeMarkerConfigurer.class);
    }

    /**
     * 获取复用系统配置的新配置对象
     *
     * @param basePackagePath 模板路径
     * @return 配置
     */
    public static Configuration getNewConfiguration(String basePackagePath) {
        Configuration newConfiguration = getNewConfiguration();
        newConfiguration.setClassForTemplateLoading(FreeMarkerUtil.class, basePackagePath);
        return newConfiguration;
    }

    /**
     * 获取复用系统配置的新配置对象
     *
     * @return 配置
     */
    public static Configuration getNewConfiguration() {
        Configuration configuration;
        try {
            configuration = getDefaultConfigurer().createConfiguration();
        } catch (IOException | TemplateException e) {
            configuration = new Configuration(Configuration.VERSION_2_3_28);
        }
        return configuration;
    }

    /**
     * 通过模板渲染
     *
     * @param template 模板
     * @param model    数据模型
     * @param writer   字符输出流
     */
    public static boolean render(Template template, Object model, Writer writer) {
        try {
            // 渲染
            template.process(model, writer);
            // 关闭流
            writer.close();
            return true;
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 通过模板资源渲染
     *
     * @param defaultConfiguration 是否使用默认配置，与视图模板同目录同配置
     * @param resources            模板资源
     * @param model                数据模型
     * @param writer               字符输出流
     */
    public static boolean renderByResources(boolean defaultConfiguration, String resources, Object model, Writer writer) {
        Configuration newConfiguration = defaultConfiguration ? getDefaultConfiguration() : getNewConfiguration();
        try {
            // 构建模板
            Template template = newConfiguration.getTemplate(resources);
            // 渲染模板
            return render(template, model, writer);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 通过模板资源渲染
     *
     * @param defaultConfiguration 是否使用默认配置，与视图模板同目录同配置
     * @param resources            模板资源
     * @param model                数据模型
     * @return 渲染结果字符串
     */
    public static String renderByResources(boolean defaultConfiguration, String resources, Object model) {
        StringWriter writer = new StringWriter();
        boolean b = renderByResources(defaultConfiguration, resources, model, writer);
        if (!b) return "";
        return writer.toString();
    }

    /**
     * 通过模板资源渲染
     *
     * @param resources 模板资源
     * @param model     数据模型
     * @param writer    字符输出流
     */
    public static boolean renderByResources(String resources, Object model, Writer writer) {
        return renderByResources(true, resources, model, writer);
    }

    /**
     * 通过模板资源渲染
     *
     * @param resources 模板资源
     * @param model     数据模型
     * @return 渲染结果字符串
     */
    public static String renderByResources(String resources, Object model) {
        StringWriter writer = new StringWriter();
        boolean b = renderByResources(true, resources, model, writer);
        if (!b) return "";
        return writer.toString();
    }

    /**
     * 通过模板文件渲染
     *
     * @param file   模板文件
     * @param model  数据模型
     * @param writer 字符输出流
     */
    public static boolean renderByFile(File file, Object model, Writer writer) {
        try {
            // 模板名称
            String name = MD5Util.encode(file.getAbsolutePath());
            // 获取缓存模板
            Template template = (Template) getDefaultConfiguration().getCacheStorage().get(name);
            if (template == null) {
                // 构建模板，此处构建的模板需手动缓存
                template = new Template(file.getName(), FileUtil.readFileToString(file), getDefaultConfiguration());
                getDefaultConfiguration().getCacheStorage().put(name, template);
            }
            // 渲染模板
            return render(template, model, writer);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 通过模板文件渲染
     *
     * @param file  模板文件
     * @param model 数据模型
     * @return 渲染结果字符串
     */
    public static String renderByFile(File file, Object model) {
        StringWriter writer = new StringWriter();
        boolean b = renderByFile(file, model, writer);
        if (!b) return "";
        return writer.toString();
    }

    /**
     * 通过模板字符串渲染
     *
     * @param source 模板字符串
     * @param model  数据模型
     * @param writer 字符输出流
     */
    public static boolean renderByString(String source, Object model, Writer writer) {
        try {
            // 模板名称
            String name = MD5Util.encode(source);
            // 获取缓存模板
            Template template = (Template) getDefaultConfiguration().getCacheStorage().get(name);
            if (template == null) {
                // 构建模板，此处构建的模板需手动缓存
                template = new Template(name, source, getDefaultConfiguration());
                getDefaultConfiguration().getCacheStorage().put(name, template);
            }
            // 渲染模板
            return render(template, model, writer);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 通过模板字符串渲染
     *
     * @param source 模板字符串
     * @param model  数据模型
     * @return 渲染结果字符串
     */
    public static String renderByString(String source, Object model) {
        StringWriter writer = new StringWriter();
        boolean b = renderByString(source, model, writer);
        if (!b) return "";
        return writer.toString();
    }
}
