// Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package com.twitter.intellij.pants.service.task;

import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.externalSystem.model.ExternalSystemException;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener;
import com.intellij.openapi.externalSystem.task.AbstractExternalSystemTaskManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.util.containers.ContainerUtil;
import com.twitter.intellij.pants.PantsBundle;
import com.twitter.intellij.pants.model.PantsTargetAddress;
import com.twitter.intellij.pants.settings.PantsExecutionSettings;
import com.twitter.intellij.pants.util.PantsConstants;
import com.twitter.intellij.pants.util.PantsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.List;
import java.util.Map;

public class PantsTaskManager extends AbstractExternalSystemTaskManager<PantsExecutionSettings> {
  private static final Map<String, String> goal2JvmOptionsFlag = ContainerUtil.newHashMap(
    Pair.create("test", "--test-junit-jvm-options"),
    Pair.create("run", "--run-jvm-jvm-options")
  );

  private final Map<ExternalSystemTaskId, Process> myCancellationMap = ContainerUtil.newConcurrentMap();

  @Override
  public void executeTasks(
    @NotNull final ExternalSystemTaskId id,
    @NotNull List<String> taskNames,
    @NotNull String projectPath,
    @Nullable PantsExecutionSettings settings,
    @NotNull List<String> vmOptions,
    @NotNull List<String> scriptParameters,
    @Nullable String debuggerSetup,
    @NotNull final ExternalSystemTaskNotificationListener listener
  ) throws ExternalSystemException {
    if (settings == null) {
      return;
    }
    projectPath = PantsTargetAddress.extractPath(projectPath);
    final GeneralCommandLine commandLine = PantsUtil.defaultCommandLine(projectPath);

    final String relativeProjectPath = PantsUtil.getRelativeProjectPath(commandLine.getWorkDirectory(), new File(projectPath));

    commandLine.addParameters(taskNames);
    for (String targetName : settings.getTargetNames()) {
      commandLine.addParameter(relativeProjectPath + ":" + targetName);
    }
    commandLine.addParameters(scriptParameters);
    for (String goal : taskNames) {
      final String jvmOptionsFlag = goal2JvmOptionsFlag.get(goal);
      if (jvmOptionsFlag == null) {
        continue;
      }
      for (String vmOption : vmOptions) {
        commandLine.addParameter(jvmOptionsFlag + "=" + vmOption);
      }
    }

    commandLine.addParameters("--no-color");
    if (debuggerSetup != null) {
      if (taskNames.size() > 1) {
        throw new ExternalSystemException(PantsBundle.message("pants.error.multiple.tasks.for.debugging"));
      }
      final String goal = taskNames.iterator().next();
      final String jvmOptionsFlag = goal2JvmOptionsFlag.get(goal);
      if (jvmOptionsFlag == null) {
        throw new ExternalSystemException(PantsBundle.message("pants.error.cannot.debug.task", goal));
      }
      commandLine.addParameter(jvmOptionsFlag + "=" + debuggerSetup);
    }

    listener.onTaskOutput(id, commandLine.getCommandLineString(PantsConstants.PANTS), true);
    try {
      final Process process = commandLine.createProcess();
      myCancellationMap.put(id, process);
      PantsUtil.getOutput(
        process,
        new ProcessAdapter() {
          @Override
          public void startNotified(ProcessEvent event) {
            super.startNotified(event);
            listener.onStart(id);
          }

          @Override
          public void onTextAvailable(ProcessEvent event, Key outputType) {
            super.onTextAvailable(event, outputType);
            listener.onTaskOutput(id, event.getText(), outputType == ProcessOutputTypes.STDOUT);
          }
        }
      );
    }
    catch (ExecutionException e) {
      throw new ExternalSystemException(e);
    }
    finally {
      myCancellationMap.remove(id);
    }
  }

  @Override
  public boolean cancelTask(@NotNull ExternalSystemTaskId id, @NotNull ExternalSystemTaskNotificationListener listener)
    throws ExternalSystemException {
    final Process process = myCancellationMap.get(id);
    if (process != null) {
      try {
        process.destroy();
        return true;
      }
      catch (Exception e) {
        throw new ExternalSystemException(e);
      }
    }
    return true;
  }
}

@@ -2532,24 +2532,74 @@
 taskNames);%0A
+    if (!settings.getTargetNames().isEmpty()) %7B%0A  
     for (Str
@@ -2640,24 +2640,26 @@
 tNames()) %7B%0A
+  
       comman
@@ -2714,24 +2714,122 @@
 argetName);%0A
+      %7D%0A    %7D else %7B%0A      commandLine.addParameter(relativeProjectPath + File.separator + %22::%22);%0A
     %7D%0A    co

Handle :: for tasks