/*
 * Copyright 2019, 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.generate.dart;

import com.google.common.collect.ImmutableMap;
import io.spine.tools.gradle.SpinePlugin;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.tasks.Copy;

import java.io.File;

import static io.spine.tools.gradle.ProtoDartTaskName.copyGeneratedDart;
import static io.spine.tools.gradle.ProtoDartTaskName.copyTestGeneratedDart;
import static io.spine.tools.gradle.ProtobufTaskName.generateProto;
import static io.spine.tools.gradle.ProtobufTaskName.generateTestProto;
import static io.spine.tools.gradle.ProtocPluginName.dart;
import static io.spine.tools.gradle.SourceScope.main;
import static io.spine.tools.gradle.SourceScope.test;
import static org.gradle.api.Task.TASK_TYPE;

/**
 * A Gradle plugin which configures Protobuf Dart code generation.
 *
 * <p>Generates mapping between Protobuf type URLs and Dart types and reflective descriptors
 * (a.k.a. {@code BuilderInfo}s).
 *
 * @see DartProtocConfigurationPlugin
 */
public final class ProtoDartPlugin extends SpinePlugin {

    @Override
    public void apply(Project project) {
        Extension extension = new Extension(project);
        extension.register();

        Plugin<Project> protocConfig = new DartProtocConfigurationPlugin();
        protocConfig.apply(project);

        createMainCopyTask(project, extension);
        createTestCopyTask(project, extension);
    }

    private static void createMainCopyTask(Project project, Extension extension) {
        Copy task = (Copy) project.task(ImmutableMap.of(TASK_TYPE, Copy.class),
                                        copyGeneratedDart.name());
        task.from(extension.getGeneratedBaseDir()
                           .dir(main.name() + File.separator + dart.name()));
        task.into(extension.getLibDir());
        task.dependsOn(generateProto.name());
    }

    private static void createTestCopyTask(Project project, Extension extension) {
        Copy task = (Copy) project.task(ImmutableMap.of(TASK_TYPE, Copy.class),
                                        copyTestGeneratedDart.name());
        task.from(extension.getGeneratedBaseDir()
                           .dir(test.name() + File.separator + dart.name()));
        task.into(extension.getTestDir());
        task.dependsOn(generateTestProto.name());
    }
}

@@ -1115,27 +1115,156 @@
 .gradle.
-SpinePlugin
+ProtoDartTaskName;%0Aimport io.spine.tools.gradle.SourceScope;%0Aimport io.spine.tools.gradle.SpinePlugin;%0Aimport io.spine.tools.gradle.TaskName
 ;%0Aimport
@@ -1318,16 +1318,62 @@
 roject;%0A
+import org.gradle.api.file.DirectoryProperty;%0A
 import o
@@ -1421,16 +1421,75 @@
 .File;%0A%0A
+import static io.spine.tools.gradle.BaseTaskName.assemble;%0A
 import s
@@ -2739,196 +2739,367 @@
     
+create
 Copy
- task = (Copy) project.task(ImmutableMap.of(TASK_TYPE, Copy.class),%0A             
+Task(project, extension, main);%0A    %7D%0A%0A    private static void createTestCopyTask(Project project, Extension extension) %7B%0A        createCopyTask(project, extension, test);%0A
     
+%7D%0A%0A
     
-                   copyGeneratedDart.name());%0A        task.from(extension.getGeneratedBaseDir()
+private static void createCopyTask(Project project, Extension extension, SourceScope scope) %7B%0A        ProtoDartTaskName taskName;%0A        DirectoryProperty targetDir;
 %0A   
@@ -3107,86 +3107,109 @@
     
-                   .dir(main.name() + File.separator + dart.name()));%0A
+TaskName runAfter;%0A        if (scope == main) %7B%0A            taskName = copyGeneratedDart;%0A   
         
 task
@@ -3200,34 +3200,37 @@
 %0A           
+ 
 ta
-sk.into(
+rgetDir = 
 extension.ge
@@ -3234,25 +3234,24 @@
 .getLibDir()
-)
 ;%0A        ta
@@ -3252,135 +3252,200 @@
     
-task.dependsOn(generateProto.name());%0A    %7D%0A%0A    private static void createTestCopyTask(Project project, Extension extension) %7B
+    runAfter = generateProto;%0A        %7D else %7B%0A            taskName = copyTestGeneratedDart;%0A            targetDir = extension.getTestDir();%0A            runAfter = generateTestProto;%0A        %7D
 %0A   
@@ -3524,70 +3524,17 @@
 ss),
-%0A                                        copyTestGeneratedDart
+ taskName
 .nam
@@ -3621,20 +3621,21 @@
    .dir(
-test
+scope
 .name() 
@@ -3686,38 +3686,25 @@
 sk.into(
-extension.getTes
+targe
 tDir
-()
 );%0A     
@@ -3725,32 +3725,139 @@
 sOn(
-generateTestProto.name()
+runAfter.name());%0A        project.getTasks()%0A               .getByName(assemble.name())%0A               .dependsOn(copyGeneratedDart
 );%0A 

Make copy Dart tasks run on `assemble`