package com.cftech.core.mybatis.mybatis;

import com.cftech.core.util.Constants;
import com.cftech.core.util.DateUtils;
import com.cftech.core.util.StringUtils;

import java.util.ArrayList;
import java.util.List;


/**
 * Created by lisw on 2016-08-05
 */
public class MapperGenerator extends AbstractMybatisGenerator {

	/**
	 * 是否使用缓存
	 */
	private Boolean withCache = false;

	@Override
	public void outputJavaCode(TableBean tableBean) {
		if (tableBean != null) {
			String rootPath = "";
			if (StringUtils.isBlank(rootPath)) {
				rootPath = System.getProperty("user.dir");
			}
			if (!rootPath.endsWith("/src/main/java") || !rootPath.endsWith("src/main/java/")) {
				if (rootPath.endsWith("/")) {
					rootPath += "src/main/java/";
				} else {
					rootPath += "/src/main/java/";
				}
			}
			String basePackage = tableBean.getBasePackage();

			String filePath = rootPath + basePackage.replace(pointStr, "/") + "/";
			String entityName = tableBean.getEntityName();
			String daoFilePath = filePath + "dao/" + entityName + "Mapper.java";
			String mapperXmlFilePath = filePath + "dao/" + entityName + "Mapper.xml";
			String modelPackage = tableBean.getPackageName();
			outputJavaFile(daoFilePath, generateMapper(basePackage, modelPackage, entityName).toString());
			outputJavaFile(mapperXmlFilePath, generateMapperXml(tableBean).toString());
		}
	}

	/**
	 * generateMapper
	 *
	 * @param basePackage
	 * @param modelPackage
	 * @param entityName
	 * @return StringBuffer
	 */
	private StringBuffer generateMapper(String basePackage, String modelPackage, String entityName) {
		StringBuffer sb = new StringBuffer();
		String clazzSimpleName = entityName;
		String clazzNameLower = clazzSimpleName.substring(0, 1).toLowerCase() + clazzSimpleName.substring(1);

		String suffix = "dao";
		String suffixUpper = "Mapper";
		sb.append("package ").append(basePackage).append(pointStr).append(suffix).append(semicolon).append(enter);
		sb.append(enter);
		sb.append("import GenericDao;").append(enter);
		sb.append("import ").append(modelPackage).append(pointStr).append(clazzSimpleName).append(semicolon).append(enter);
		sb.append(enter);
		sb.append("/**").append(enter);
		sb.append(" * ").append(clazzNameLower).append(suffixUpper).append(enter);
		// sb.append(" * @author ice").append(enter);
		sb.append(" * Created by " + this.authorName + " " + DateUtils.getDate("dd/MM/yyyy") + ".").append(enter);
		sb.append(" */").append(enter);
		sb.append("public interface ").append(clazzSimpleName).append(suffixUpper);
		sb.append(" extends GenericDao<").append(clazzSimpleName).append("> {").append(enter);
		sb.append(enter);
		sb.append("}").append(enter);
		return sb;
	}

	/**
	 * generateMapperXml
	 * @param tableBean
	 * @return StringBuffer
	 */
	private StringBuffer generateMapperXml(TableBean tableBean) {
		String basePackage = tableBean.getBasePackage();
		String entityName = tableBean.getEntityName();
		String tableName = tableBean.getTableName();

		String[] columnNames = tableBean.getColumnNames();
		List<String> attributeNames = new ArrayList<>();
		int len = columnNames.length;
		for (int i = 0; i < len; i++) {
			String columnName = columnNames[i];
			String attributeName = StringUtils.camelName(columnName);
			attributeNames.add(attributeName);
		}

		StringBuffer sb = new StringBuffer();
		sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append(enter);
		sb.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">");
		sb.append(enter);
		sb.append("<mapper namespace=\"").append(basePackage).append(".dao.").append(entityName).append("Mapper\">").append(enter);
		// sb.append(tab).append("<cache flushInterval=\"300000\" size=\"512\"/>").append(enter);
		if (this.withCache) {
			sb.append(tab).append("<cache flushInterval=\"300000\" size=\"512\" type=\"org.mybatis.caches.ehcache.LoggingEhcache\" />").append(enter);
		}
		sb.append(enter);

		sb.append(this.generateResultMap(tableBean, attributeNames)).append(enter);

		sb.append(this.generateSqlWhere()).append(enter);

		sb.append(this.generateSqlColumns(tableBean)).append(enter);

		sb.append(this.generateInsert(tableBean, attributeNames)).append(enter);

		sb.append(this.generateFetchById(tableName)).append(enter);

		sb.append(this.generateCount(tableName)).append(enter);

		sb.append(this.generateFetchByPage(tableName)).append(enter);

		sb.append(this.generateUpdate(tableBean, attributeNames)).append(enter);

		sb.append(this.generateDelete(tableName)).append(enter);

		sb.append("</mapper>");
		return sb;
	}

	/**
	 * generateResultMap
	 * @param tableBean
	 * @param attributeNames
	 * @return resultMap str
	 */
	private StringBuffer generateResultMap(TableBean tableBean, List<String> attributeNames) {
		String[] columnNames = tableBean.getColumnNames();
		int len = columnNames.length;

		String entityName = tableBean.getEntityName();
		String modelPackage = tableBean.getPackageName();

		StringBuffer sb = new StringBuffer();
		sb.append(tab).append("<resultMap id=\"resultMap\" type=\"").append(modelPackage).append(pointStr).append(entityName).append("\">").append(enter);
		for (int i = 0; i < len; i++) {
			String columnName = columnNames[i];
			String attributeName = attributeNames.get(i);
			if (i == 0) {
				sb.append(tab).append(tab).append("<id column=\"").append(columnName).append("\" property=\"").append(attributeName).append("\"/>")
						.append(enter);
			} else {
				sb.append(tab).append(tab).append("<result column=\"").append(columnName).append("\" property=\"").append(attributeName).append("\"/>")
						.append(enter);
			}
		}
		sb.append(tab).append("</resultMap>").append(enter);
		return sb;
	}

	/**
	 * sqlWhere
	 * @return sqlWhere str
	 */
	private StringBuffer generateSqlWhere() {
		StringBuffer sb = new StringBuffer();

		sb.append(tab).append("<sql id=\"sqlWhere\">").append(enter);
		sb.append(tab).append(tab).append("<if test=\"conds!=null\">").append(enter);
		sb.append(tab).append(tab).append(tab).append("<trim prefix=\"WHERE\" prefixOverrides=\"AND|OR\">").append(enter);

		sb.append(tab).append(tab).append(tab).append(tab);
		sb.append("<foreach collection=\"conds.conds\" index=\"index\" item=\"cond\">");
		sb.append(enter);

		sb.append(tab).append(tab).append(tab).append(tab).append(tab).append("${cond.linkType}").append(enter);

		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'EQUAL'\">${cond.param} = #{cond.value}</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'NOTEQUAL'\">${cond.param} &lt;&gt; #{cond.value}</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'GREATEQUAL'\">${cond.param} &gt;= #{cond.value}</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'GREATTHAN'\">${cond.param} &gt; #{cond.value}</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'LESSEQUAL'\">${cond.param} &lt;= #{cond.value}</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'LESSTHAN'\">${cond.param} &lt; #{cond.value}</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'BETWEEN'\">${cond.param} BETWEEN #{cond.startValue} AND #{cond.endValue}</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'ISNULL'\">${cond.param} IS NULL</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'NOTNULL'\">${cond.param} IS NOT NULL</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'LIKE'\">${cond.param} LIKE #{cond.value}</if>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<if test=\"cond.condType == 'IN'\">${cond.param} IN");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("<foreach item=\"item\" index=\"index\" collection=\"cond.value\" open=\"(\" separator=\",\" close=\")\">");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("#{item}");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("</foreach>");
		sb.append(enter);
		sb.append(tab).append(tab).append(tab).append(tab).append(tab);
		sb.append("</if>");
		sb.append(enter);

		sb.append(tab).append(tab).append(tab).append(tab).append("</foreach>").append(enter);
		sb.append(tab).append(tab).append(tab).append("</trim>").append(enter);
		sb.append(tab).append(tab).append("</if>").append(enter);
		sb.append(tab).append("</sql>").append(enter);
		return sb;
	}

	/**
	 * sqlColumns
	 * @param tableBean
	 * @return sqlColumns str
	 */
	private StringBuffer generateSqlColumns(TableBean tableBean) {
		String[] columnNames = tableBean.getColumnNames();
		StringBuffer sb = new StringBuffer();
		sb.append(tab).append("<sql id=\"sqlColumns\">").append(enter);
		sb.append(tab).append(tab).append(StringUtils.join(columnNames, "," + blank)).append(enter);
	//sb.append(tab).append(tab).append(StringUtils.join(columnNames,  blank)).append(enter);
		sb.append(tab).append("</sql>").append(enter);
		return sb;
	}

	/**
	 * generateInsert
	 * @param tableBean
	 * @param attributeNames
	 * @return insert str
	 */
	private StringBuffer generateInsert(TableBean tableBean, List<String> attributeNames) {
		String[] columnNames = tableBean.getColumnNames();
		int len = columnNames.length;

		String entityName = tableBean.getEntityName();
		String modelPackage = tableBean.getPackageName();
		String tableName = tableBean.getTableName();
		String[] columnTypes = tableBean.getColumnTypes();
		List<String> columnTypeList = new ArrayList<>();

		List<String> attributeList = new ArrayList<>();
		attributeList.addAll(attributeNames);
		String autoIncrementColumn = tableBean.getAutoIncrementColumn();

		StringBuffer sb = new StringBuffer();
		if (StringUtils.isBlank(autoIncrementColumn)) {
			sb.append(tab).append("<insert id=\"save\" parameterType=\"").append(modelPackage).append(pointStr).append(entityName)
					.append("\" keyProperty=\"id\">").append(enter);
		} else {
			sb.append(tab).append("<insert id=\"save\" parameterType=\"").append(modelPackage).append(pointStr).append(entityName);
			//useGeneratedKeys保存之后，返回主键ID
			sb.append("\" useGeneratedKeys=\"true\" keyProperty=\"").append(autoIncrementColumn).append("\">").append(enter);
			//sb.append("\"   keyProperty=\"").append(autoIncrementColumn).append("\">").append(enter);
			
		}
		sb.append(tab).append(tab).append("insert into ").append(tableName).append(enter);
		sb.append(tab).append(tab).append("(").append(enter);
		if (StringUtils.isBlank(autoIncrementColumn)) {
			sb.append(tab).append(tab).append("<include refid=\"sqlColumns\"/>").append(enter);

			for (int i = 0; i < len; i++) {
				columnTypeList.add(columnTypes[i]);
			}
		} else {
			List<String> columnList = new ArrayList<>();
			for (int i = 0; i < len; i++) {
				String columnName = columnNames[i];
				/*if (!StringUtils.equals(columnName, autoIncrementColumn)) {*/ //此处if-else为去除主键ID,不生成
					columnList.add(columnName);
					columnTypeList.add(columnTypes[i]);
//				} else {
//					attributeList.remove(i);
//				}
			}
			//len--;
			//sb.append(tab).append(tab).append(StringUtils.join(columnList, blank)).append(enter);
			sb.append(tab).append(tab).append(StringUtils.join(columnList, Constants.VALUE_SIMPLE_SPLIT_CHAR + blank)).append(enter);
		}
		sb.append(tab).append(tab).append(")").append(enter);
		sb.append(tab).append(tab).append("values").append(enter);
		sb.append(tab).append(tab).append("(").append(enter);
		sb.append(tab).append(tab);
		for (int i = 0; i < len; i++) {
			if (i > 0) {
				sb.append(Constants.VALUE_SIMPLE_SPLIT_CHAR).append(blank);
				sb.append(blank);
			}
			if ((i > 0) && (i % 4 == 0)) {
				sb.append(enter).append(tab).append(tab);
			}
			String columnTypeStr = columnTypeList.get(i);
			String columnType = columnTypeStr.toUpperCase();
			if (StringUtils.contains(columnTypeStr, blank)) {
				columnType = columnTypeStr.substring(0, columnTypeStr.indexOf(blank)).toUpperCase();
			}
			if(attributeList.get(i).equalsIgnoreCase("createtime") || attributeList.get(i).equalsIgnoreCase("updatetime")){
				sb.append("now()");
			}else{
				sb.append("#{").append(attributeList.get(i)).append(", jdbcType=").append(this.transType(columnType)).append("}");
			}
		}
		sb.append(enter);
		sb.append(tab).append(tab).append(")").append(enter);
		sb.append(tab).append("</insert>").append(enter);
		return sb;
	}

	/**
	 * fetchById
	 * @param tableName
	 * @return fetchById str
	 */
	private StringBuffer generateFetchById(String tableName) {
		StringBuffer sb = new StringBuffer();
		sb.append(tab).append("<select id=\"fetchById\" parameterType=\"java.lang.Long\" resultMap=\"resultMap\">").append(enter);
		sb.append(tab).append(tab).append("SELECT");
		sb.append(" <include refid=\"sqlColumns\" />");
		sb.append(" FROM ").append(tableName).append(" t").append(enter);
		sb.append(tab).append(tab).append("WHERE t.id=#{id}").append(enter);
		sb.append(tab).append("</select>").append(enter);
		return sb;
	}

	/**
	 * count
	 * @param tableName
	 * @return count str
	 */
	private StringBuffer generateCount(String tableName) {
		StringBuffer sb = new StringBuffer();
		sb.append(tab).append("<select id=\"count\" parameterType=\"java.util.Map\" resultType=\"java.lang.Integer\">").append(enter);
		sb.append(tab).append(tab).append("SELECT COUNT(1)  FROM ").append(tableName);
		sb.append("<include refid=\"sqlWhere\" />").append(enter);
		sb.append(tab).append("</select>").append(enter);
		return sb;
	}

	/**
	 * fetchByPage
	 * @param tableName
	 * @return fetchByPage str
	 */
	private StringBuffer generateFetchByPage(String tableName) {
		StringBuffer sb = new StringBuffer();
		sb.append(tab).append("<select id=\"fetchSearchByPage\" parameterType=\"java.util.Map\" resultMap=\"resultMap\">").append(enter);
		sb.append(tab).append(tab).append("SELECT <include refid=\"sqlColumns\" /> ").append(enter);
		sb.append(tab).append(tab).append("FROM ").append(tableName).append(" t").append(enter);
		sb.append(tab).append(tab).append("<include refid=\"sqlWhere\" />").append(enter);
		sb.append(tab).append(tab).append("<if test=\"sort!=null\">").append("ORDER BY ${sort.param} ${sort.type}").append("</if>").append(enter);
		sb.append(tab).append(tab).append("<if test=\"limit>0\">").append("limit #{offset},#{limit}").append("</if>").append(enter);
		sb.append(tab).append("</select>").append(enter);
		return sb;
	}

	/**
	 * update
	 * @param tableBean
	 * @param attributeNames
	 * @return update str
	 */
	private StringBuffer generateUpdate(TableBean tableBean, List<String> attributeNames) {
		String[] columnNames = tableBean.getColumnNames();
		int len = columnNames.length;
		String entityName = tableBean.getEntityName();
		String modelPackage = tableBean.getPackageName();
		String tableName = tableBean.getTableName();
		String[] columnTypes = tableBean.getColumnTypes();

		StringBuffer sb = new StringBuffer();
		sb.append(tab).append("<update id=\"update\" parameterType=\"").append(modelPackage).append(pointStr).append(entityName).append("\">").append(enter);
		sb.append(tab).append(tab).append("update ").append(tableName).append(enter);
		sb.append(tab).append(tab).append("<set>").append(enter);

		for (int i = 0; i < len; i++) {
			String columnTypeStr = columnTypes[i];
			String columnType = columnTypeStr.toUpperCase();
			if (StringUtils.contains(columnTypeStr, blank)) {
				columnType = columnTypeStr.substring(0, columnTypeStr.indexOf(blank)).toUpperCase();
			}
			// sb.append("#{").append(attributeNames.get(i)).append(", jdbcType=").append(columnType).append("}");
			String attributeName = attributeNames.get(i);
			sb.append(tab).append(tab).append(tab).append("<if test=\"").append(attributeName).append(" != null\">").append(enter);
			if(attributeName.equalsIgnoreCase("updatetime")){
				sb.append(tab).append(tab).append(tab).append(tab).append(columnNames[i]).append("=now(),").append(enter);
			}else{
				sb.append(tab).append(tab).append(tab).append(tab).append(columnNames[i]).append(" = #{");
				sb.append(attributeName).append(", jdbcType=").append(this.transType(columnType)).append("},").append(enter);
			}
			sb.append(tab).append(tab).append(tab).append("</if>").append(enter);
		}
		sb.append(tab).append(tab).append("</set>").append(enter);
		sb.append(tab).append(tab).append("where id=#{id,jdbcType=BIGINT}").append(enter);
		sb.append(tab).append("</update>").append(enter);// transType
		return sb;
	}

	/**
	 * delete
	 * @param tableName
	 * @return delete str
	 */
	private StringBuffer generateDelete(String tableName) {
		StringBuffer sb = new StringBuffer();
		sb.append(tab).append("<update id=\"delete\" parameterType=\"java.lang.Long\">").append(enter);
		sb.append(tab).append(tab).append("update ").append(tableName).append(" set delflag=1 where id=#{id,jdbcType=BIGINT}").append(enter);
		sb.append(tab).append("</update>").append(enter);
		return sb;
	}

	/**
	 * transType
	 * @param type
	 * @return sqlType
	 */
	private String transType(String type) {
		if (StringUtils.equalsIgnoreCase("INT", type)) {
			return "INTEGER";
		}
		if (StringUtils.equalsIgnoreCase("MEDIUMINT", type)) {
			return "INTEGER";
		}
		if (StringUtils.equalsIgnoreCase("LONGTEXT", type)) {
			return "LONGVARCHAR";
		}
		if (StringUtils.equalsIgnoreCase("DATETIME", type)) {
			return "TIMESTAMP";
		}
		return type;
	}

	public void setWithCache(Boolean withCache) {
		this.withCache = withCache;
	}

}
