/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * 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.android;

import com.facebook.buck.core.model.UnflavoredBuildTarget;
import com.facebook.buck.rules.coercer.BuildConfigFields;
import com.google.common.collect.ImmutableList;

/** Utilities for generating a {@code BuildConfig.java} file for Android. */
public class BuildConfigs {

  /**
   * Name of the boolean global variable provided by the standard Android tools to indicate whether
   * an app was built in debug mode or not.
   */
  public static final String DEBUG_CONSTANT = "DEBUG";

  /**
   * Name of a Buck-specific global variable that indicates whether an app was built using
   * exopackage.
   */
  public static final String IS_EXO_CONSTANT = "IS_EXOPACKAGE";

  /** Name of a global variable that includes the exopackage configuration as a bitmask. */
  public static final String EXOPACKAGE_FLAGS = "EXOPACKAGE_FLAGS";

  /** @see #getDefaultBuildConfigFields() */
  private static final BuildConfigFields DEFAULT_BUILD_CONFIG_CONSTANTS =
      BuildConfigFields.fromFields(
          ImmutableList.of(
              // DEBUG is expected by the standard Android tools.
              BuildConfigFields.Field.of("boolean", DEBUG_CONSTANT, "true"),
              // IS_EXOPACKAGE is a value we use internally for checking whether exopackage is being
              // used.
              BuildConfigFields.Field.of("boolean", IS_EXO_CONSTANT, "false"),
              BuildConfigFields.Field.of("int", EXOPACKAGE_FLAGS, "0")));

  /** Utility class: do not instantiate. */
  private BuildConfigs() {}

  /**
   * Returns a list of fields (with values) that every {@code BuildConfig.java} should declare. The
   * default value of each constant may be overridden by the {@code userFields} passed to {@link
   * #generateBuildConfigDotJava(UnflavoredBuildTarget, String, boolean, BuildConfigFields)} when
   * generating a {@code BuildConfig.java}.
   */
  public static BuildConfigFields getDefaultBuildConfigFields() {
    return DEFAULT_BUILD_CONFIG_CONSTANTS;
  }

  /**
   * Generates the source code for an Android {@code BuildConfig.java} file with the default set of
   * fields specified by {@link #getDefaultBuildConfigFields()}.
   */
  public static String generateBuildConfigDotJava(
      UnflavoredBuildTarget source, String javaPackage) {
    return generateBuildConfigDotJava(
        source, javaPackage, /* useConstantExpressions */ false, BuildConfigFields.of());
  }

  /**
   * Generates the source code for an Android {@code BuildConfig.java} file with fields specified by
   * {@code userFields}.
   *
   * <p>The output will also contain a constant for every entry in the collection returned by {@link
   * #getDefaultBuildConfigFields()}.
   *
   * <p>If the name of a field in {@code userFields} matches one in the collection returned by
   * {@link #getDefaultBuildConfigFields()}, the value in the {@code userFields} map will be used.
   *
   * @param javaPackage The package for the Java class generated by this method.
   * @param useConstantExpressions If {@code true}, the value for each static final field in the
   *     generated class will be declared as the literal value in {@code userFields}. The values of
   *     such fields can be inlined by {@code javac}.
   *     <p>If {@code false}, the value for each static final field in the generated class will be
   *     declared as a non-constant expression that is guaranteed to evaluate to the same value in
   *     {@code userFields}. This ensures that the generated {@code BuildConfig.java} can still be
   *     used in Robolectric tests, but does not run the risk of its values being inlined by {@code
   *     javac}. This is important if the generated {@code BuildConfig} class is going to be swapped
   *     out by a different implementation by {@link AndroidBinary}. See {@link AndroidBuildConfig}
   *     for details.
   * @param userFields represents the fields that should be declared in the generated {@code
   *     BuildConfig} class.
   */
  public static String generateBuildConfigDotJava(
      UnflavoredBuildTarget source,
      String javaPackage,
      boolean useConstantExpressions,
      BuildConfigFields userFields) {

    BuildConfigFields totalFields = getDefaultBuildConfigFields().putAll(userFields);
    return totalFields.generateBuildConfigDotJava(source, javaPackage, useConstantExpressions);
  }
}
