/*
 *
 *  *  Copyright 2009-2018.
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *        http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.github.pampas.grpc.runtime;

import javax.tools.*;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by darrenfu on 18-1-31.
 *
 * @author: darrenfu
 * @date: 18-1-31
 */
public class CompileApi {

    private String jars = "";
    private String targetDir = "";


    public static void compile(String filePath) throws Exception {
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
        CompileApi compileApi = new CompileApi();
        File file = new File(filePath);
        String sourceDir = file.exists() && file.isFile() ? file.getParent() : filePath;
        String jarPath = System.getProperty("java.class.requestPath");
        String targetDir = sourceDir;
        boolean compilerResult = compileApi.compiler("UTF-8", jarPath, filePath, sourceDir, targetDir, diagnostics);
        if (compilerResult) {
            System.out.println("编译成功");
        } else {
            System.out.println("编译失败");
            for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
                // System.out.format("%s[line %d column %d]-->%s%n", diagnostic.getKind(), diagnostic.getLineNumber(),
                // diagnostic.getColumnNumber(),
                // diagnostic.getMessage(null));
                System.out.println(diagnostic.getMessage(null));
            }
        }


    }


    /**
     * 判断字符串是否为空 有值为true 空为:false
     */
    public boolean isnull(String str) {
        if (null == str) {
            return false;
        } else if ("".equals(str)) {
            return false;
        } else if (str.equals("null")) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 编译java文件
     *
     * @param encoding    编译编码
     * @param jars        需要加载的jar
     * @param filePath    文件或者目录(若为目录,自动递归编译)
     * @param sourceDir   java源文件存放目录
     * @param targetDir   编译后class类文件存放目录
     * @param diagnostics 存放编译过程中的错误信息
     * @return
     * @throws Exception
     */
    public boolean compiler(String encoding, String jars, String filePath, String sourceDir, String targetDir, DiagnosticCollector<JavaFileObject> diagnostics)
            throws Exception {
        // 获取编译器实例
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // 获取标准文件管理器实例
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        try {
            if (!isnull(filePath) && !isnull(sourceDir) && !isnull(targetDir)) {
                return false;
            }
            // 得到filePath目录下的所有java源文件
            File sourceFile = new File(filePath);
            List<File> sourceFileList = new ArrayList<File>();
            this.targetDir = targetDir;
            getSourceFiles(sourceFile, sourceFileList);
            // 没有java文件,直接返回
            if (sourceFileList.size() == 0) {
                System.out.println(filePath + "目录下查找不到任何java文件");
                return false;
            }
            // 获取要编译的编译单元
            Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFileList);
            /**
             * 编译选项,在编译java文件时,编译程序会自动的去寻找java文件引用的其他的java源文件或者class。 -sourcepath选项就是定义java源文件的查找目录, -classpath选项就是定义class文件的查找目录。
             */
            Iterable<String> options = Arrays.asList("-encoding", encoding, "-classpath", jars, "-d", targetDir, "-sourcepath", sourceDir);
            JavaCompiler.CompilationTask compilationTask = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
            // 运行编译任务
            return compilationTask.call();
        } finally {
            fileManager.close();
        }
    }

    /**
     * 查找该目录下的所有的java文件
     *
     * @param sourceFile
     * @param sourceFileList
     * @throws Exception
     */
    private void getSourceFiles(File sourceFile, List<File> sourceFileList) throws Exception {
        if (sourceFile.exists() && sourceFileList != null) {//文件或者目录必须存在
            if (sourceFile.isDirectory()) {// 若file对象为目录
                // 得到该目录下以.java结尾的文件或者目录
                File[] childrenFiles = sourceFile.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        if (pathname.isDirectory()) {
                            return true;
                        } else {
                            String name = pathname.getName();
                            if (name.endsWith(".java") ? true : false) {
                                return true;
                            }

                            return false;
                        }
                    }
                });
                // 递归调用
                for (File childFile : childrenFiles) {
                    getSourceFiles(childFile, sourceFileList);
                }
            } else {// 若file对象为文件
                sourceFileList.add(sourceFile);
            }
        }
    }

    /**
     * 查找该目录下的所有的jar文件
     *
     * @param jarPath
     * @throws Exception
     */
    private String getJarFiles(String jarPath) throws Exception {
        File sourceFile = new File(jarPath);
        // String jars="";
        if (sourceFile.exists()) {// 文件或者目录必须存在
            if (sourceFile.isDirectory()) {// 若file对象为目录
                // 得到该目录下以.java结尾的文件或者目录
                File[] childrenFiles = sourceFile.listFiles(new FileFilter() {
                    public boolean accept(File pathname) {
                        if (pathname.isDirectory()) {
                            return true;
                        } else {
                            String name = pathname.getName();
                            if (name.endsWith(".jar") ? true : false) {
                                jars = jars + pathname.getPath() + File.separatorChar;
                                return true;
                            }
                            return false;
                        }
                    }
                });
            }
        }
        return jars;
    }

}