/*
 * Copyright 2013-2015, Teradata, Inc. All rights reserved.
 */
package com.teradata.benchto.driver.macro.shell;

import com.google.common.collect.ImmutableMap;
import com.teradata.benchto.driver.Benchmark;
import com.teradata.benchto.driver.BenchmarkExecutionException;
import com.teradata.benchto.driver.macro.MacroExecutionDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * Executes macros using bash defined in application yaml file.
 */
@Component
public class ShellMacroExecutionDriver
        implements MacroExecutionDriver
{
    private static final Logger LOGGER = LoggerFactory.getLogger(ShellMacroExecutionDriver.class);

    private static final String SHELL = "bash";

    @Autowired
    private ShellMacrosProperties macros;

    public boolean canExecuteBenchmarkMacro(String macroName)
    {
        return macros.getMacros().containsKey(macroName);
    }

    @Override
    public void runBenchmarkMacro(String macroName, Optional<Benchmark> benchmark)
    {
        runBenchmarkMacro(macroName, ImmutableMap.of(), benchmark);
    }

    public void runBenchmarkMacro(String macroName, Map<String, String> environment, Optional<Benchmark> benchmark)
    {
        try {
            String macroCommand = getMacroCommand(macroName);
            ProcessBuilder processBuilder = new ProcessBuilder(SHELL, "-c", macroCommand);
            processBuilder.environment().putAll(environment);
            Process macroProcess = processBuilder.start();
            LOGGER.info("Executing macro: '{}'", macroCommand);
            printOutput(macroProcess);
            macroProcess.waitFor();
            checkState(macroProcess.exitValue() == 0, "Macro %s exited with code %s", macroName, macroProcess.exitValue());
        }
        catch (IOException | InterruptedException e) {
            throw new BenchmarkExecutionException("Could not execute macro " + macroName, e);
        }
    }

    private String getMacroCommand(String macroName)
    {
        checkArgument(macros.getMacros().containsKey(macroName), "Macro %s is not defined", macroName);
        return checkNotNull(macros.getMacros().get(macroName).getCommand(), "Macro %s has no command defined", macroName);
    }

    private void printOutput(Process process)
            throws IOException
    {
        String line;

        LOGGER.info("std output:");
        try (BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            while ((line = input.readLine()) != null) {
                LOGGER.info(line);
            }
        }

        LOGGER.info("std error:");
        try (BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
            while ((line = error.readLine()) != null) {
                LOGGER.error(line);
            }
        }
    }
}

@@ -555,24 +555,52 @@
 OException;%0A
+import java.io.InputStream;%0A
 import java.
@@ -669,16 +669,52 @@
 ptional;
+%0Aimport java.util.function.Consumer;
 %0A%0Aimport
@@ -2070,28 +2070,16 @@
         
-printOutput(
 macroPro
@@ -2082,16 +2082,25 @@
 oProcess
+.waitFor(
 );%0A     
@@ -2106,38 +2106,77 @@
         
-macroProcess.waitFor()
+boolean completedSuccessfully = macroProcess.exitValue() == 0
 ;%0A      
@@ -2181,26 +2181,27 @@
         
-checkState
+printOutput
 (macroPr
@@ -2205,33 +2205,87 @@
 oProcess
-.exitValue() == 0
+, !completedSuccessfully);%0A            checkState(completedSuccessfully
 , %22Macro
@@ -2861,16 +2861,39 @@
  process
+, boolean stdoutAsError
 )%0A      
@@ -2935,218 +2935,191 @@
     
-String line;%0A%0A        LOGGER.info(
+logStream(process.getInputStream(), line -%3E %7B%0A            line = 
 %22std
- 
 out
-put:%22);%0A        try (BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()))) %7B%0A            while ((line = input.readLine()) != null)
+: %22 + line;%0A            if (stdoutAsError) %7B%0A                LOGGER.error(line);%0A            %7D%0A            else
  %7B%0A 
@@ -3140,20 +3140,21 @@
  LOGGER.
-info
+debug
 (line);%0A
@@ -3172,25 +3172,26 @@
  %7D%0A        %7D
-%0A
+);
 %0A        LOG
@@ -3191,33 +3191,226 @@
     
-LOGGER.info(%22std error:%22)
+logStream(process.getErrorStream(), line -%3E LOGGER.error(%22stderr: %22 + line));%0A    %7D%0A%0A    private void logStream(InputStream inputStream, Consumer%3CString%3E logger)%0A            throws IOException%0A    %7B%0A        String line
 ;%0A  
@@ -3435,21 +3435,21 @@
 dReader 
-error
+input
  = new B
@@ -3488,32 +3488,19 @@
 der(
-process.getError
+input
 Stream
-()
 ))) 
@@ -3528,21 +3528,21 @@
 (line = 
-error
+input
 .readLin
@@ -3569,36 +3569,37 @@
             
-LOGGER.error
+logger.accept
 (line);%0A    

remove abundant stdout/stderr printing in benchto logging