package net.northking.cctp.maven.gen;


import net.northking.cctp.maven.analyse.Messages;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;

/**
 * 基于Apache Velocity 的代码生成器抽象类
 *
 * @author : 谢秀松
 * @since : 1.0
 */
public abstract class VMGenerator
{
  /**
   * 配置文件包名路径
   */
  private static final String NK_PLUGIN = "config.nkplugin";

  /**
   * 保留代码开始标志
   */
  private static final String HOLD_CODE_START = "THE CODE EXTENDED BY DEVELOPER START";

  /**
   * 保留代码结束标志
   */
  private static final String HOLD_CODE_END = "THE CODE EXTENDED BY DEVELOPER END";

  /**
   * KEY-保留代码
   */
  protected static final String HOLD_CODE = "holdCode";

  /**
   * KEY-导入依赖
   */
  protected static final String IMPORT_LIBS = "importLibs";

  /**
   * 加载参数配置文件
   */
  private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(NK_PLUGIN);

  /**
   * 生成文件
   *
   * @param dataMap     数据
   * @param vmTemplate  模板
   * @param filePath    文件路径
   * @param fileName    文件名称
   * @param isOverWrite 是否覆盖
   * @throws Exception 异常
   */
  protected final void genFile(final Map<String, Object> dataMap, final String vmTemplate, final String filePath,
                               final String fileName, final boolean isOverWrite) throws Exception
  {
    File file = this.createFile(filePath, fileName);

    Messages.info("Print.GenFileStart " + file.getAbsolutePath());
    boolean isExists = file.exists(); // 文件是否存在
    if (isExists)
    { // 判断文件是否存在，是否覆蓋
    }

    // 加载VM模板
    Template javaVMTemplate = loadTemplate(vmTemplate);
    // VM模板合成生成文件
    genFileByVmTemplate(dataMap, file, javaVMTemplate);
    if (isExists)
    {
      Messages.info("print.CoverFile", file.getAbsolutePath());
    } else
    {
      Messages.info("Print.CreateFile", file.getAbsolutePath());
    }
  }

  /**
   * 获取用户手写代码
   *
   * @param file 代码文件
   * @return 保留内容
   * @throws IOException 异常
   */
  protected final Map<String, String> readHoldCode(final File file) throws IOException
  {
    String lineSeparator = System.getProperty("line.separator");
    Map<String, String> holdCodeMap = new HashMap<String, String>();
    StringBuffer holdCode = new StringBuffer();
    StringBuffer importLibs = new StringBuffer();
    BufferedReader reader = null;
    boolean holdCodeFlag = false;
    boolean hasHoldCodeTag = false;
    try
    {
      reader = new BufferedReader(new FileReader(file));
      String line;
      while ((line = reader.readLine()) != null)
      {
        if (line.toUpperCase().contains(HOLD_CODE_START))
        {
          holdCodeFlag = true;
          hasHoldCodeTag = true;
        } else if (line.toUpperCase().contains(HOLD_CODE_END))
        {
          holdCodeFlag = false;
        } else
        {
          if (holdCodeFlag && line.length() > 0)
          {
            holdCode.append(line);
            holdCode.append(lineSeparator);
          } else if (line.startsWith("import"))
          {
            importLibs.append(line);
            importLibs.append(lineSeparator);
          }
        }
      }
    } catch (Exception e)
    {
      throw e;
    } finally
    {
      try
      {
        if (reader != null)
        {
          reader.close();
        }
      } catch (IOException e)
      {
      }
    }

    if (hasHoldCodeTag)
    {
      holdCodeMap.put(HOLD_CODE, holdCode.toString());
      holdCodeMap.put(IMPORT_LIBS, importLibs.toString());
    } else
    {
      return null;
    }
    return holdCodeMap;
  }

  /**
   * 基于Velocity模板生成文件
   *
   * @param dataMap  数据
   * @param file     文件
   * @param template 模板
   * @throws Exception 异常
   */
  protected final void genFileByVmTemplate(final Map<String, Object> dataMap, final File file, final Template template) throws Exception
  {
    VelocityContext context = this.createVmContext();
    for (Map.Entry<String, Object> entry : dataMap.entrySet())
    {
      context.put(entry.getKey(), entry.getValue());
    }
    OutputStream os = createOutputStream(file);
    write(template, context, os);
  }

  /**
   * 加载模板
   *
   * @param templateFile 模板文件
   * @return 模板
   */
  protected final Template loadTemplate(final String templateFile)
  {
    VelocityEngine ve = new VelocityEngine();
    ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
    ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
    ve.setProperty(Velocity.ENCODING_DEFAULT, "utf-8");
    ve.setProperty(Velocity.INPUT_ENCODING, "utf-8");
    ve.setProperty(Velocity.OUTPUT_ENCODING, "utf-8");
    ve.init();
    Template template;
    try
    {
      template = ve.getTemplate(templateFile);
    } catch (ResourceNotFoundException rnfe)
    {
      Messages.error("Error.Template1", templateFile);
      throw rnfe;
    } catch (ParseErrorException pee)
    {
      Messages.error("Error.Template2", templateFile);
      throw pee;
    }
    return template;
  }

  /**
   * 创建输出流
   *
   * @param file 文件
   * @return 输出流
   * @throws IOException IO异常
   */
  protected final OutputStream createOutputStream(final File file) throws IOException
  {
    return new FileOutputStream(file);
  }

  /**
   * 创建文件
   *
   * @param filePath 文件路径
   * @param fileName 文件名称
   * @return 文件夹
   * @throws Exception 异常
   */
  protected final File createFile(final String filePath, final String fileName) throws Exception
  {
    File pg = new File(filePath);
    if (pg.exists() && pg.isDirectory())
    {
      Messages.info("Print.DirExists", pg.getAbsolutePath());
    } else
    {
      boolean flag = pg.mkdirs();
      if (!flag)
      {
        throw new Exception(Messages.getMessage("Error.CreateFileDir", pg.getParent()));
      }
    }

    String fName = filePath + "/" + fileName;
    fName = fName.replace("\\", "/").replace("//", "/");
    return new File(fName);
  }

  /**
   * 写文件
   *
   * @param t    模板
   * @param vctx 上下文根
   * @param os   输出流
   * @throws Exception 异常
   */
  protected final void write(final Template t, final VelocityContext vctx, final OutputStream os) throws Exception
  {
    if (t == null || vctx == null || os == null)
    {
      throw new NullPointerException();
    }
    BufferedWriter writer = null;
    OutputStreamWriter outputStreamWriter = null;
    try
    {
      outputStreamWriter = new OutputStreamWriter(os, "utf-8");
      if (outputStreamWriter != null)
      {
        writer = new BufferedWriter(outputStreamWriter);
      }
      if (writer != null)
      {
        t.merge(vctx, writer);
      }
    } catch (Exception e)
    {
      throw e;
    } finally
    {
      if (writer != null)
      {
        writer.close();
      }
    }
  }

  /**
   * 创建模板上下文根
   *
   * @return 模板上下文根
   */
  private VelocityContext createVmContext()
  {
    VelocityContext context = new VelocityContext();
    // 引入版本、声明 等等
    for (String key : RESOURCE_BUNDLE.keySet())
    {
      context.put(key, RESOURCE_BUNDLE.getString(key));
    }
    SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    context.put("now", fmt.format(new Date()));
    return context;
  }

}

