/*
 * Copyright 2016-2017 the original author or authors.
 *
 * 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.blindpirate.gogradle.util;


import com.google.common.collect.Lists;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;

import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Singleton
public class ProcessUtils {
    private static final Logger LOGGER = Logging.getLogger(ProcessUtils.class);

    public static class ProcessResult {
        private int code;
        private String stdout;
        private String stderr;

        public int getCode() {
            return code;
        }

        public String getStdout() {
            return stdout;
        }

        public String getStderr() {
            return stderr;
        }

        public ProcessResult(Process process) throws InterruptedException {
            code = process.waitFor();
            stdout = IOUtils.toString(process.getInputStream());
            stderr = IOUtils.toString(process.getErrorStream());
        }
    }

    public String getStdout(Process process) {
        String ret = getResult(process).getStdout();
        LOGGER.debug("Process stdout: {}", ret);
        return ret;
    }

    public String runAndGetStdout(String... args) {
        return getStdout(run(args));
    }

    public String runAndGetStderr(String... args) {
        return getResult(run(args)).stderr;
    }

    public String runAndGetStdout(File workingDir, String... args) {
        return getStdout(run(Arrays.asList(args), null, workingDir));
    }

    public Process run(String... args) {
        return run(Arrays.asList(args));
    }

    public Process run(List<String> args) {
        return run(args, null, null);
    }

    public ProcessResult getResult(Process process) {
        try {
            return new ProcessResult(process);
        } catch (InterruptedException e) {
            throw ExceptionHandler.uncheckException(e);
        }
    }

    public Process run(List<String> args, Map<String, String> envs, File workingDirectory) {
        LOGGER.debug("Forking process: args {}, envs {}, workingDir {}", args, envs, workingDirectory);
        try {
            ProcessBuilder pb = new ProcessBuilder().command(args);
            if (envs != null) {
                pb.environment().putAll(envs);
            }
            if (workingDirectory != null) {
                pb.directory(workingDirectory);
            }
            return pb.start();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    // this should be moved to test source set since it's only used in test
    public ProcessResult runProcessWithCurrentClasspath(Class mainClass,
                                                        List<String> args,
                                                        Map<String, String> envs) {
        String currentClasspath = System.getProperty("java.class.path");

        List<String> cmds = Lists.newArrayList("java", "-cp", currentClasspath, mainClass.getName());
        cmds.addAll(args);
        return getResult(run(cmds, envs, null));
    }
}

@@ -975,16 +975,222 @@
 til.Map;
+%0Aimport java.util.concurrent.Callable;%0Aimport java.util.concurrent.ExecutionException;%0Aimport java.util.concurrent.ExecutorService;%0Aimport java.util.concurrent.Executors;%0Aimport java.util.concurrent.Future;
 %0A%0A@Singl
@@ -1343,16 +1343,101 @@
 esult %7B%0A
+        private static ExecutorService threadPool = Executors.newCachedThreadPool();%0A
         
@@ -1737,27 +1737,216 @@
 %7D%0A%0A        p
-ubl
+rivate ProcessResult(int code, String stdout, String stderr) %7B%0A            this.code = code;%0A            this.stdout = stdout;%0A            this.stderr = stderr;%0A        %7D%0A%0A        private stat
 ic ProcessRe
@@ -1945,24 +1945,38 @@
 rocessResult
+ waitForFinish
 (Process pro
@@ -2012,37 +2012,111 @@
 tion
- %7B%0A            code =
+, ExecutionException %7B%0A            Future%3CInteger%3E code = threadPool.submit((Callable%3CInteger%3E)
  process
 .wai
@@ -2115,17 +2115,17 @@
 cess
-.
+::
 waitFor
-(
 );%0A 
@@ -2135,24 +2135,63 @@
         
-stdout =
+Future%3CString%3E stdout = threadPool.submit(() -%3E
  IOUtils
@@ -2217,32 +2217,33 @@
 etInputStream())
+)
 ;%0A            st
@@ -2244,56 +2244,174 @@
     
-stderr = IOUtils.toString(process.getErrorStream
+Future%3CString%3E stderr = threadPool.submit(() -%3E IOUtils.toString(process.getErrorStream()));%0A            return new ProcessResult(code.get(), stdout.get(), stderr.get
 ());
@@ -2420,32 +2420,33 @@
        %7D%0A    %7D%0A%0A
+%0A
     public Strin
@@ -3207,36 +3207,32 @@
          return 
-new 
 ProcessResult(pr
@@ -3224,24 +3224,38 @@
 rocessResult
+.waitForFinish
 (process);%0A 
@@ -3283,32 +3283,53 @@
 ruptedException 
+%7C ExecutionException 
 e) %7B%0A           

Fix ProcessUtils deadlock issue (#243)