/*******************************************************************************
 * Copyright (c) 2022 Microsoft Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Microsoft Corporation - initial API and implementation
 *******************************************************************************/
import org.gradle.api.Project
import org.gradle.api.Plugin
import org.gradle.api.file.FileCollection
import org.gradle.api.plugins.PluginContainer
import org.gradle.api.tasks.compile.CompileOptions
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.gradle.util.GradleVersion

import javax.inject.Inject

class GradleAnnotationProcessorPatchPlugin implements Plugin<Project> {

	private final ToolingModelBuilderRegistry registry

	@Inject
	GradleAnnotationProcessorPatchPlugin(ToolingModelBuilderRegistry registry) {
		this.registry = registry
	}

	@Override
	void apply(Project project) {
		this.registry.register(new AnnotationProcessorModelBuilder())
	}

	private static class AnnotationProcessorModelBuilder implements ToolingModelBuilder {

		@Override
		boolean canBuild(String modelName) {
			return "java.util.Map" == modelName
		}

		@Override
		Object buildAll(String modelName, Project rootProject) {
			final GradleVersion current = GradleVersion.current().getBaseVersion()
			if (current < GradleVersion.version("5.2")) {
				return Collections.emptyMap()
			}

			Set<Project> allProject = rootProject.getAllprojects()
			Map<File, Map<String, Object>> annotationProcessorInfo = new HashMap<>()
			for (Project project : allProject) {
				PluginContainer plugins = project.getPlugins()
				if (!hasPlugin(plugins, "java")) {
					continue
				}

				Set<File> processors = new HashSet<>()
				List<String> compilerArgs = new LinkedList<>()
				// Compiler args for test are ignored.
				// Due to JDT does not differentiate the args between main and test.
				collectApConfiguration(project, "compileJava", processors, compilerArgs)

				Map<String, Object> apInfo = new HashMap<>()
				if (processors.size() > 0) {
					apInfo.put("processors", processors)
					apInfo.put("compilerArgs", compilerArgs)
				}
				annotationProcessorInfo.put(project.getProjectDir(), apInfo)
			}
			return annotationProcessorInfo
		}

		private static boolean hasPlugin(PluginContainer plugins, String pluginId) {
			return plugins.findPlugin(pluginId) != null
		}

		private void collectApConfiguration(project, compileTaskName, processors, compilerArgs) {
			JavaCompile javaCompile = project.getTasks().findByName(compileTaskName)
			if (javaCompile != null) {
				CompileOptions options = javaCompile.getOptions()
				if (!options.compilerArgs.contains("-proc:none")) {
					FileCollection apPath = options.getAnnotationProcessorPath()
					if (apPath != null) {
						processors.addAll(apPath.getFiles())
					}
					compilerArgs.addAll(options.getCompilerArgs())
				}
			}
		}
	}
}

allprojects {
	afterEvaluate {
		it.getPlugins().apply(GradleAnnotationProcessorPatchPlugin)
	}
}
