package com.jonaslasauskas.gradle.plugin;

import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.io.CharStreams.copy;
import static com.google.common.truth.Truth.assertThat;
import static java.text.MessageFormat.format;
import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.SECONDS;

import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import com.google.common.base.Joiner;



public final class ExecutableJar {
  
  public static ExecutableJar at(File jarFile) {
    File directory = jarFile.getParentFile();
    assertThat(directory.list()).asList().named("Files at %s", directory).contains(jarFile.getName());
    
    return new ExecutableJar(jarFile);
  }
  
  
  private final String jarFilePath;
  
  private final List<String> systemProperties;
  
  
  public ExecutableJar(File jarFile) {
    jarFilePath = jarFile.getAbsolutePath();
    systemProperties = new ArrayList<>();
  }
  
  public ExecutableJar withSystemProperty(String property) {
    systemProperties.add("-D" + property);
    
    return this;
  }
  
  public ExecutableJar withSystemProperty(String property, String value) {
    systemProperties.add("-D" + property + "=" + value);
    
    return this;
  }
  
  public Execution run() throws IOException {
    return new Execution(new ProcessBuilder(jarCommand()));
  }
  
  public Execution runWithArguments(String... arguments) {
    List<String> command = jarCommand();
    command.addAll(asList(arguments));
    
    return new Execution(new ProcessBuilder(command));
  }
  
  private List<String> jarCommand() {
    List<String> command = new ArrayList<>();
    command.add("java");
    command.addAll(systemProperties);
    command.add("-jar");
    command.add(jarFilePath);
    return command;
  }
  
  
  public static final class Execution {
    
    public final Iterable<String> command;
    
    public final int exitCode;
    
    public final String output;
    
    public final String error;
    
    
    public Execution(ProcessBuilder processBuilder) {
      command = processBuilder.command();
      
      try {
        Process process = processBuilder.start();
        
        try (
            Reader outputReader = new InputStreamReader(process.getInputStream(), UTF_8);
            Reader errorReader = new InputStreamReader(process.getErrorStream(), UTF_8)) {
          
          ensureCompletesIn(process, 42, SECONDS);
          
          StringBuilder outputBuilder = new StringBuilder();
          copy(outputReader, outputBuilder);
          
          StringBuilder errorBuilder = new StringBuilder();
          copy(errorReader, errorBuilder);
          
          exitCode = process.exitValue();
          output = outputBuilder.toString();
          error = errorBuilder.toString();
        } catch (InterruptedException e) {
          throw new RuntimeException("Execution failed.", e);
        }
      } catch (IOException e) {
        throw new RuntimeException("Execution did not start.", e);
      }
    }
    
    private void ensureCompletesIn(Process process, int duration, TimeUnit timeUnit) throws InterruptedException {
      if (!process.waitFor(duration, timeUnit)) {
        process.destroyForcibly();
        throw new RuntimeException(format("Execution of ''{0}'' didn''t finished in {1} {2}.", Joiner.on(" ").join(command), duration, timeUnit));
      }
    }
    
  }
  
}

@@ -488,20 +488,68 @@
 va.util.
-List
+HashMap;%0Aimport java.util.List;%0Aimport java.util.Map
 ;%0Aimport
@@ -997,24 +997,85 @@
 perties;%0A  %0A
+  private final Map%3CString, String%3E environmentVariables;%0A  %0A
   %0A  public 
@@ -1191,16 +1191,60 @@
 st%3C%3E();%0A
+    environmentVariables = new HashMap%3C%3E();%0A
   %7D%0A  %0A 
@@ -1555,302 +1555,680 @@
 ecut
-ion run() throws IOException %7B%0A    return new Execution(new ProcessBuilder(jarCommand()));%0A  %7D%0A  %0A  public Execution runWithArguments(String... arguments) %7B%0A    List%3CString%3E command = jarCommand();%0A    command.addAll(asList(arguments));%0A    %0A    return new Execution(new ProcessBuilder(command)
+ableJar withEnvironmentVariable(String name, String value) %7B%0A    environmentVariables.put(name, value);%0A    %0A    return this;%0A  %7D%0A  %0A  public Execution run() throws IOException %7B%0A    ProcessBuilder processBuilder = new ProcessBuilder(jarCommand());%0A    processBuilder.environment().putAll(environmentVariables);%0A    return new Execution(processBuilder);%0A  %7D%0A  %0A  public Execution runWithArguments(String... arguments) %7B%0A    List%3CString%3E command = jarCommand();%0A    command.addAll(asList(arguments));%0A    %0A    ProcessBuilder processBuilder = new ProcessBuilder(command);%0A    processBuilder.environment().putAll(environmentVariables);%0A    return new Execution(processBuilder
 );%0A 

Support environment variables when running ExecutableJar