// Copyright 2018 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.rules.android;

import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.vfs.PathFragment;

/** Builder for creating databinding processing action. */
public class AndroidDataBindingProcessorBuilder {

  /**
   * Creates and registers an action to strip databinding from layout xml and generate the layout
   * info file.
   *
   * @param dataContext The android data context.
   * @param androidResources The resources to process.
   * @param appId The app id (the app's java package).
   * @param dataBindingLayoutInfoOut The output layout info file to write.
   * @param useDataBindingAndroidX Whether generated files should use AndroidX dependencies.
   * @return The new AndroidResources that has been processed by databinding.
   */
  public static AndroidResources create(
      AndroidDataContext dataContext,
      AndroidResources androidResources,
      String appId,
      Artifact dataBindingLayoutInfoOut,
      boolean useDataBindingAndroidX) {

    ImmutableList.Builder<Artifact> databindingProcessedResourcesBuilder = ImmutableList.builder();
    for (Artifact resource : androidResources.getResources()) {

      // Create resources that will be processed by databinding under paths that look like:
      //
      // <bazel-pkg>/databinding-processed-resources/<rule-name>/<bazal-pkg>/<resource-dir>

      Artifact databindingProcessedResource =
          dataContext.getUniqueDirectoryArtifact(
              "databinding-processed-resources", resource.getExecPath());

      databindingProcessedResourcesBuilder.add(databindingProcessedResource);
    }
    ImmutableList<Artifact> databindingProcessedResources =
        databindingProcessedResourcesBuilder.build();

    BusyBoxActionBuilder builder = BusyBoxActionBuilder.create(dataContext, "PROCESS_DATABINDING");

    // Create output resource roots that correspond to the paths of the resources created above:
    //
    //   <bazel-pkg>/databinding-processed-resources/<rule-name>/<resource-root>
    ArtifactRoot binOrGenfiles = dataContext.getBinOrGenfilesDirectory();
    PathFragment uniqueDir =
        dataContext.getUniqueDirectory(PathFragment.create("databinding-processed-resources"));
    PathFragment outputResourceRoot = binOrGenfiles.getExecPath().getRelative(uniqueDir);

    // AndroidDataBindingProcessingAction will append each value of --resource_root to its
    // corresponding --output_resource_directory, so the only part that needs to be constructed here
    // is:
    //
    //   <bazel-pkg>/databinding-processed-resources/<rule-name>
    builder.addFlag("--output_resource_directory", outputResourceRoot.toString());

    ImmutableList.Builder<PathFragment> outputResourceRootsBuilder = ImmutableList.builder();
    for (PathFragment resourceRoot : androidResources.getResourceRoots()) {

      builder.addFlag("--resource_root", resourceRoot.toString());

      outputResourceRootsBuilder.add(outputResourceRoot.getRelative(resourceRoot));
    }

    // Even though the databinding processor really only cares about layout files, we send
    // all the resources so that the new resource root that is created for databinding processing
    // can be used for later processing (e.g. aapt). It would be nice to send only the layout
    // files, but then we'd have to mix roots and rely on sandboxing to "hide" the
    // old unprocessed files, which might not work if, for example, the actions run locally.
    builder.addInputs(androidResources.getResources());

    builder.addOutputs(databindingProcessedResources);

    builder.addOutput("--dataBindingInfoOut", dataBindingLayoutInfoOut);
    builder.addFlag("--appId", appId);
    builder.maybeAddFlag("--useDataBindingAndroidX", useDataBindingAndroidX);

    builder.buildAndRegister("Processing data binding", "ProcessDatabinding");

    return new AndroidResources(databindingProcessedResources, outputResourceRootsBuilder.build());
  }
}
