/*
 * Copyright 2016 DiffPlug
 *
 * 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 com.diffplug.gradle.spotless;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.execution.TaskExecutionGraph;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaBasePlugin;

import com.diffplug.spotless.SpotlessCache;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import groovy.lang.Closure;

public class SpotlessPlugin implements Plugin<Project> {
	SpotlessExtension spotlessExtension;

	static final String EXTENSION = "spotless";
	static final String CHECK = "Check";
	static final String APPLY = "Apply";

	private static final String TASK_GROUP = "Verification";
	private static final String CHECK_DESCRIPTION = "Checks that sourcecode satisfies formatting steps.";
	private static final String APPLY_DESCRIPTION = "Applies code formatting steps to sourcecode in-place.";

	@Override
	public void apply(Project project) {
		// make sure there's a `clean` task
		project.getPlugins().apply(BasePlugin.class);

		// setup the extension
		spotlessExtension = project.getExtensions().create(EXTENSION, SpotlessExtension.class, project);

		// after the project has been evaluated, configure the check and format tasks per source set
		project.afterEvaluate(this::createTasks);
	}

	/** The extension for this plugin. */
	public SpotlessExtension getExtension() {
		return spotlessExtension;
	}

	@SuppressWarnings("rawtypes")
	void createTasks(Project project) {
		Task rootCheckTask = project.task(EXTENSION + CHECK);
		rootCheckTask.setGroup(TASK_GROUP);
		rootCheckTask.setDescription(CHECK_DESCRIPTION);
		Task rootApplyTask = project.task(EXTENSION + APPLY);
		rootApplyTask.setGroup(TASK_GROUP);
		rootApplyTask.setDescription(APPLY_DESCRIPTION);

		spotlessExtension.formats.forEach((key, value) -> {
			// create the task that does the work
			String taskName = EXTENSION + capitalize(key);
			SpotlessTask spotlessTask = project.getTasks().create(taskName, SpotlessTask.class);
			value.setupTask(spotlessTask);

			// create the check and apply control tasks
			Task checkTask = project.getTasks().create(taskName + CHECK);
			Task applyTask = project.getTasks().create(taskName + APPLY);
			// the root tasks depend on them
			rootCheckTask.dependsOn(checkTask);
			rootApplyTask.dependsOn(applyTask);
			// and they depend on the work task
			checkTask.dependsOn(spotlessTask);
			applyTask.dependsOn(spotlessTask);

			// when the task graph is ready, we'll configure the spotlessTask appropriately
			project.getGradle().getTaskGraph().whenReady(new Closure(null) {
				private static final long serialVersionUID = 1L;

				// called by gradle
				@SuppressFBWarnings("UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS")
				public Object doCall(TaskExecutionGraph graph) {
					if (graph.hasTask(checkTask)) {
						spotlessTask.setCheck();
					}
					if (graph.hasTask(applyTask)) {
						spotlessTask.setApply();
					}
					return Closure.DONE;
				}
			});
		});

		// Add our check task as a dependency on the global check task
		// getTasks() returns a "live" collection, so this works even if the
		// task doesn't exist at the time this call is made
		if (spotlessExtension.enforceCheck) {
			project.getTasks()
					.matching(task -> task.getName().equals(JavaBasePlugin.CHECK_TASK_NAME))
					.all(task -> task.dependsOn(rootCheckTask));
		}

		// clear spotless' cache when the user does a clean, but only after any spotless tasks
		Task clean = project.getTasks().getByName(BasePlugin.CLEAN_TASK_NAME);
		clean.doLast(unused -> SpotlessCache.clear());
		project.getTasks()
				.matching(task -> task instanceof SpotlessTask)
				.all(task -> task.mustRunAfter(clean));
	}

	static String capitalize(String input) {
		return Character.toUpperCase(input.charAt(0)) + input.substring(1);
	}
}

@@ -4198,41 +4198,17 @@
 %09%09%09.
-matching(task -%3E task instanceof 
+withType(
 Spot
@@ -4215,16 +4215,22 @@
 lessTask
+.class
 )%0A%09%09%09%09.a

Use `withType` instead of `matching(task -> task instanceof`