/*
 * Copyright 2016 the original author or authors.
 *
 * 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 org.gradle.kotlin.dsl.tooling.builders

import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.ResolvedComponentResult
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.TaskAction
import org.gradle.initialization.buildsrc.BuildSrcProjectConfigurationAction
import org.gradle.kotlin.dsl.provider.inClassPathMode
import org.gradle.kotlin.dsl.resolver.BUILD_SRC_SOURCE_ROOTS_FILE_PATH


internal
class BuildSrcClassPathModeConfigurationAction : BuildSrcProjectConfigurationAction {

    override fun execute(project: ProjectInternal) = project.run {
        if (inClassPathMode()) {
            afterEvaluate {
                configureBuildSrcSourceRootsTask()
            }
        }
    }

    private
    fun Project.configureBuildSrcSourceRootsTask() {
        plugins.withType(JavaBasePlugin::class.java) {
            val generateSourceRoots = tasks.register("generateSourceRoots", GenerateSourceRootsFile::class.java) { task ->
                task.sourceRoots.set(projectDependenciesSourceRootsFrom("runtimeClasspath"))
                task.destinationFile.set(layout.projectDirectory.file(BUILD_SRC_SOURCE_ROOTS_FILE_PATH))
            }
            tasks.named("jar") { jar ->
                jar.finalizedBy(generateSourceRoots)
            }
        }
    }

    private
    fun Project.projectDependenciesSourceRootsFrom(configurationName: String) = provider {
        configurations.getByName(configurationName).incoming.resolutionResult.allComponents.asSequence()
            .projectDependenciesIdentifiers()
            .map { project(it.projectPath) }
            .filterHasJavaBasePlugin()
            .allSourceSetsRoots()
            .map { it.relativeTo(rootDir).path }
            .toList()
    }

    private
    fun Sequence<ResolvedComponentResult>.projectDependenciesIdentifiers() =
        mapNotNull { it.id as? ProjectComponentIdentifier }

    private
    fun Sequence<Project>.filterHasJavaBasePlugin() =
        filter { it.plugins.hasPlugin(JavaBasePlugin::class.java) }

    private
    fun Sequence<Project>.allSourceSetsRoots() =
        flatMap { project ->
            project.extensions.getByType(SourceSetContainer::class.java)
                .flatMap { it.allSource.srcDirs }
                .asSequence()
        }
}


@CacheableTask
abstract class GenerateSourceRootsFile : DefaultTask() {

    @get:Input
    abstract val sourceRoots: ListProperty<String>

    @get:OutputFile
    abstract val destinationFile: RegularFileProperty

    @TaskAction
    @Suppress("unused")
    fun generateSourcePathFile() =
        destinationFile.get().asFile.printWriter().use { writer ->
            sourceRoots.get().forEach { sourceRoot ->
                writer.print(sourceRoot)
                writer.print("\n")
            }
        }
}
