package com.seezoon.generator.io;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.springframework.boot.system.ApplicationHome;

import com.seezoon.generator.constants.CodeTemplate;
import com.seezoon.generator.plan.TablePlan;

import lombok.extern.slf4j.Slf4j;

/**
 * 文件的方式生成
 *
 * @author hdf
 */
@Slf4j
public class FileCodeGenerator implements CodeGenerator {

    public static final String TARGET = "target";
    public static final String CLASSES = "classes";
    public static final String GENERATED_SOURCES_FOLDER = "seezoon-generated";

    private static final String ZIP_NAME = "all-sources-in-one.zip";

    @Override
    public void generate(TablePlan... tablePlans) throws IOException {
        // 获取根目录
        Path generatedFolderPath = this.getReadyGeneratedSourcesFolder();
        for (TablePlan tablePlan : tablePlans) {
            Arrays.stream(CodeTemplate.values()).forEach((ct) -> {
                this.createSourceFile(tablePlan, generatedFolderPath, ct);
            });
        }
        zipSourceFiles(generatedFolderPath);
    }

    private void zipSourceFiles(Path generatedFolderPath) throws IOException {
        try (OutputStream fos = Files.newOutputStream(generatedFolderPath.resolve(ZIP_NAME));
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            ZipOutputStream zipOutputStream = new ZipOutputStream(bos)) {
            // 生成zip包
            Files.walk(generatedFolderPath).filter(p -> !Files.isDirectory(p) && !p.endsWith(ZIP_NAME)).forEach((f) -> {
                try (InputStream fis = Files.newInputStream(f);
                    BufferedInputStream bins = new BufferedInputStream(fis)) {
                    String relativePath = generatedFolderPath.relativize(f).toString();
                    zipOutputStream.putNextEntry(new ZipEntry(relativePath));
                    // 写入文件
                    byte[] buff = new byte[(int)FileUtils.ONE_KB * 8];
                    int len = 0;
                    while (-1 != (len = bins.read(buff))) {
                        zipOutputStream.write(buff, 0, len);
                    }
                    zipOutputStream.closeEntry();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }

    private void createSourceFile(TablePlan tablePlan, Path generatedFolderPath, CodeTemplate ct) {
        try {
            // 得到生成文件的完成目录
            Path fileParentPath =
                generatedFolderPath.resolve(FreeMarkerRender.renderStringTemplate(ct.path(), tablePlan));
            if (!Files.exists(fileParentPath)) {
                Files.createDirectories(fileParentPath);
            }
            // 得到生成文件路径
            Path filePath = fileParentPath.resolve(FreeMarkerRender.renderStringTemplate(ct.fileName(), tablePlan));
            Files.createFile(filePath);
            String content = FreeMarkerRender.renderTemplate(ct.tplName(), tablePlan);
            Files.writeString(filePath, content);
            log.info("success generated source files {}", filePath.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 开发环境则为target,jar环境则为jar所在目录
     *
     * @return
     */
    public Path getReadyGeneratedSourcesFolder() throws IOException {
        ApplicationHome applicationHome = new ApplicationHome(FileCodeGenerator.class);
        // 工程路径或者jar所在目录
        Path usrDir = applicationHome.getDir().toPath();
        log.info("generator user dir:{}", usrDir);
        // 在jar 中不为null，不在jar中dir 则为项目目录
        Path generatedFolderPath = usrDir;
        if (null != applicationHome.getSource()) {
            // main idea main 方法运行
            if (generatedFolderPath.endsWith(TARGET + File.separator + CLASSES)) {
                generatedFolderPath = generatedFolderPath.resolveSibling(GENERATED_SOURCES_FOLDER);
            } else {
                // 独立jar运行
                generatedFolderPath = generatedFolderPath.resolve(GENERATED_SOURCES_FOLDER);
            }
        } else {
            // junit test 运行
            generatedFolderPath = generatedFolderPath.resolve(TARGET + File.separator + GENERATED_SOURCES_FOLDER);
        }
        this.initGeneratedFolder(generatedFolderPath);
        return generatedFolderPath;
    }

    private void initGeneratedFolder(Path generatedFolderPath) throws IOException {
        if (Files.exists(generatedFolderPath)) {
            FileUtils.deleteDirectory(generatedFolderPath.toFile());
        }
        Files.createDirectories(generatedFolderPath);
    }
}
