/*
 * 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;

import com.intellij.openapi.util.SystemInfo;
import jetbrains.buildServer.agent.BuildRunnerContext;
import jetbrains.buildServer.agent.android.tools.*;
import jetbrains.buildServer.agent.runner.JavaRunnerUtil;
import jetbrains.buildServer.runner.JavaRunnerConstants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.Map;

import static jetbrains.buildServer.runner.android.AndroidEnvironmentConstants.*;

class AndroidEnvironment {
  public static final String FN_TOOLS = "tools";
  public static final String FN_PLATFORM_TOOLS = "platform-tools";

  @NotNull private final BuildRunnerContext myContext;
  @NotNull private final String mySdkHome;
  @Nullable private final String myTargetHome;
  @Nullable private final String myJavaHome;

  public AndroidEnvironment(@NotNull final BuildRunnerContext context,
                            final boolean needTarget,
                            final boolean needJava) {
    myContext = context;

    final Map<String, String> config = context.getRunnerParameters();
    final Map<String, String> environmentVariables = context.getBuildParameters().getEnvironmentVariables();

    String sdkHome = config.get(PARAM_SDK_HOME);
    if (sdkHome == null) {
      sdkHome = environmentVariables.get(ENV_SDK_HOME);
    }

    String targetHome = null;
    if (!needTarget || Boolean.parseBoolean(config.get(PARAM_USE_CUSTOM_TARGET_PATH))) {
      targetHome = config.get(PARAM_TARGET_HOME);
      if (targetHome == null) {
        targetHome = environmentVariables.get(ENV_TARGET_HOME);
      }
    } else if (sdkHome != null) {
      final File latestTarget = AndroidSDKUtil.getLatestTarget(sdkHome);
      if (latestTarget != null) {
        targetHome = latestTarget.getAbsolutePath();
      }
    }

    if (targetHome != null) {
      if (new File(targetHome).isAbsolute()) {
        if (sdkHome == null) {
          sdkHome = new File(targetHome).getParentFile().getParent();
        }
      } else {
        targetHome = sdkHome == null ? null : new File(sdkHome, targetHome).getAbsolutePath();
      }
    }

    final String javaHome = JavaRunnerUtil.findJavaHome(
        config.get(JavaRunnerConstants.TARGET_JDK_HOME),
        context.getBuildParameters().getAllParameters(),
        context.getWorkingDirectory().getPath()
    );

    if (sdkHome == null) {
      throw new BadEnvironmentException("Unable to find Android SDK home directory");
    }

    if (needTarget && targetHome == null) {
      throw new BadEnvironmentException("Unable to find Android target home directory");
    }

    if (needJava && javaHome == null) {
      throw new BadEnvironmentException("Unable to find java home directory");
    }

    mySdkHome = sdkHome;
    myTargetHome = targetHome;
    myJavaHome = javaHome;
  }

  @NotNull
  public AaptTool getAaptTool() {
    return new AaptTool(getTargetToolPath(SystemInfo.isWindows ? "aapt.exe" : "aapt"), getAndroidJarPath(), myContext);
  }

  @NotNull
  public DxTool getDxTool() {
    return new DxTool(getTargetToolPath(SystemInfo.isWindows ? "dx.bat" : "dx"), myContext);
  }

  @NotNull
  public JavacTool getJavacTool() {
    return new JavacTool(getJavaToolPath(SystemInfo.isWindows ? "javac.exe" : "javac"), getAndroidJarPath(), myContext);
  }

  @NotNull
  public KeytoolTool getKeytoolTool() {
    return new KeytoolTool(getJavaToolPath(SystemInfo.isWindows ? "keytool.exe" : "keytool"), myContext);
  }

  @NotNull
  public JarsignerTool getJarsignerTool() {
    return new JarsignerTool(getJavaToolPath(SystemInfo.isWindows ? "jarsigner.exe" : "jarsigner"), myContext);
  }

  @NotNull
  public ZipalignTool getZipalignTool() {
    return new ZipalignTool(getSdkToolPath(SystemInfo.isWindows ? "zipalign.exe" : "zipalign"), myContext);
  }

  @NotNull
  private String getAndroidJarPath() {
    return new File(myTargetHome, "android.jar").getAbsolutePath();
  }

  @NotNull
  private String getTargetToolPath(@NotNull final String toolName) {
    {
      final File file = getToolFile(mySdkHome, FN_PLATFORM_TOOLS, toolName);
      if (file.exists() && file.canExecute()) {
        return file.getAbsolutePath();
      }
    }
    if (myTargetHome == null) {
      throw new IllegalStateException("TargetHome is null. Cannot return tool '%s'. Initialize AndroidEnvironment with needTarget=true");
    }
    {
      final File file = getToolFile(myTargetHome, FN_TOOLS, toolName);
      if (file.exists() && file.canExecute()) {
        return file.getAbsolutePath();
      }
    }
    throw new BadEnvironmentException(String.format("Unable to find target tool '%s'", toolName));
  }

  @NotNull
  private String getSdkToolPath(@NotNull final String toolName) {
    {
      final File file = getToolFile(mySdkHome, FN_TOOLS, toolName);
      if (file.exists() && file.canExecute()) {
        return file.getAbsolutePath();
      }
    }
    throw new BadEnvironmentException(String.format("Unable to find sdk tool '%s'", toolName));
  }

  @NotNull
  private String getJavaToolPath(@NotNull final String toolName) {
    if (myJavaHome == null) {
      throw new IllegalStateException("JavaHome is null. Cannot return tool '%s'. Initialize AndroidEnvironment with needJava=true");
    }
    return getToolPath(myJavaHome, "bin", toolName);
  }

  @NotNull
  private static String getToolPath(@NotNull final String home, @NotNull final String dir, @NotNull final String toolName) {
    return getToolFile(home, dir, toolName).getAbsolutePath();
  }

  @NotNull
  private static File getToolFile(@NotNull final String home, @NotNull final String dir, @NotNull final String toolName) {
    return new File(new File(home, dir), toolName);
  }

}
