package org.rug.runners;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Models an external tool which command is defined in the 'tools.properties' file and arguments
 * can be given at runtime.
 */
public abstract class ToolRunner{

    protected final static Logger logger = LoggerFactory.getLogger(ToolRunner.class);

    private ProcessBuilder builder;
    private String homeDir;
    private String command;
    private List<String> commandLine;
    private String toolName;

    /**
     * Initializes a tool with the given name and the given command.
     * @param toolName The prefix toolName of the tool used in the properties file.
     * @param command The command to execute.
     */
    public ToolRunner(String toolName, String command){
        this.toolName = toolName;
        this.command = command;
        this.homeDir = new java.io.File( "." ).getAbsolutePath();
    }

    /**
     * Sets the arguments to pass to this tool.
     * @param args the array containing the arguments.
     */
    protected final void setArgs(String... args){
        if (args == null)
            args = new String[]{""};
        commandLine = new ArrayList<>(Arrays.asList(command.split(" ")));
        commandLine.addAll(Arrays.asList(args));
        this.builder = new ProcessBuilder(commandLine);

    }

    /**
     * Builds the underlying process and invokes {@link ProcessBuilder#start()} that causes the execution
     * of the underlying process.
     * @return The process being executed.
     */
    public int run(){
        int exitCode;
        Process p;
        try {
            logger.info("Running {} with command: {}", getToolName(), String.join(" ", commandLine));
            preProcess();
            p = builder.start();
            exitCode = p.waitFor();
            if (exitCode == 0)
                postProcess(p);
            logger.info("Completed {} with exit code {}.", getToolName(), exitCode);
        }catch (IOException e) {
            logger.error("Could not run the following command: {}", String.join(" ", commandLine));
            logger.error("The following error message was generated: {}", e.getMessage());
            exitCode = -1;
        } catch (InterruptedException e) {
            logger.error(e.getMessage());
            exitCode = -1;
        }
        return exitCode;
    }

    protected abstract void preProcess();
    protected abstract void postProcess(Process p) throws IOException;

    public ProcessBuilder getBuilder() {
        return builder;
    }

    public void setHomeDir(String homeDir) {
        this.homeDir = homeDir;
        this.builder.directory(new File(this.homeDir));
    }

    public void inheritOutput(boolean showOutput){
        if (!showOutput) {
            this.builder.redirectOutput(ProcessBuilder.Redirect.DISCARD);
            this.builder.redirectError(ProcessBuilder.Redirect.DISCARD);
        } else {
            this.builder.redirectError(ProcessBuilder.Redirect.INHERIT);
            this.builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        }
    }

    public String getToolName() {
        return toolName;
    }


    public String getHomeDir() {
        return homeDir;
    }

    @Override
    public String toString() {
        return String.join(" ", commandLine);
    }

}
