package org.pitest.maven.report;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.plexus.util.FileUtils;
import org.pitest.aggregate.ReportAggregator;
import org.pitest.functional.FCollection;
import org.pitest.maven.DependencyFilter;
import org.pitest.mutationtest.config.DirectoryResultOutputStrategy;
import org.pitest.mutationtest.config.PluginServices;
import org.pitest.mutationtest.config.UndatedReportDirCreationStrategy;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;

/**
 * Common code for report aggregation mojo.
 */
abstract class AbstractPitAggregationReportMojo extends PitReportMojo {

  private static final String MUTATION_RESULT_FILTER = "target/pit-reports/mutations.xml";
  private static final String LINECOVERAGE_FILTER    = "target/pit-reports/linecoverage.xml";

  /**
   * The projects in the reactor.
   */
  @Parameter(property = "reactorProjects", readonly = true)
  List<MavenProject> reactorProjects;

  /**
   * @return  projects to inspect for report files.
   */
  abstract Collection<MavenProject> findDependencies();

  @Override
  protected void executeReport(final Locale locale)
      throws MavenReportException {
    try {
      final Collection<MavenProject> allProjects = findDependencies();

      final ReportAggregator.Builder reportAggregationBuilder = ReportAggregator
          .builder();

      for (final MavenProject proj : allProjects) {
        addProjectFiles(reportAggregationBuilder, proj);
      }

      final ReportAggregator reportAggregator = reportAggregationBuilder
          .resultOutputStrategy(new DirectoryResultOutputStrategy(
              getReportsDirectory().getAbsolutePath(),
              new UndatedReportDirCreationStrategy()))
          .build();

      reportAggregator.aggregateReport();
    } catch (final Exception e) {
      throw new MavenReportException(e.getMessage(), e);
    }
  }

  private void addProjectFiles(
      final ReportAggregator.Builder reportAggregationBuilder,
      final MavenProject proj) throws IOException, Exception {
    final File projectBaseDir = proj.getBasedir();
    List<File> files = getProjectFilesByFilter(projectBaseDir,
        MUTATION_RESULT_FILTER);
    for (final File file : files) {
      reportAggregationBuilder.addMutationResultsFile(file);
    }
    files = getProjectFilesByFilter(projectBaseDir, LINECOVERAGE_FILTER);
    for (final File file : files) {
      reportAggregationBuilder.addLineCoverageFile(file);
    }
    files = convertToRootDirs(proj.getCompileSourceRoots(),
        proj.getTestCompileSourceRoots());
    for (final File file : files) {
      reportAggregationBuilder.addSourceCodeDirectory(file);
    }
    files = getCompiledDirs(proj);
    for (final File file : files) {
      reportAggregationBuilder.addCompiledCodeDirectory(file);
    }
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  private List<File> convertToRootDirs(final List... directoryLists) {
    final List<String> roots = new ArrayList<>();
    for (final List directoryList : directoryLists) {
      roots.addAll(directoryList);
    }
    return FCollection.map(roots, new Function<String, File>() {
      @Override
      public File apply(final String a) {
        return new File(a);
      }
    });
  }

  private List<File> getProjectFilesByFilter(final File projectBaseDir,
                                             final String filter) throws IOException {
    final List<File> files = FileUtils.getFiles(projectBaseDir, filter, "");
    return files == null ? new ArrayList<>() : files;
  }

  @SuppressWarnings("unchecked")
  private List<File> getCompiledDirs(final MavenProject project)
      throws Exception {
    final List<String> sourceRoots = new ArrayList<>();
    for (final Object artifactObj : FCollection
        .filter(project.getPluginArtifactMap().values(), new DependencyFilter(
            new PluginServices(this.getClass().getClassLoader())))) {

      final Artifact artifact = (Artifact) artifactObj;
      sourceRoots.add(artifact.getFile().getAbsolutePath());
    }
    return convertToRootDirs(project.getTestClasspathElements(),
        Arrays.asList(project.getBuild().getOutputDirectory(),
            project.getBuild().getTestOutputDirectory()),
        sourceRoots);
  }
}

@@ -967,16 +967,101 @@
  String 
+REPORT_DIR_RELATIVE_TO_PROJECT = %22target/pit-reports%22;%0A  private static final String 
 MUTATION
@@ -1078,35 +1078,16 @@
 LTER = %22
-target/pit-reports/
 mutation
@@ -1148,33 +1148,11 @@
 TER 
-   = %22target/pit-reports/
+= %22
 line
@@ -1312,16 +1312,96 @@
 jects;%0A%0A
+  private ReportSourceLocator reportSourceLocator = new ReportSourceLocator();%0A%0A
   /**%0A  
@@ -3853,32 +3853,278 @@
 s IOException %7B%0A
+%0A    File reportsDir = projectBaseDir.toPath().resolve(REPORT_DIR_RELATIVE_TO_PROJECT).toFile();%0A    if (!reportsDir.exists()) %7B%0A      return new ArrayList%3C%3E();%0A    %7D%0A%0A    File latestReportDir = reportSourceLocator.locate(reportsDir, getLog());%0A%0A
     final List%3CF
@@ -4155,27 +4155,28 @@
 etFiles(
-projectBase
+latestReport
 Dir, fil

Allow aggregation of timestamped reports