package com.atlassian.bitbucket.mesh.process;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static java.util.Objects.requireNonNull;

/**
 * Provides a summary of a completed process.
 */
public class ProcessSummary {

    private final boolean canceled;
    private final String commandLine;
    private final int exitCode;
    private final String stderr;
    private final long stderrRead;
    private final long stdinWritten;
    private final long stdoutRead;
    private final Throwable thrown;
    private final boolean timedOut;

    private ProcessSummary(Builder builder) {
        canceled = builder.canceled;
        commandLine = builder.commandLine;
        exitCode = builder.exitCode;
        stderr = builder.stderr;
        stderrRead = builder.stderrRead;
        stdinWritten = builder.stdinWritten;
        stdoutRead = builder.stdoutRead;
        thrown = builder.thrown;
        timedOut = builder.timedOut;
    }

    /**
     * @return the process's command line
     */
    @Nonnull
    public String getCommandLine() {
        return commandLine;
    }

    /**
     * @return the process's exit code
     */
    public int getExitCode() {
        return exitCode;
    }

    /**
     * @return everything the process wrote to {@code stderr} <i>unless</i> {@link ProcessStdioHandler#onStderr}
     *         consumed it, in which case this will be {@code null}
     */
    @Nullable
    public String getStderr() {
        return stderr;
    }

    /**
     * @return the number of bytes read from the process's {@code stderr}
     */
    public long getStderrRead() {
        return stderrRead;
    }

    /**
     * @return the number of bytes written to the process's {@code stdin}
     */
    public long getStdinWritten() {
        return stdinWritten;
    }

    /**
     * @return the number of bytes read from the process's {@code stdout}
     */
    public long getStdoutRead() {
        return stdoutRead;
    }

    /**
     * @return any exception thrown by the {@link ProcessStdioHandler}, or {@code null}
     */
    @Nullable
    public Throwable getThrown() {
        return thrown;
    }

    /**
     * @return {@code true} if the {@link ProcessStdioHandler} used the {@link OsProcess} to cancel the process,
     *         or, for asynchronous processes, if the process was canceled by the caller; otherwise, {@code false}
     */
    public boolean isCanceled() {
        return canceled;
    }

    /**
     * @return {@code true} if the process timed out before it could complete; otherwise, {@code false}
     */
    public boolean isTimedOut() {
        return timedOut;
    }

    /**
     * @return the process's command line and exit code
     */
    @Override
    public String toString() {
        return commandLine + " (" + exitCode + ")";
    }

    public static class Builder {

        private final String commandLine;

        private boolean canceled;
        private int exitCode;
        private String stderr;
        private long stderrRead;
        private long stdinWritten;
        private long stdoutRead;
        private Throwable thrown;
        private boolean timedOut;

        public Builder(@Nonnull ProcessSummary processSummary) {
            canceled = requireNonNull(processSummary, "processSummary").isCanceled();
            commandLine = requireNonNull(processSummary.getCommandLine(), "commandLine");
            exitCode = processSummary.getExitCode();
            stderr = processSummary.getStderr();
            thrown = processSummary.getThrown();
            timedOut = processSummary.isTimedOut();
        }

        public Builder(@Nonnull String commandLine) {
            this.commandLine = requireNonNull(commandLine, "commandLine");
        }

        @Nonnull
        public ProcessSummary build() {
            return new ProcessSummary(this);
        }

        @Nonnull
        public Builder canceled(boolean value) {
            canceled = value;

            return this;
        }

        @Nonnull
        public Builder exitCode(int value) {
            exitCode = value;

            return this;
        }

        @Nonnull
        public Builder stderr(@Nullable String value) {
            stderr = value;

            return this;
        }

        @Nonnull
        public Builder stderrRead(long value) {
            stderrRead = value;

            return this;
        }

        @Nonnull
        public Builder stdinWritten(long value) {
            stdinWritten = value;

            return this;
        }

        @Nonnull
        public Builder stdoutRead(long value) {
            stdoutRead = value;

            return this;
        }

        @Nonnull
        public Builder thrown(@Nullable Throwable value) {
            thrown = value;

            return this;
        }

        @Nonnull
        public Builder timedOut(boolean value) {
            timedOut = value;

            return this;
        }
    }
}
