/*
 * Copyright 2012-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.cli;

import com.facebook.buck.graph.AbstractBottomUpTraversal;
import com.facebook.buck.json.BuildFileParseException;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetException;
import com.facebook.buck.parser.ParseContext;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.TargetGraph;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeMultimap;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Set;

public class AuditInputCommand extends AbstractCommandRunner<AuditCommandOptions> {

  public AuditInputCommand(CommandRunnerParams params) {
    super(params);
  }

  @Override
  AuditCommandOptions createOptions(BuckConfig buckConfig) {
    return new AuditCommandOptions(buckConfig);
  }

  @Override
  int runCommandWithOptionsInternal(AuditCommandOptions options)
      throws IOException, InterruptedException {
    // Create a TargetGraph that is composed of the transitive closure of all of the dependent
    // TargetNodes for the specified BuildTargets.
    final ImmutableSet<String> fullyQualifiedBuildTargets = ImmutableSet.copyOf(
        options.getArgumentsFormattedAsBuildTargets());

    if (fullyQualifiedBuildTargets.isEmpty()) {
      console.printBuildFailure("Please specify at least one build target.");
      return 1;
    }

    ImmutableSet<BuildTarget> targets = FluentIterable
        .from(options.getArgumentsFormattedAsBuildTargets())
        .transform(new Function<String, BuildTarget>() {
                     @Override
                     public BuildTarget apply(String input) {
                       return getParser().getBuildTargetParser().parse(
                           input,
                           ParseContext.fullyQualified());
                     }
                   })
        .toSet();

    TargetGraph graph;
    try {
      graph = getParser().buildTargetGraphForBuildTargets(
          targets,
          options.getDefaultIncludes(),
          getBuckEventBus(),
          console,
          environment,
          options.getEnableProfiling());
    } catch (BuildTargetException | BuildFileParseException e) {
      console.printBuildFailureWithoutStacktrace(e);
      return 1;
    }

    if (options.shouldGenerateJsonOutput()) {
      return printJsonInputs(graph);
    }
    return printInputs(graph);
  }

  @VisibleForTesting
  int printJsonInputs(TargetGraph graph) throws IOException {
    final Multimap<String, String> targetInputs = TreeMultimap.create();

    new AbstractBottomUpTraversal<BuildRule, Void>(graph.getActionGraph()) {

      @Override
      public void visit(BuildRule rule) {
        for (Path input : rule.getInputs()) {
          // TODO(user) remove `toString` once Jackson supports serializing Path instances
          targetInputs.put(rule.getFullyQualifiedName(), input.toString());
        }
      }

      @Override
      public Void getResult() {
       return null;
      }

    }.traverse();

    // Note: using `asMap` here ensures that the keys are sorted
    getObjectMapper().writeValue(
        console.getStdOut(),
        targetInputs.asMap());

    return 0;
  }

  private int printInputs(TargetGraph graph) {
    // Traverse the TargetGraph and print out all of the inputs used to produce each TargetNode.
    // Keep track of the inputs that have been displayed to ensure that they are not displayed more
    // than once.
    new AbstractBottomUpTraversal<BuildRule, Void>(graph.getActionGraph()) {

      final Set<Path> inputs = Sets.newHashSet();

      @Override
      public void visit(BuildRule rule) {
        for (Path input : rule.getInputs()) {
          boolean isNewInput = inputs.add(input);
          if (isNewInput) {
            getStdOut().println(input);
          }
        }
      }

      @Override
      public Void getResult() {
        return null;
      }

    }.traverse();

    return 0;
  }

  @Override
  String getUsageIntro() {
    return "provides facilities to audit build targets' input files";
  }

}

@@ -919,25 +919,27 @@
 k.rules.
-BuildRule
+TargetGraph
 ;%0Aimport
@@ -961,37 +961,36 @@
 uck.rules.Target
-Graph
+Node
 ;%0Aimport com.goo
@@ -3445,47 +3445,34 @@
 sal%3C
-BuildRule, Void%3E(graph.getActionGraph()
+TargetNode%3C?%3E, Void%3E(graph
 ) %7B%0A
@@ -3508,37 +3508,41 @@
  void visit(
-BuildRule rul
+TargetNode%3C?%3E nod
 e) %7B%0A       
@@ -3552,35 +3552,35 @@
 r (Path input : 
-rul
+nod
 e.getInputs()) %7B
@@ -3698,20 +3698,37 @@
 uts.put(
-rule
+node.getBuildTarget()
 .getFull
@@ -4358,47 +4358,34 @@
 sal%3C
-BuildRule, Void%3E(graph.getActionGraph()
+TargetNode%3C?%3E, Void%3E(graph
 ) %7B%0A
@@ -4480,21 +4480,25 @@
 sit(
-BuildRule rul
+TargetNode%3C?%3E nod
 e) %7B
@@ -4524,19 +4524,19 @@
 input : 
-rul
+nod
 e.getInp

Change AuditInputCommand to use target graph.