/*
 * Copyright 2020 The Android Open Source Project
 *
 * 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 androidx.compose.material.icons.generator.tasks

import androidx.compose.material.icons.generator.CoreIcons
import androidx.compose.material.icons.generator.IconWriter
import org.gradle.api.Project
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Jar
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import java.io.File

/**
 * Task responsible for converting core icons from xml to a programmatic representation.
 */
@CacheableTask
open class CoreIconGenerationTask : IconGenerationTask() {
    override fun run() =
        IconWriter(loadIcons()).generateTo(generatedSrcMainDirectory) { it in CoreIcons }

    companion object {
        /**
         * Registers [CoreIconGenerationTask] in [project].
         */
        @Suppress("DEPRECATION") // BaseVariant
        fun register(project: Project, variant: com.android.build.gradle.api.BaseVariant? = null) {
            val (task, buildDirectory) = project.registerGenerationTask(
                "generateCoreIcons",
                CoreIconGenerationTask::class.java,
                variant
            )
            // Multiplatform
            if (variant == null) {
                registerIconGenerationTask(project, task, buildDirectory)
            }
            // AGP
            else variant.registerIconGenerationTask(project, task, buildDirectory)
        }
    }
}

/**
 * Task responsible for converting extended icons from xml to a programmatic representation.
 */
@CacheableTask
open class ExtendedIconGenerationTask : IconGenerationTask() {
    override fun run() =
        IconWriter(loadIcons()).generateTo(generatedSrcMainDirectory) { it !in CoreIcons }

    companion object {
        /**
         * Registers [ExtendedIconGenerationTask] in [project]. (for use with mpp)
         */
        @Suppress("DEPRECATION") // BaseVariant
        fun register(project: Project, variant: com.android.build.gradle.api.BaseVariant? = null) {
            val (task, buildDirectory) = project.registerGenerationTask(
                "generateExtendedIcons",
                ExtendedIconGenerationTask::class.java,
                variant
            )
            // Multiplatform
            if (variant == null) {
                registerIconGenerationTask(project, task, buildDirectory)
            }
            // AGP
            else variant.registerIconGenerationTask(project, task, buildDirectory)
        }

        /**
         * Registers the icon generation task just for source jar generation, and not for
         * compilation. This is temporarily needed since we manually parallelize compilation in
         * material-icons-extended for the AGP build. When we remove that parallelization code,
         * we can remove this too.
         */
        @JvmStatic
        @Suppress("DEPRECATION") // BaseVariant
        fun registerSourceJarOnly(
            project: Project,
            variant: com.android.build.gradle.api.BaseVariant
        ) {
            // Setup the source jar task if this is the release variant
            if (variant.name == "release") {
                val (task, buildDirectory) = project.registerGenerationTask(
                    "generateExtendedIcons",
                    ExtendedIconGenerationTask::class.java,
                    variant
                )
                val generatedSrcMainDirectory = buildDirectory.resolve(GeneratedSrcMain)
                project.addToSourceJar(generatedSrcMainDirectory, task)
            }
        }
    }
}

/**
 * Helper to register [task] that outputs to [buildDirectory] as the Kotlin source generating
 * task for [project].
 */
private fun registerIconGenerationTask(
    project: Project,
    task: TaskProvider<*>,
    buildDirectory: File
) {
    val sourceSet = project.getMultiplatformSourceSet(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
    val generatedSrcMainDirectory = buildDirectory.resolve(IconGenerationTask.GeneratedSrcMain)
    sourceSet.kotlin.srcDir(project.files(generatedSrcMainDirectory).builtBy(task))
    // add it to the multiplatform sources as well.
    project.tasks.named("multiplatformSourceJar", Jar::class.java).configure {
        it.from(task.map { generatedSrcMainDirectory })
    }
    project.addToSourceJar(generatedSrcMainDirectory, task)
}

/**
 * Helper to register [task] as the java source generating task that outputs to [buildDirectory].
 */
@Suppress("DEPRECATION") // BaseVariant
private fun com.android.build.gradle.api.BaseVariant.registerIconGenerationTask(
    project: Project,
    task: TaskProvider<*>,
    buildDirectory: File
) {
    val generatedSrcMainDirectory = buildDirectory.resolve(IconGenerationTask.GeneratedSrcMain)
    registerJavaGeneratingTask(task, generatedSrcMainDirectory)
    // Setup the source jar task if this is the release variant
    if (name == "release") {
        project.addToSourceJar(generatedSrcMainDirectory, task)
    }
}

/**
 * Adds the contents of [buildDirectory] to the source jar generated for this [Project] by [task]
 */
// TODO: b/191485164 remove when AGP lets us get generated sources from a TestedExtension or
// similar, then we can just add generated sources in SourceJarTaskHelper for all projects,
// instead of needing one-off support here.
private fun Project.addToSourceJar(buildDirectory: File, task: TaskProvider<*>) {
    afterEvaluate {
        val sourceJar = tasks.named("sourceJarRelease", Jar::class.java)
        sourceJar.configure {
            // Generating source jars requires the generation task to run first. This shouldn't
            // be needed for the MPP build because we use builtBy to set up the dependency
            // (https://github.com/gradle/gradle/issues/17250) but the path is different for AGP,
            // so we will still need this for the AGP build.
            it.dependsOn(task)
            it.from(buildDirectory)
        }
    }
}
