/*
 * Copyright 2018, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.tools.gradle.compiler;

import io.spine.tools.gradle.SpinePlugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileTreeElement;
import org.gradle.api.specs.Spec;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.compile.JavaCompile;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableMap.of;
import static io.spine.tools.gradle.TaskName.COMPILE_JAVA;
import static io.spine.tools.gradle.TaskName.GENERATE_PROTO;

/**
 * @author Dmytro Dashenkov
 */
public class CompileConfiguration extends SpinePlugin {

    @Override
    public void apply(Project project) {
        configureGeneratedJavaCompilation(project);
    }

    private static void configureGeneratedJavaCompilation(Project project) {
        Task generatedCompile = createCompilationTask(project);
        configureStandardCompile(project, generatedCompile);
    }

    private static Task createCompilationTask(Project project) {
        JavaCompile task = (JavaCompile) project.task(of("type", JavaCompile.class),
                                                      "compileGeneratedJava");
        task.dependsOn(GENERATE_PROTO.getValue());
        SourceSet mainJava = project.getExtensions()
                                    .getByType(SourceSetContainer.class)
                                    .findByName("main");
        checkNotNull(mainJava);
        task.source(mainJava);
        task.include(GeneratedJavaFile.in(project));
        task.setDestinationDir(project.getBuildDir()
                                      .toPath()
                                      .resolve("generatedClasses")
                                      .toFile());
        return task;
    }

    private static void configureStandardCompile(Project project, Task compileGenerated) {
        Task standardCompile = project.getTasks()
                                      .findByName(COMPILE_JAVA.getValue());
        checkNotNull(standardCompile);
        standardCompile.dependsOn(compileGenerated);

        JavaCompile standardJavaCompile = (JavaCompile) standardCompile;
        standardJavaCompile.exclude(GeneratedJavaFile.in(project));
        FileCollection generatedClasses = compileGenerated.getOutputs()
                                                          .getFiles();
        standardJavaCompile.getClasspath()
                           .add(generatedClasses);
    }

    private static final class GeneratedJavaFile implements Spec<FileTreeElement> {

        private static Spec<FileTreeElement> in(Project project) {
            return new GeneratedJavaFile();
        }

        @Override
        public boolean isSatisfiedBy(FileTreeElement file) {
        // TODO:2018-06-05:dmytro.dashenkov: Use generated base dir from Extension.
            return file.toString().contains("/generated/");
        }
    }
}

@@ -1179,26 +1179,27 @@
 ile.File
-Collection
+TreeElement
 ;%0Aimport
@@ -1214,36 +1214,44 @@
 dle.api.
-file.FileTreeElement
+plugins.JavaPluginConvention
 ;%0Aimport
@@ -1321,56 +1321,8 @@
 et;%0A
-import org.gradle.api.tasks.SourceSetContainer;%0A
 impo
@@ -1367,16 +1367,38 @@
 mpile;%0A%0A
+import java.io.File;%0A%0A
 import s
@@ -2328,24 +2328,25 @@
 lue());%0A
+%0A
         
 SourceSe
@@ -2341,28 +2341,80 @@
     
-SourceSet mainJava =
+JavaPluginConvention convention =%0A                (JavaPluginConvention)
  pro
@@ -2425,94 +2425,200 @@
 .get
-Extensions()%0A                                    .getByType(SourceSetContainer.class)%0A
+Convention()%0A                                              .getPlugins()%0A                                              .get(%22java%22);%0A        SourceSet mainJava = convention.getSourceSets()%0A   
     
@@ -2714,28 +2714,34 @@
     
-task.
+Object%5B%5D 
 source
-(
+ = 
 mainJava
 );%0A 
@@ -2740,117 +2740,30 @@
 Java
-);%0A        task.include(GeneratedJavaFile.in(project));%0A        task.setDestinationDir(project.getBuildDir()%0A
+.getAllJava()%0A        
     
@@ -2788,27 +2788,17 @@
     
-            .toPath
+.getFiles
 ()%0A 
@@ -2834,88 +2834,212 @@
     
-    .resolve(%22generatedClasses%22)%0A                                      .toFile()
+.toArray();%0A        task.source(source);%0A        task.include(GeneratedJavaFile.in(project));%0A        File generatedClasses = generatedClassesRoot(project);%0A        task.setDestinationDir(generatedClasses
 );%0A 
@@ -3532,54 +3532,212 @@
 File
-Collection generatedClasses = compileG
+ generatedClasses = generatedClassesRoot(project);%0A        project.getDependencies()%0A               .add(%22compile%22, project.fileTree(of(%22dir%22, generatedClasses)));%0A    %7D%0A%0A    private static File g
 enerated
 .get
@@ -3736,185 +3736,241 @@
 ated
-.getOutputs()%0A                                                          .getFiles();%0A        standardJavaCompile.getClasspath()%0A                           .add(generatedClasses)
+ClassesRoot(Project project) %7B%0A        File result = project.getBuildDir()%0A                             .toPath()%0A                             .resolve(%22generatedClasses%22)%0A                             .toFile();%0A        return result
 ;%0A  

Fix compilation config.