package org.implab.gradle.common.exec.runtime;

import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.implab.gradle.common.exec.model.CommandSpec;
import org.implab.gradle.common.exec.model.EnvironmentSpec;
import org.implab.gradle.common.exec.model.PipeSpec;

@NonNullByDefault
class SystemExecBuilder extends AbstractExecBuilder<CommandSpec> {
    SystemExecBuilder(CommandSpec command) {
        super(command);
    }

    @Override
    protected CompletableFuture<Integer> startInternal(
            CommandSpec command,
            EnvironmentSpec environment,
            PipeSpec redirect) throws IOException {

        var builder = new ProcessBuilder(command.commandLine());

        environment.workingDirectory().ifPresent(builder::directory);

        // if the env isn't inherited we need to clear it
        if (!environment.inheritEnvironment())
            builder.environment().clear();

        builder.environment().putAll(environment.environment());

        var tasks = new ArrayList<CompletableFuture<?>>();

        if (!redirect.stdout().isPresent())
            builder.redirectOutput(Redirect.DISCARD);
        if (!redirect.stderr().isPresent())
            builder.redirectError(Redirect.DISCARD);

        // run process
        var proc = builder.start();

        tasks.add(proc.onExit());

        redirect.stdin().map(from -> from.redirect(proc.getOutputStream())).ifPresent(tasks::add);
        redirect.stdout().map(to -> to.redirect(proc.getInputStream())).ifPresent(tasks::add);
        redirect.stderr().map(to -> to.redirect(proc.getErrorStream())).ifPresent(tasks::add);

        return CompletableFuture
                .allOf(tasks.toArray(new CompletableFuture<?>[0]))
                .thenApply(t -> proc.exitValue());
    }
}
