/*
 * 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.tools.gradle.compiler;

import io.spine.tools.compiler.descriptor.FileDescriptorSuperset;
import io.spine.tools.gradle.ConfigurationName;
import io.spine.tools.gradle.SpinePlugin;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;

import java.io.File;

import static io.spine.tools.gradle.ConfigurationName.RUNTIME_CLASSPATH;
import static io.spine.tools.gradle.ConfigurationName.TEST_RUNTIME_CLASSPATH;
import static io.spine.tools.gradle.TaskName.GENERATE_PROTO;
import static io.spine.tools.gradle.TaskName.GENERATE_TEST_PROTO;
import static io.spine.tools.gradle.TaskName.MERGE_DESCRIPTOR_SET;
import static io.spine.tools.gradle.TaskName.MERGE_TEST_DESCRIPTOR_SET;
import static io.spine.tools.gradle.compiler.Extension.getMainDescriptorSetPath;
import static io.spine.tools.gradle.compiler.Extension.getTestDescriptorSetPath;

/**
 * A Gradle plugin which merges the descriptor file with all the descriptor files from
 * the project runtime classpath.
 *
 * <p>The merge result is used to {@linkplain
 * io.spine.tools.type.MoreKnownTypes#extendWith(java.io.File) extend the known type registry}.
 */
public class DescriptorSetLoaderPlugin extends SpinePlugin {

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

    private void createMainTask(Project project) {
        newTask(MERGE_DESCRIPTOR_SET,
                createMergingAction(configuration(project, RUNTIME_CLASSPATH),
                                    getMainDescriptorSetPath(project)))
                .insertAfterTask(GENERATE_PROTO)
                .applyNowTo(project);
    }

    private void createTestTask(Project project) {
        newTask(MERGE_TEST_DESCRIPTOR_SET,
                createMergingAction(configuration(project, TEST_RUNTIME_CLASSPATH),
                                    getTestDescriptorSetPath(project)))
                .insertAfterTask(GENERATE_TEST_PROTO)
                .applyNowTo(project);
    }

    private static Action<Task> createMergingAction(Configuration configuration,
                                                    String descriptorSetPath) {
        return task -> {
            File descriptorSet = new File(descriptorSetPath);
            FileDescriptorSuperset superset = new FileDescriptorSuperset();
            configuration.forEach(superset::addFromDependency);
            if (descriptorSet.exists()) {
                superset.addFromDependency(descriptorSet);
            }
            superset.loadIntoKnownTypes();
        };
    }

    private static Configuration configuration(Project project, ConfigurationName name) {
        return project.getConfigurations()
                      .getByName(name.getValue());
    }
}

@@ -2494,32 +2494,16 @@
 TOR_SET,
-%0A               
  createM
@@ -2519,120 +2519,13 @@
 ion(
-configuration(project, RUNTIME_CLASSPATH),%0A                                    getMainDescriptorSetPath(project)
+false
 ))%0A 
@@ -2710,32 +2710,16 @@
 TOR_SET,
-%0A               
  createM
@@ -2735,125 +2735,12 @@
 ion(
-configuration(project, TEST_RUNTIME_CLASSPATH),%0A                                    getTestDescriptorSetPath(project)
+true
 ))%0A 
@@ -2893,141 +2893,190 @@
 ion(
-Configuration configuration,%0A                                                    String descriptorSetPath) %7B%0A        return task -%3E %7B
+boolean tests) %7B%0A        return task -%3E %7B%0A            Project project = task.getProject();%0A            Configuration configuration = configuration(project, configurationName(tests));
 %0A   
@@ -3109,17 +3109,8 @@
 t = 
-new File(
 desc
@@ -3118,20 +3118,31 @@
 iptorSet
-Path
+(project, tests
 );%0A     
@@ -3641,11 +3641,442 @@
 );%0A    %7D
+%0A%0A    private static ConfigurationName configurationName(boolean tests) %7B%0A        return tests%0A               ? TEST_RUNTIME_CLASSPATH%0A               : RUNTIME_CLASSPATH;%0A    %7D%0A%0A    private static File descriptorSet(Project project, boolean tests) %7B%0A        String path = tests%0A                      ? getTestDescriptorSetPath(project)%0A                      : getMainDescriptorSetPath(project);%0A        return new File(path);%0A    %7D
 %0A%7D%0A

Resolve plugin inputs in the task, not at configuration time.