/*
 * Copyright 2015 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package nebula.plugin.metrics;

import nebula.plugin.metrics.collector.GradleBuildCollector;
import nebula.plugin.metrics.collector.GradleProfileCollector;
import nebula.plugin.metrics.collector.GradleTestSuiteCollector;
import nebula.plugin.metrics.collector.LogbackCollector;
import nebula.plugin.metrics.dispatcher.DispatcherLifecycleListener;
import nebula.plugin.metrics.dispatcher.ESClientMetricsDispatcher;
import nebula.plugin.metrics.dispatcher.MetricsDispatcher;

import com.google.common.annotations.VisibleForTesting;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.internal.GradleInternal;
import org.gradle.api.invocation.Gradle;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.testing.Test;
import org.gradle.initialization.ClassLoaderRegistry;
import org.gradle.internal.classloader.FilteringClassLoader;
import org.gradle.invocation.DefaultGradle;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * Gradle build metrics plugin.
 *
 * @author Danny Thomas
 */
public final class MetricsPlugin implements Plugin<Project> {
    private MetricsDispatcher dispatcher;

    @Override
    public void apply(Project project) {
        checkNotNull(project);
        checkState(project == project.getRootProject(), "The metrics plugin may only be applied to the root project");
        allowLogbackClassLoading(project);
        ExtensionContainer extensions = project.getExtensions();
        extensions.add("metrics", new MetricsPluginExtension());
        MetricsPluginExtension extension = extensions.getByType(MetricsPluginExtension.class);
        dispatcher = new ESClientMetricsDispatcher(extension);
        project.afterEvaluate(new Action<Project>() {
            @Override
            public void execute(Project project) {
                configureCollectors(project);
            }
        });
    }

    @VisibleForTesting
    void setDispatcher(MetricsDispatcher dispatcher) {
        this.dispatcher = checkNotNull(dispatcher);
    }

    private void allowLogbackClassLoading(Project project) {
        GradleInternal gradle = (DefaultGradle) project.getGradle();
        ClassLoaderRegistry registry = gradle.getServices().get(ClassLoaderRegistry.class);
        FilteringClassLoader classLoader = (FilteringClassLoader) registry.getGradleApiClassLoader().getParent();
        classLoader.allowPackage("ch.qos.logback");
    }

    private void configureCollectors(Project rootProject) {
        LogbackCollector.configureLogbackCollection(dispatcher);

        Gradle gradle = rootProject.getGradle();
        gradle.addListener(new DispatcherLifecycleListener(dispatcher));
        gradle.addListener(new GradleBuildCollector(dispatcher));
        gradle.addListener(new GradleProfileCollector(dispatcher));

        for (Project project : rootProject.getAllprojects()) {
            TaskContainer tasks = project.getTasks();
            for (String name : tasks.getNames()) {
                Task task = tasks.getByName(name);
                if (task instanceof Test) {
                    GradleTestSuiteCollector suiteCollector = new GradleTestSuiteCollector(dispatcher, (Test) task);
                    ((Test) task).addTestListener(suiteCollector);
                }
            }
        }
    }
}

@@ -1624,16 +1624,39 @@
 radle;%0A%0A
+import java.util.Set;%0A%0A
 import s
@@ -2484,16 +2484,71 @@
 nsion);%0A
+        configureBuildCollectors(project.getGradle());%0A
         
@@ -2683,32 +2683,39 @@
        configure
+Project
 Collectors(proje
@@ -2708,32 +2708,49 @@
 llectors(project
+.getAllprojects()
 );%0A            %7D
@@ -3327,16 +3327,21 @@
 onfigure
+Build
 Collecto
@@ -3347,27 +3347,21 @@
 ors(
-Project rootProject
+Gradle gradle
 ) %7B%0A
@@ -3428,58 +3428,8 @@
 er);
-%0A%0A        Gradle gradle = rootProject.getGradle();
 %0A   
@@ -3636,37 +3636,79 @@
 ));%0A
-%0A
     
+%7D%0A%0A
     
-for (
+private void configureProjectCollectors(Set%3C
 Project
+%3E
  project
  : r
@@ -3707,37 +3707,52 @@
 ject
- : rootProject.getAll
+s) %7B%0A        for (Project project : 
 projects
 ()) 
@@ -3747,18 +3747,16 @@
 projects
-()
 ) %7B%0A    

Improve ordering of build vs project collectors