package com.github.ferstl.depgraph;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.artifact.filter.ScopeArtifactFilter;
import org.apache.maven.shared.artifact.filter.StrictPatternExcludesArtifactFilter;
import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
import org.apache.maven.shared.dependency.graph.DependencyNode;

import com.github.ferstl.depgraph.dot.DotBuilder;

@Mojo(
    name = "aggregate",
    aggregator = true,
    defaultPhase = LifecyclePhase.NONE,
    inheritByDefault = false,
    requiresDependencyCollection = ResolutionScope.TEST,
    requiresDirectInvocation = true,
    threadSafe = true)
public class DepGraphMojo extends AbstractMojo {

  @Parameter(property = "scope")
  private String scope;

  @Parameter(property = "includes", defaultValue = "")
  private List<String> includes;

  @Parameter(property = "excludes", defaultValue = "")
  private List<String> excludes;

  @Component
  private MavenProject project;

  @Component( hint = "default" )
  private DependencyGraphBuilder dependencyGraphBuilder;

  @Override
  public void execute() throws MojoExecutionException {
    ArtifactFilter filter = createArtifactFilters();

    try {
      DotBuilder dotBuilder = new DotBuilder(ArtifactIdRenderer.VERSIONLESS_ID, ArtifactIdRenderer.ARTIFACT_ID);

      @SuppressWarnings("unchecked")
      List<MavenProject> collectedProjects = this.project.getCollectedProjects();
      buildModuleTree(collectedProjects, dotBuilder);

      for (MavenProject collectedProject : collectedProjects) {
        DependencyNode root = this.dependencyGraphBuilder.buildDependencyGraph(collectedProject, filter);

        DotBuildingVisitor visitor = new DotBuildingVisitor(dotBuilder);
        root.accept(visitor);
      }

      System.err.println(dotBuilder);

    } catch (DependencyGraphBuilderException e) {
      throw new MojoExecutionException("boom");
    }
  }

  private ArtifactFilter createArtifactFilters() {
    List<ArtifactFilter> filters = new ArrayList<>(3);

    if (this.scope != null) {
      filters.add(new ScopeArtifactFilter(this.scope));
    }

    if (!this.includes.isEmpty()) {
      filters.add(new StrictPatternIncludesArtifactFilter(this.includes));
    }

    if (!this.excludes.isEmpty()) {
      filters.add(new StrictPatternExcludesArtifactFilter(this.excludes));
    }

    return new AndArtifactFilter(filters);
  }

  private void buildModuleTree(Collection<MavenProject> collectedProjects, DotBuilder dotBuilder) {
    // FIXME apply filters here
    for (MavenProject collectedProject : collectedProjects) {
      MavenProject child = collectedProject;
      MavenProject parent = collectedProject.getParent();

      while (parent != null) {
        ArtifactNode parentNode = new ArtifactNode(parent.getArtifact());
        ArtifactNode childNode = new ArtifactNode(child.getArtifact());
        dotBuilder.addEdge(parentNode, childNode);

        child = parent;
        parent = parent.getParent();
      }
    }
  }
}

@@ -30,16 +30,88 @@
 graph;%0A%0A
+import java.io.File;%0Aimport java.io.IOException;%0Aimport java.io.Writer;%0A
 import j
@@ -130,16 +130,16 @@
 ayList;%0A
-
 import j
@@ -1246,16 +1246,91 @@
 Builder;
+%0Aimport com.google.common.base.Charsets;%0Aimport com.google.common.io.Files;
 %0A%0A@Mojo(
@@ -1850,16 +1850,148 @@
 ludes;%0A%0A
+  @Parameter(property = %22outputFile%22, defaultValue = %22$%7Bproject.build.directory%7D/dependency-graph.dot%22)%0A  private File outputFile;%0A%0A
   @Compo
@@ -2818,24 +2818,56 @@
 );%0A      %7D%0A%0A
+      writeDotFile(dotBuilder);%0A
       System
@@ -2938,16 +2938,30 @@
 ception 
+%7C IOException 
 e) %7B%0A   
@@ -3001,13 +3001,294 @@
 on(%22
-boom%22
+Unable to create dependency graph.%22, e);%0A    %7D%0A  %7D%0A%0A  private void writeDotFile(DotBuilder dotBuilder) throws IOException %7B%0A    Files.createParentDirs(this.outputFile);%0A%0A    try(Writer writer = Files.newWriter(this.outputFile, Charsets.UTF_8)) %7B%0A      writer.write(dotBuilder.toString()
 );%0A 

Write graph to file