package io.freefair.gradle.plugins.aspectj;

import io.freefair.gradle.plugins.aspectj.internal.AspectJCompileSpec;
import io.freefair.gradle.plugins.aspectj.internal.AspectJCompiler;
import lombok.AccessLevel;
import lombok.Getter;
import org.gradle.api.Action;
import org.gradle.api.NonNullApi;
import org.gradle.api.Task;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.ClasspathNormalizer;
import org.gradle.api.tasks.compile.AbstractCompile;
import org.gradle.process.internal.JavaExecHandleFactory;

import javax.inject.Inject;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;

import static io.freefair.gradle.util.TaskUtils.*;

/**
 * @author Lars Grefer
 * @see AspectJPostCompileWeavingPlugin
 */
@Getter
@NonNullApi
public class AjcAction implements Action<Task> {

    @Getter(AccessLevel.NONE)
    private final ProjectLayout projectLayout;
    @Getter(AccessLevel.NONE)
    private final JavaExecHandleFactory javaExecHandleFactory;

    private final ConfigurableFileCollection classpath;

    private final Property<Boolean> enabled;

    private final AspectJCompileOptions options;

    private final ConfigurableFileCollection additionalInpath;

    public void options(Action<AspectJCompileOptions> action) {
        action.execute(getOptions());
    }

    @Inject
    public AjcAction(ProjectLayout projectLayout, ObjectFactory objectFactory, JavaExecHandleFactory javaExecHandleFactory) {
        this.projectLayout = projectLayout;
        options = new AspectJCompileOptions(objectFactory);
        classpath = objectFactory.fileCollection();
        additionalInpath = objectFactory.fileCollection();

        enabled = objectFactory.property(Boolean.class).convention(true);
        this.javaExecHandleFactory = javaExecHandleFactory;
    }

    @SuppressWarnings("WeakerAccess")
    public void addToTask(Task task) {
        task.doLast("ajc", this);
        task.getExtensions().add("ajc", this);

        task.getInputs().files(this.getClasspath())
                .withPropertyName("aspectjClasspath")
                .withNormalizer(ClasspathNormalizer.class)
                .optional(false);

        task.getInputs().property("ajcEnabled", this.getEnabled())
                .optional(true);

        try {
            registerNested(task, AspectJCompileOptions.class, this.getOptions(), "ajcOptions");
        } catch (InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void execute(Task task) {
        if (!enabled.getOrElse(true)) {
            return;
        }

        AspectJCompileSpec spec = createSpec((AbstractCompile) task);

        new AspectJCompiler(javaExecHandleFactory).execute(spec);
    }

    private AspectJCompileSpec createSpec(AbstractCompile compile) {
        AspectJCompileSpec spec = new AspectJCompileSpec();

        spec.setDestinationDir(compile.getDestinationDirectory().get().getAsFile());
        spec.setWorkingDir(projectLayout.getProjectDirectory().getAsFile());
        spec.setTempDir(compile.getTemporaryDir());
        spec.setCompileClasspath(new ArrayList<>(compile.getClasspath().filter(File::exists).getFiles()));
        spec.setTargetCompatibility(compile.getTargetCompatibility());
        spec.setSourceCompatibility(compile.getSourceCompatibility());

        spec.setAspectJClasspath(getClasspath());
        spec.setAspectJCompileOptions(getOptions());
        spec.setAdditionalInpath(getAdditionalInpath());

        return spec;
    }
}

@@ -658,16 +658,75 @@
 Factory;
+%0Aimport org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile;
 %0A%0Aimport
@@ -2899,26 +2899,8 @@
 pec(
-(AbstractCompile) 
 task
@@ -3018,31 +3018,20 @@
 ateSpec(
-AbstractCompile
+Task
  compile
@@ -3091,24 +3091,151 @@
 ileSpec();%0A%0A
+        if (compile instanceof AbstractCompile) %7B%0A            AbstractCompile abstractCompile = (AbstractCompile) compile;%0A    
         spec
@@ -3249,25 +3249,33 @@
 tinationDir(
-c
+abstractC
 ompile.getDe
@@ -3323,24 +3323,28 @@
         
+    
 spec.set
 WorkingD
@@ -3339,121 +3339,517 @@
 .set
-WorkingDir(projectLayout.getProjectDirectory().getAsFile());%0A        spec.setTempDir(compile.getTemporaryDir
+CompileClasspath(new ArrayList%3C%3E(abstractCompile.getClasspath().filter(File::exists).getFiles()));%0A            spec.setTargetCompatibility(abstractCompile.getTargetCompatibility());%0A            spec.setSourceCompatibility(abstractCompile.getSourceCompatibility());%0A        %7D%0A        else if (compile instanceof KotlinJvmCompile) %7B%0A            KotlinJvmCompile kotlinJvmCompile = (KotlinJvmCompile) compile;%0A            spec.setDestinationDir(kotlinJvmCompile.getDestinationDirectory().get().getAsFile
 ());%0A
+    
     
@@ -3893,17 +3893,26 @@
 yList%3C%3E(
-c
+kotlinJvmC
 ompile.g
@@ -3909,33 +3909,33 @@
 mCompile.get
-Classpath
+Libraries
 ().filter(Fi
@@ -3960,32 +3960,36 @@
 es()));%0A        
+ 
+   
 spec.setTargetCo
@@ -4004,109 +4004,196 @@
 ity(
-compile.getTargetCompatibility());%0A        spec.setSourceCompatibility(compile.getSourceCompatibility
+kotlinJvmCompile.getKotlinOptions().getJvmTarget());%0A        %7D%0A%0A        spec.setWorkingDir(projectLayout.getProjectDirectory().getAsFile());%0A        spec.setTempDir(compile.getTemporaryDir
 ());

Support Kotlin 1.7.0