/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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.model.BuildTargets;
import com.facebook.buck.rules.AbstractBuildRule;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.BuildOutputInitializer;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.InitializableFromDisk;
import com.facebook.buck.rules.OnDiskBuildInfo;
import com.facebook.buck.rules.RecordFileSha1Step;
import com.facebook.buck.rules.RuleKey;
import com.facebook.buck.rules.Sha1HashCode;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.facebook.buck.zip.ZipStep;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.Hashing;

import java.nio.file.Path;

import javax.annotation.Nullable;

/**
 * Buildable responsible for compiling non-english string resources to {@code .fbstr} files stored
 * as assets. Only applicable for {@code android_binary} rules with {@code resource_compression}
 * parameter set to {@link com.facebook.buck.android.ResourcesFilter.ResourceCompressionMode
 * #ENABLED_WITH_STRINGS_AS_ASSETS}.
 */
public class PackageStringAssets extends AbstractBuildRule
    implements InitializableFromDisk<PackageStringAssets.BuildOutput> {

  private static final String STRING_ASSETS_ZIP_HASH = "STRING_ASSETS_ZIP_HASH";

  private final FilteredResourcesProvider filteredResourcesProvider;
  private final AaptPackageResources aaptPackageResources;
  private final BuildOutputInitializer<BuildOutput> buildOutputInitializer;

  public PackageStringAssets(
      BuildRuleParams params,
      SourcePathResolver resolver,
      FilteredResourcesProvider filteredResourcesProvider,
      AaptPackageResources aaptPackageResources) {
    super(params, resolver);
    this.filteredResourcesProvider = filteredResourcesProvider;
    this.aaptPackageResources = aaptPackageResources;
    this.buildOutputInitializer = new BuildOutputInitializer<>(params.getBuildTarget(), this);
  }

  @Override
  public ImmutableCollection<Path> getInputsToCompareToOutput() {
    return ImmutableSet.of();
  }

  @Override
  public ImmutableList<Step> getBuildSteps(
      BuildContext context,
      BuildableContext buildableContext) {
    if (filteredResourcesProvider.getResDirectories().isEmpty()) {
      // There is no zip file, but we still need to provide a consistent hash to
      // ComputeExopackageDepsAbi in this case.
      buildableContext.addMetadata(
          STRING_ASSETS_ZIP_HASH,
          Hashing.sha1().hashInt(0).toString());
      return ImmutableList.of();
    }

    ImmutableList.Builder<Step> steps = ImmutableList.builder();
    // We need to generate a zip file with the following dir structure:
    // /assets/strings/*.fbstr
    Path pathToBaseDir = getPathToStringAssetsDir();
    Path pathToDirContainingAssetsDir = pathToBaseDir.resolve("string_assets");
    steps.add(new MakeCleanDirectoryStep(pathToDirContainingAssetsDir));
    Path pathToStrings = pathToDirContainingAssetsDir.resolve("assets").resolve("strings");
    Path pathToStringAssetsZip = getPathToStringAssetsZip();
    steps.add(new MakeCleanDirectoryStep(pathToStrings));
    steps.add(new CompileStringsStep(
            filteredResourcesProvider.getNonEnglishStringFiles(),
            aaptPackageResources.getPathToRDotTxtDir(),
            pathToStrings));
    steps.add(new ZipStep(
            pathToStringAssetsZip,
            ImmutableSet.<Path>of(),
            false,
            ZipStep.MAX_COMPRESSION_LEVEL,
            pathToDirContainingAssetsDir));
    steps.add(
        new RecordFileSha1Step(pathToStringAssetsZip, STRING_ASSETS_ZIP_HASH, buildableContext));
    buildableContext.recordArtifact(pathToStringAssetsZip);
    return steps.build();
  }

  public Path getPathToStringAssetsZip() {
    return getPathToStringAssetsDir().resolve("string_assets.zip");
  }

  public Sha1HashCode getStringAssetsZipHash() {
    return buildOutputInitializer.getBuildOutput().stringAssetsZipHash;
  }

  @Override
  public BuildOutput initializeFromDisk(OnDiskBuildInfo onDiskBuildInfo) {
    return new BuildOutput(onDiskBuildInfo.getHash(STRING_ASSETS_ZIP_HASH).get());
  }

  @Override
  public BuildOutputInitializer<BuildOutput> getBuildOutputInitializer() {
    return buildOutputInitializer;
  }

  public static class BuildOutput {
    private final Sha1HashCode stringAssetsZipHash;

    public BuildOutput(Sha1HashCode stringAssetsZipHash) {
      this.stringAssetsZipHash = stringAssetsZipHash;
    }
  }

  @Override
  public RuleKey.Builder appendDetailsToRuleKey(RuleKey.Builder builder) {
    return builder;
  }

  @Nullable
  @Override
  public Path getPathToOutputFile() {
    return null;
  }

  private Path getPathToStringAssetsDir() {
    return BuildTargets.getBinPath(getBuildTarget(), "__strings_%s__");
  }
}

@@ -3617,24 +3617,82 @@
 ssetsDir();%0A
+    steps.add(new MakeCleanDirectoryStep(pathToBaseDir));%0A
     Path pat

Clean the directory containing the zip of string assets so that ZipStep does not complain.