/*
 * Copyright (c) 2014 by Stefan Ferstl <st.ferstl@gmail.com>
 *
 * 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.github.ferstl.depgraph;

import java.util.ArrayDeque;
import java.util.Deque;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import com.github.ferstl.depgraph.dot.DotBuilder;
import com.github.ferstl.depgraph.dot.Node;


/**
 * A node visitor that creates edges between the visited nodes using a {@link DotBuilder}. This
 * class implements the {@code DependencyNodeVisitor} interfaces for dependency trees and dependency
 * graphs and adapts the different node instances using {@link DependencyNodeAdapter}.
 */
class DotBuildingVisitor implements org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor, org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor {

  private final DotBuilder dotBuilder;
  private final Deque<Node> stack;
  private final ArtifactFilter globalFilter;
  private final ArtifactFilter targetFilter;


  public DotBuildingVisitor(DotBuilder dotBuilder, ArtifactFilter globalFilter, ArtifactFilter targetFilter) {
    this.dotBuilder = dotBuilder;
    this.stack = new ArrayDeque<>();
    this.globalFilter = globalFilter;
    this.targetFilter = targetFilter;
  }

  public DotBuildingVisitor(DotBuilder dotBuilder, ArtifactFilter targetFilter) {
    this(dotBuilder, DoNothingArtifactFilter.INSTANCE, targetFilter);
  }

  @Override
  public boolean visit(org.apache.maven.shared.dependency.graph.DependencyNode node) {
    return internalVisit(new DependencyNodeAdapter(node));
  }

  @Override
  public boolean endVisit(org.apache.maven.shared.dependency.graph.DependencyNode node) {
    return internalEndVisit(new DependencyNodeAdapter(node));
  }

  @Override
  public boolean visit(org.apache.maven.shared.dependency.tree.DependencyNode node) {
    return internalVisit(new DependencyNodeAdapter(node));
  }

  @Override
  public boolean endVisit(org.apache.maven.shared.dependency.tree.DependencyNode node) {
    return internalEndVisit(new DependencyNodeAdapter(node));
  }

  private boolean internalVisit(DependencyNodeAdapter node) {
    Node currentParent = this.stack.peek();

    if (this.globalFilter.include(node.getArtifact()) && leadsToTargetDependencies(node)) {
      if (currentParent != null) {
        this.dotBuilder.addEdge(currentParent, node);
      }

      this.stack.push(node);

      return true;
    }

    return false;
  }

  private boolean leadsToTargetDependencies(DependencyNodeAdapter node) {
    if (this.targetFilter.include(node.getArtifact())) {
      return true;
    }

    for (DependencyNodeAdapter c : node.getChildren()) {
      if (leadsToTargetDependencies(c)) {
        return true;
      }
    }

    return false;
  }

  private boolean internalEndVisit(DependencyNodeAdapter node) {
    if (this.globalFilter.include(node.getArtifact()) && leadsToTargetDependencies(node)) {
      this.stack.pop();
    }

    return true;
  }

  private static enum DoNothingArtifactFilter implements ArtifactFilter {
    INSTANCE;

    @Override
    public boolean include(Artifact artifact) {
      return true;
    }

  }
}

@@ -2816,35 +2816,33 @@
 oTargetDependenc
-ies
+y
 (node)) %7B%0A      
@@ -3052,19 +3052,17 @@
 ependenc
-ies
+y
 (Depende
@@ -3256,19 +3256,17 @@
 ependenc
-ies
+y
 (c)) %7B%0A 
@@ -3471,11 +3471,9 @@
 denc
-ies
+y
 (nod

Rename leadsToTargetDependencies -> leadsToTargetDependency