/*
 * Copyright 2000-2012 JetBrains s.r.o.
 *
 * 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 jetbrains.buildServer.agent.android.tools;

import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.util.Key;
import jetbrains.buildServer.agent.BuildProgressLogger;
import jetbrains.buildServer.agent.BuildRunnerContext;
import jetbrains.buildServer.agent.FlowGenerator;
import jetbrains.buildServer.agent.FlowLogger;
import jetbrains.buildServer.agent.android.AndroidRunnersUtil;
import jetbrains.buildServer.agent.android.NonZeroExitCodeException;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List;

abstract class AbstractTool {
  @NotNull private final String myToolPath;
  @NotNull private final BuildRunnerContext myContext;

  public AbstractTool(@NotNull final String toolPath, @NotNull final BuildRunnerContext context) {
    myToolPath = toolPath;
    myContext = context;
  }

  @NotNull
  protected File getWorkingDirectory() {
    return myContext.getWorkingDirectory();
  }

  protected boolean isDebug() {
    return AndroidRunnersUtil.isDebug(myContext);
  }

  protected void execute(@NotNull final List<String> argumentsBefore,
                         @NotNull final List<String> argumentsAfter,
                         @NotNull final String additionalArguments) throws IOException, ExecutionException, NonZeroExitCodeException {
    final GeneralCommandLine commandLine = createCommandLine(argumentsBefore, argumentsAfter, additionalArguments);

    final BuildProgressLogger logger = myContext.getBuild().getBuildLogger(), stdOutLogger = createNewFlowLogger(), stdErrLogger = createNewFlowLogger();
    final boolean isDebug = isDebug();

    if (isDebug) {
      logger.message("Executing command: " + commandLine.getCommandLineString());
    }

    final OSProcessHandler processHandler = createProcessHandler(commandLine);

    processHandler.addProcessListener(new ProcessAdapter() {
      @Override
      public void onTextAvailable(final ProcessEvent event, final Key outputType) {
        if (outputType == ProcessOutputTypes.STDERR) {
          stdErrLogger.error(event.getText());
        } else if (isDebug && outputType == ProcessOutputTypes.STDOUT) {
          stdOutLogger.message(event.getText());
        }
      }
    });

    processHandler.startNotify();
    processHandler.waitFor();

    final int exitCode = processHandler.getProcess().exitValue();
    if (exitCode != 0) {
      logger.error("Execution failed. Exit code is " + exitCode + ".");
      throw new NonZeroExitCodeException();
    }
  }

  @NotNull
  private FlowLogger createNewFlowLogger() {
    return myContext.getBuild().getBuildLogger().getFlowLogger(FlowGenerator.generateNewFlow());
  }

  @NotNull
  private static OSProcessHandler createProcessHandler(@NotNull final GeneralCommandLine commandLine) throws ExecutionException {
    return new OSProcessHandler(commandLine.createProcess(), "") {
      @Override
      protected Reader createProcessOutReader() {
        return new InputStreamReader(getProcess().getInputStream());
      }

      @Override
      protected Reader createProcessErrReader() {
        return new InputStreamReader(getProcess().getErrorStream());
      }
    };
  }

  @NotNull
  private GeneralCommandLine createCommandLine(@NotNull final List<String> argumentsBefore,
                                               @NotNull final List<String> argumentsAfter,
                                               @NotNull final String additionalArguments) {
    final GeneralCommandLine cmd = new GeneralCommandLine();

    cmd.setExePath(myToolPath);
    cmd.setWorkDirectory(getWorkingDirectory().getPath());
    cmd.setPassParentEnvs(false);
    cmd.setEnvParams(myContext.getBuildParameters().getEnvironmentVariables());
    cmd.addParameters(argumentsBefore);
    cmd.getParametersList().addParametersString(additionalArguments);
    cmd.addParameters(argumentsAfter);

    return cmd;
  }
}
