/*
 * Copyright 2013-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.jvm.java;

import com.facebook.buck.cxx.CxxPlatform;
import com.facebook.buck.cxx.NativeLinkable;
import com.facebook.buck.graph.AbstractBreadthFirstThrowingTraversal;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.OnDiskBuildInfo;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MkdirStep;
import com.facebook.buck.util.MoreCollectors;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.hash.HashCode;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;

/**
 * Common utilities for working with {@link JavaLibrary} objects.
 */
public class JavaLibraryRules {

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

  static void addAccumulateClassNamesStep(
      JavaLibrary javaLibrary,
      BuildableContext buildableContext,
      SourcePathResolver pathResolver,
      ImmutableList.Builder<Step> steps) {

    Path pathToClassHashes = JavaLibraryRules.getPathToClassHashes(
        javaLibrary.getBuildTarget(), javaLibrary.getProjectFilesystem());
    steps.add(MkdirStep.of(javaLibrary.getProjectFilesystem(), pathToClassHashes.getParent()));
    steps.add(
        new AccumulateClassNamesStep(
            javaLibrary.getProjectFilesystem(),
            Optional.ofNullable(javaLibrary.getSourcePathToOutput())
                .map(pathResolver::getRelativePath),
            pathToClassHashes));
    buildableContext.recordArtifact(pathToClassHashes);
  }

  static JavaLibrary.Data initializeFromDisk(
      BuildTarget buildTarget,
      ProjectFilesystem filesystem,
      OnDiskBuildInfo onDiskBuildInfo)
      throws IOException {
    List<String> lines =
        onDiskBuildInfo.getOutputFileContentsByLine(getPathToClassHashes(buildTarget, filesystem));
    ImmutableSortedMap<String, HashCode> classHashes = AccumulateClassNamesStep.parseClassHashes(
        lines);

    return new JavaLibrary.Data(classHashes);
  }

  private static Path getPathToClassHashes(BuildTarget buildTarget, ProjectFilesystem filesystem) {
    return BuildTargets.getGenPath(filesystem, buildTarget, "%s.classes.txt");
  }

  /**
   * @return all the transitive native libraries a rule depends on, represented as
   *     a map from their system-specific library names to their {@link SourcePath} objects.
   */
  public static ImmutableMap<String, SourcePath> getNativeLibraries(
      Iterable<BuildRule> deps,
      final CxxPlatform cxxPlatform) throws NoSuchBuildTargetException {
    final ImmutableMap.Builder<String, SourcePath> libraries = ImmutableMap.builder();

    new AbstractBreadthFirstThrowingTraversal<BuildRule, NoSuchBuildTargetException>(deps) {
      @Override
      public ImmutableSet<BuildRule> visit(BuildRule rule) throws NoSuchBuildTargetException {
        if (rule instanceof NativeLinkable) {
          NativeLinkable linkable = (NativeLinkable) rule;
          libraries.putAll(linkable.getSharedLibraries(cxxPlatform));
        }
        if (rule instanceof NativeLinkable ||
            rule instanceof JavaLibrary) {
          return rule.getBuildDeps();
        } else {
          return ImmutableSet.of();
        }
      }
    }.start();

    return libraries.build();
  }

  public static ImmutableSortedSet<BuildRule> getAbiRules(
      BuildRuleResolver resolver,
      Iterable<BuildRule> inputs) throws NoSuchBuildTargetException {
    ImmutableSortedSet.Builder<BuildRule> abiRules = ImmutableSortedSet.naturalOrder();
    for (BuildRule input : inputs) {
      if (input instanceof HasJavaAbi && ((HasJavaAbi) input).getAbiJar().isPresent()) {
        Optional<BuildTarget> abiJarTarget = ((HasJavaAbi) input).getAbiJar();
        BuildRule abiJarRule = resolver.requireRule(abiJarTarget.get());
        abiRules.add(abiJarRule);
      }
    }
    return abiRules.build();
  }

  public static ImmutableSortedSet<SourcePath> getAbiSourcePaths(
      BuildRuleResolver resolver,
      Iterable<BuildRule> inputs) throws NoSuchBuildTargetException {
    return getAbiRules(resolver, inputs)
        .stream()
        .map(BuildRule::getSourcePathToOutput)
        .collect(MoreCollectors.toImmutableSortedSet());
  }

}

@@ -726,78 +726,9 @@
 able
-;%0Aimport com.facebook.buck.graph.AbstractBreadthFirstThrowingTraversal
+s
 ;%0Aim
@@ -1429,55 +1429,8 @@
 ap;%0A
-import com.google.common.collect.ImmutableSet;%0A
 impo
@@ -3642,531 +3642,96 @@
     
-final ImmutableMap.Builder%3CString, SourcePath%3E libraries = ImmutableMap.builder();%0A%0A    new AbstractBreadthFirstThrowingTraversal%3CBuildRule, NoSuchBuildTargetException%3E(deps) %7B%0A      @Override%0A      public ImmutableSet%3CBuildRule%3E visit(BuildRule rule) throws NoSuchBuildTargetException %7B%0A        if (rule instanceof NativeLinkable) %7B%0A          NativeLinkable linkable = (NativeLinkable) rule;%0A          libraries.putAll(linkable.getSharedLibraries(cxxPlatform));%0A        %7D%0A        if (rule instanceof NativeLinkable %7C%7C%0A    
+return NativeLinkables.getTransitiveSharedLibraries(%0A        cxxPlatform,%0A        deps,%0A
     
@@ -3735,19 +3735,21 @@
        r
-ule
+ -%3E r
  instanc
@@ -3768,164 +3768,8 @@
 ary)
- %7B%0A          return rule.getBuildDeps();%0A        %7D else %7B%0A          return ImmutableSet.of();%0A        %7D%0A      %7D%0A    %7D.start();%0A%0A    return libraries.build()
 ;%0A  

use `NativeLinkables` utility method to gather shared libraries