/*
 * Copyright 2016-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.facebook.buck.cli;

import com.facebook.buck.distributed.BuildJobStateSerializer;
import com.facebook.buck.distributed.DistBuildMode;
import com.facebook.buck.distributed.DistBuildService;
import com.facebook.buck.distributed.DistBuildSlaveExecutor;
import com.facebook.buck.distributed.thrift.BuildJobState;
import com.facebook.buck.distributed.thrift.StampedeId;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.Pair;
import com.facebook.buck.util.Console;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;

import org.kohsuke.args4j.Option;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

public class DistBuildRunCommand extends AbstractDistBuildCommand {

  public static final String BUILD_STATE_FILE_ARG_NAME = "--build-state-file";
  public static final String BUILD_STATE_FILE_ARG_USAGE = "File containing the BuildStateJob data.";

  @Nullable
  @Option(name = BUILD_STATE_FILE_ARG_NAME, usage = BUILD_STATE_FILE_ARG_USAGE)
  private String buildStateFile;

  @Nullable
  @Option(name = "--coordinator-port",
      usage = "The local port that the build coordinator thrift server will listen on.")
  private int coordinatorPort = -1;

  @Nullable
  @Option(name = "--build-mode",
      usage = "The mode in which the distributed build is going to run.")
  private DistBuildMode distBuildMode = DistBuildMode.REMOTE_BUILD;

  @Nullable
  @Option(name = "--global-cache-dir",
      usage = "Full path to an existing directory that will contain a global cache across builds.")
  private Path globalCacheDir;

  @Override
  public boolean isReadOnly() {
    return false;
  }

  @Override
  public String getShortDescription() {
    return "runs a distributed build in the current machine (experimental)";
  }

  @Override
  public int runWithoutHelp(CommandRunnerParams params) throws IOException, InterruptedException {
    Stopwatch stopwatch = Stopwatch.createStarted();
    Console console = params.getConsole();
    try (DistBuildService service = DistBuildFactory.newDistBuildService(params)) {
      Pair<BuildJobState, String> jobStateAndBuildName = getBuildJobStateAndBuildName(
          params.getCell().getFilesystem(),
          console,
          service);
      BuildJobState jobState = jobStateAndBuildName.getFirst();
      String buildName = jobStateAndBuildName.getSecond();

      console.getStdOut().println(String.format(
          "BuildJob depends on a total of [%d] input deps.",
          jobState.getFileHashesSize()));
      try (CommandThreadManager pool = new CommandThreadManager(
          getClass().getName(),
          getConcurrencyLimit(params.getBuckConfig()))) {
        DistBuildSlaveExecutor distBuildExecutor = DistBuildFactory.createDistBuildExecutor(
            jobState,
            params,
            pool.getExecutor(),
            service,
            Preconditions.checkNotNull(distBuildMode),
            coordinatorPort,
            getStampedeIdOptional(),
            getGlobalCacheDirOptional());
        int returnCode = distBuildExecutor.buildAndReturnExitCode();
        console.printSuccess(String.format(
            "Successfully ran distributed build [%s] in [%d millis].",
            buildName,
            stopwatch.elapsed(
                TimeUnit.MILLISECONDS)));
        return returnCode;
      }
    }
  }

  public Pair<BuildJobState, String> getBuildJobStateAndBuildName(
      ProjectFilesystem filesystem,
      Console console,
      DistBuildService service) throws IOException {

    if (buildStateFile != null) {
      Path buildStateFilePath = Paths.get(buildStateFile);
      console.getStdOut().println(String.format(
          "Retrieving BuildJobState for from file [%s].",
          buildStateFilePath));
      return new Pair<>(
          BuildJobStateSerializer.deserialize(filesystem.newFileInputStream(buildStateFilePath)),
          String.format("LocalFile=[%s]", buildStateFile));
    } else {
      StampedeId stampedeId = getStampedeId();
      console.getStdOut().println(String.format(
          "Retrieving BuildJobState for build [%s].",
          stampedeId));
      return new Pair<>(
          service.fetchBuildJobState(stampedeId),
          String.format("DistBuild=[%s]", stampedeId.toString()));
    }
  }

  private Optional<Path> getGlobalCacheDirOptional() {
    if (globalCacheDir == null) {
      return Optional.empty();
    } else {
      if (!Files.isDirectory(globalCacheDir)) {
        try {
          Files.createDirectories(globalCacheDir);
        } catch (IOException exception) {
          throw new HumanReadableException(
              exception,
              "Directory passed for --global-cache-dir cannot be created. value=[%s]",
              globalCacheDir.toString());
        }
      }

      return Optional.of(globalCacheDir);
    }
  }
}

@@ -3921,24 +3921,57 @@
 ExitCode();%0A
+        if (returnCode == 0) %7B%0A  
         cons
@@ -4014,16 +4014,18 @@
         
+ 
+ 
 %22Success
@@ -4083,24 +4083,26 @@
             
+  
 buildName,%0A 
@@ -4100,16 +4100,18 @@
 ldName,%0A
+  
         
@@ -4149,16 +4149,18 @@
         
+  
 TimeUnit
@@ -4173,24 +4173,249 @@
 SECONDS)));%0A
+        %7D else %7B%0A          console.printErrorText(%0A              %22Failed distributed build %5B%25s%5D in %5B%25d millis%5D.%22,%0A              buildName,%0A              stopwatch.elapsed(%0A                  TimeUnit.MILLISECONDS));%0A        %7D%0A
         retu

Fix the exit message to printError if dist build failed.