package com.hubspot.singularity.runner.base.shared;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ProcessBuilder.Redirect;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import com.google.common.collect.Sets;
import org.slf4j.Logger;

import com.google.common.base.Optional;
import com.google.common.base.Throwables;

public class SimpleProcessManager extends SafeProcessManager {

  public SimpleProcessManager(Logger log) {
    super(log);
  }

  public void runCommand(final List<String> command, final Set<Integer> acceptableExitCodes) throws InterruptedException, ProcessFailedException {
    runCommand(command, Redirect.INHERIT, acceptableExitCodes);
  }

  public void runCommand(final List<String> command) throws InterruptedException, ProcessFailedException {
    runCommand(command, Redirect.INHERIT, Sets.newHashSet(0));
  }

  public List<String> runCommandWithOutput(final List<String> command, final Set<Integer> acceptableExitCodes) throws InterruptedException, ProcessFailedException {
    return runCommand(command, Redirect.PIPE, acceptableExitCodes);
  }

  public List<String> runCommandWithOutput(final List<String> command) throws InterruptedException, ProcessFailedException {
    return runCommand(command, Redirect.PIPE, Sets.newHashSet(0));
  }

  public List<String> runCommand(final List<String> command, final Redirect redirectOutput) throws InterruptedException, ProcessFailedException {
    return runCommand(command, redirectOutput, Sets.newHashSet(0));
  }

  public List<String> runCommand(final List<String> command, final Redirect redirectOutput, final Set<Integer> acceptableExitCodes) throws InterruptedException, ProcessFailedException {
    final ProcessBuilder processBuilder = new ProcessBuilder(command);

    Optional<Integer> exitCode = Optional.absent();

    Optional<OutputReader> reader = Optional.absent();

    try {
      processBuilder.redirectError(Redirect.INHERIT);
      processBuilder.redirectOutput(redirectOutput);

      final Process process = startProcess(processBuilder);

      if (redirectOutput == Redirect.PIPE) {
        reader = Optional.of(new OutputReader(process.getInputStream()));
        reader.get().start();
      }

      exitCode = Optional.of(process.waitFor());

      if (reader.isPresent()) {
        reader.get().join();

        if (reader.get().error.isPresent()) {
          throw reader.get().error.get();
        }
      }

    } catch (InterruptedException ie) {
      signalKillToProcessIfActive();

      throw ie;
    } catch (Throwable t) {
      getLog().error("Unexpected exception while running {}", getCurrentProcessToString(), t);

      signalKillToProcessIfActive();

      throw Throwables.propagate(t);
    } finally {
      processFinished(exitCode);
    }

    if (exitCode.isPresent() && !acceptableExitCodes.contains(exitCode.get())) {
      throw new ProcessFailedException(String.format("Got unacceptable exit code %s while running %s", exitCode, getCurrentProcessToString()));
    }

    if (!reader.isPresent()) {
      return Collections.emptyList();
    }

    return reader.get().output;
  }

  private static class OutputReader extends Thread {

    private final List<String> output;
    private final InputStream inputStream;
    private Optional<Throwable> error;

    public OutputReader(InputStream inputStream) {
      this.output = new ArrayList<>();
      this.inputStream = inputStream;
      this.error = Optional.absent();
    }

    @Override
    public void run() {
      try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
        String line = br.readLine();
        while (line != null) {
          output.add(line);
          line = br.readLine();
        }
      } catch (Throwable t) {
        this.error = Optional.of(t);
      }
    }

  }


}

@@ -331,47 +331,8 @@
 t;%0A%0A
-import com.google.common.collect.Sets;%0A
 impo
@@ -434,16 +434,55 @@
 owables;
+%0Aimport com.google.common.collect.Sets;
 %0A%0Apublic
@@ -2012,24 +2012,83 @@
 .absent();%0A%0A
+    String processToString = getCurrentProcessToString();%0A%0A
     try %7B%0A  
@@ -2081,24 +2081,24 @@
 %0A%0A    try %7B%0A
-
       proces
@@ -2250,24 +2250,78 @@
 sBuilder);%0A%0A
+      processToString = getCurrentProcessToString();%0A%0A
       if (re
@@ -2869,27 +2869,17 @@
 ng %7B%7D%22, 
-getCurrentP
+p
 rocessTo
@@ -2884,18 +2884,16 @@
 ToString
-()
 , t);%0A%0A 
@@ -3216,27 +3216,17 @@
 itCode, 
-getCurrentP
+p
 rocessTo
@@ -3231,18 +3231,16 @@
 ToString
-()
 ));%0A    

fix error stmt.