/**
 * Copyright (c) 2018 人人开源 All rights reserved.
 * 
 * https://www.renren.io
 * 
 * 版权所有,侵权必究!
 */
package com.zt.generator.utils;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.zt.common.exception.RenException;
import com.zt.generator.model.Column;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.texen.util.FileUtil;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 代码生成器 工具类
 *
 * @author Mark sunlightcs@gmail.com
 */
public class GenUtils {
    public static List getTemplates() {
        List templates = new ArrayList();
        templates.add("template/Entity.java.vm");
        templates.add("template/Dao.java.vm");
        templates.add("template/Dao.xml.vm");
        templates.add("template/Service.java.vm");
        templates.add("template/Controller.java.vm");
        templates.add("template/Excel.java.vm");
        templates.add("template/index.vue.vm");
        templates.add("template/add-or-update.vue.vm");
        // templates.add("template/mysql.vm");
        return templates;
    }
    public static void generatorCode(String tableName, String isGenService, String className, String isPageFlag,
            String isExport, JSONArray columns, String packageName, String javaFilePath, String vueFilePath) {
        boolean hasBigDecimal = false, hasDate = false;
        // 列信息
        List columsList = new ArrayList<>();
        String filter = ",id,creator,updater,update_date,create_date,is_delete,tenant_id,company_id,dept_id,)";
        String createFilter = ",creator,create_date,tenant_id,company_id,dept_id,)";
        String updateFilter = ",updater,update_date,is_delete,)";
        int createCount = 0, updateCount = 0;
        Column pkColumn = null;
        for (int i = 0; i < columns.size(); i++) {
            JSONObject object = columns.getJSONObject(i);
            Column columnEntity = new Column();
            String columnName = object.getString("columnName");
            if ("id".equals(columnName.toLowerCase())) {
                continue;
            }
            if (createFilter.indexOf("," + columnName.toLowerCase() + ",") >= 0) {
                createCount++;
                continue;
            }
            if (updateFilter.indexOf("," + columnName.toLowerCase() + ",") >= 0) {
                updateCount++;
                continue;
            }
            columnEntity.setColumnName(columnName);
            columnEntity.setDataType(object.getString("typeName"));
            String comment = object.getString("remarks");
            if (StringUtils.isNotEmpty(comment)) {
                comment = comment.trim();
            }
            int index = comment.indexOf(" ");
            if (index > 0) {
                comment = comment.substring(0, index);
            }
            index = comment.indexOf("。");
            if (index > 0) {
                comment = comment.substring(0, index);
            }
            columnEntity.setComments(comment);
            columnEntity.setIsSelectColumn(object.getString("isSelectColumn"));
            columnEntity.setIsTableColumn(object.getString("isTableColumn"));
            columnEntity.setDictType(object.getString("dictType"));
            columnEntity.setOp(object.getString("op"));
            // 列名转换成Java属性名
            String attrName = columnToJava(columnEntity.getColumnName());
            columnEntity.setAttrName(StringUtils.uncapitalize(attrName));
            // 列的数据类型,转换成Java类型
            String attrType = map.get(columnEntity.getDataType());
            if (StringUtils.isBlank(attrType)) {
                attrType = "String";
            }
            columnEntity.setAttrType(attrType);
            if (!hasBigDecimal && attrType.equals("BigDecimal")) {
                hasBigDecimal = true;
            }
            if (!hasDate && attrType.equals("Date")) {
                hasDate = true;
            }
            // 是否主键
            if ("Y".equalsIgnoreCase(object.getString("isKey"))) {
                if (pkColumn != null) {
                    throw new RenException("请不要设置联合主键");
                }
                pkColumn = columnEntity;
            }
            columsList.add(columnEntity);
        }
        // 设置velocity资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);
        // 封装模板数据
        Map map = new HashMap<>();
        map.put("tableName", tableName);// 表名@TableName("${tableName}")
        map.put("comments", tableName);
        if (createCount < 5) {
            throw new RenException("数据库字段缺失,请按照规范");
        } else if (updateCount == 3) {
            map.put("entityType", "BusiEntity");
        } else {
            map.put("entityType", "BusiLogEntity");
        }
        map.put("pk", pkColumn);
        map.put("package", packageName);
        map.put("className", className);  //实体类名字
        // private ${className}Service ${classBean}Service;
        map.put("classBean", StringUtils.uncapitalize(className));  //实体实例名字
        // model bean的名称insert(@RequestBody ${className} ${modelName})
        map.put("modelName", StringUtils.uncapitalize(className)); //模型名字
        // @RequestMapping("/${moduleName}/${pathName}")
        String moduleName = packageName.substring(packageName.lastIndexOf(".") + 1, packageName.length());
        map.put("moduleName", moduleName); //模块名字  包名(相对路径)
        String pathName = StrUtil.toSymbolCase(className, '/');// 驼峰转下划线
        if (pathName.indexOf(moduleName) == 0) {
            pathName = pathName.substring(moduleName.length());
            if (pathName.length() > 0 && pathName.startsWith("/")) {
                pathName = pathName.substring(1);
            }
        }
        map.put("importPath", tableName.replaceAll("_", "-"));// url请求的
        map.put("pathName", pathName);// url请求的
        map.put("permName", pathName.replace("/", ":"));// 权限的
        map.put("columns", columsList);
        map.put("selectColumns", columsList.stream().filter(column -> "true".equals(column.getIsSelectColumn()))
                .collect(Collectors.toList()));
        map.put("version", "1.0.0");
        map.put("author", "zt generator");
        map.put("email", "");
        map.put("isExport", isExport);
        map.put("hasBigDecimal", hasBigDecimal);
        map.put("hasDate", hasDate);
        map.put("datetime", DateUtil.now());
        map.put("date", DateUtil.format(new Date(), DatePattern.NORM_DATE_FORMAT));
        for (int i = 0; i <= 10; i++) {
            map.put("id" + i, IdWorker.getId());
        }
        VelocityContext context = new VelocityContext(map);
        // 获取模板列表
        List templates = getTemplates();
        for (String template : templates) {
            if (!"true".equals(isGenService) && (template.contains("Service") || template.contains("Controller"))) {
                continue;
            }
            if (!"true".equals(isPageFlag)
                    && (template.contains("index.vue") || template.contains("add-or-update.vue"))) {
                continue;
            }
            if (!"true".equals(isExport) && template.contains("Excel")) {
                continue;
            }
            // 渲染模板
            String fileName = "";
            String filePath = "";
            if (template.contains("index.vue")) {
                fileName = className + ".vue";
                filePath = getFilePath(template, vueFilePath, packageName, moduleName);
            } else if (template.contains("add-or-update.vue")) {
                fileName = className + "-AddOrUpdate.vue";
                filePath = getFilePath(template, vueFilePath, packageName, moduleName);
            } else if (template.contains("Entity.java.vm")) {
                fileName = className + ".java";
                filePath = getFilePath(template, javaFilePath, packageName, moduleName);
            } else {
                fileName = className + template.split("/")[1].replaceAll(".vm", "");
                filePath = getFilePath(template, javaFilePath, packageName, moduleName);
            }
            FileUtil.mkdir(filePath);
            try {
                Writer writer = new OutputStreamWriter(new FileOutputStream(filePath + "/" + fileName), "UTF-8");
                Template tpl = Velocity.getTemplate(template, "UTF-8");
                tpl.merge(context, writer);
                writer.flush();
                writer.close();
                System.out.println("生成" + fileName + "成功,目录:" + filePath);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RenException(e.getMessage());
            }
        }
    }
    /**
     * 列名转换成Java属性名
     */
    public static String columnToJava(String columnName) {
        return WordUtils.capitalizeFully(columnName, new char[] { '_' }).replace("_", "");
    }
    /**
     * 表名转换成Java类名
     */
    public static String tableToJava(String tableName, String tablePrefix) {
        if (StringUtils.isNotBlank(tablePrefix)) {
            tableName = tableName.replaceFirst(tablePrefix, "");
        }
        return columnToJava(tableName);
    }
    /**
     * 获取文件名
     */
    public static String getFilePath(String template, String filePath, String packageName, String moduleName) {
        if (template.contains("Entity.java.vm")) {
            return filePath + "/src/main/java/" + packageName.replace(".", "/") + "/model";
        }
        if (template.contains("Excel.java.vm")) {
            return filePath + "/src/main/java/" + packageName.replace(".", "/") + "/excel";
        }
        if (template.contains("Dao.java.vm")) {
            return filePath + "/src/main/java/" + packageName.replace(".", "/") + "/dao";
        }
        if (template.contains("Service.java.vm")) {
            return filePath + "/src/main/java/" + packageName.replace(".", "/") + "/service";
        }
        if (template.contains("Controller.java.vm")) {
            return filePath + "/src/main/java/" + packageName.replace(".", "/") + "/controller";
        }
        if (template.contains("Dao.xml.vm")) {
            return filePath + "/src/main/resources/mapper/" + moduleName;
        }
        if (template.contains("index.vue.vm")) {
            return filePath + "/src/views/modules/"
                    + packageName.substring(packageName.lastIndexOf(".") + 1, packageName.length());
        }
        if (template.contains("add-or-update.vue.vm")) {
            return filePath + "/src/views/modules/"
                    + packageName.substring(packageName.lastIndexOf(".") + 1, packageName.length());
        }
        if (template.contains("mysql.vm")) {
            return filePath + "/src/main/java/" + packageName.replace(".", "/") + "/mysql";
        }
        return null;
    }
    private static Map map = new HashMap() {
        {
            put("tinyint".toUpperCase(), "Integer");
            put("smallint".toUpperCase(), "Integer");
            put("mediumint".toUpperCase(), "Integer");
            put("int".toUpperCase(), "Integer");
            put("integer".toUpperCase(), "Integer");
            put("bigint".toUpperCase(), "Long");
            put("float".toUpperCase(), "Float");
            put("double".toUpperCase(), "Double");
            put("decimal".toUpperCase(), "BigDecimal");
            put("char".toUpperCase(), "String");
            put("varchar".toUpperCase(), "String");
            put("tinytext".toUpperCase(), "String");
            put("text".toUpperCase(), "String");
            put("mediumtext".toUpperCase(), "String");
            put("longtext".toUpperCase(), "String");
            put("date".toUpperCase(), "Date");
            put("datetime".toUpperCase(), "Date");
            put("timestamp".toUpperCase(), "Date");
            put("NUMBER", "Integer");
            put("INT".toLowerCase(), "Integer");
            put("INTEGER", "Integer");
            put("BINARY_INTEGER", "Integer");
            put("LONG", "String");
            put("FLOAT", "Float");
            put("BINARY_FLOAT", "Float");
            put("DOUBLE", "Double");
            put("BINARY_DOUBLE", "Double");
            put("DECIMAL", "BigDecimal");
            put("CHAR", "String");
            put("VARCHAR", "String");
            put("VARCHAR2", "String");
            put("NVARCHAR", "String");
            put("NVARCHAR2", "String");
            put("CLOB", "String");
            put("BLOB", "String");
            put("DATE", "Date");
            put("DATETIME", "Date");
            put("TIMESTAMP", "Date");
            put("TIMESTAMP(6);", "Date");
            put("int8", "Long");
            put("int4", "Integer");
            put("int2", "Integer");
            put("numeric", "BigDecimal");
            put("INT16", "Short");
            put("INT32", "Integer");
            put("INT64", "Long");
            put("BIGINT", "Long");
            put("TINYINT UNSIGNED", "Integer");
            put("TINYINT", "Integer");
            put("BIT", "Boolean");
        }
    };
}