/*
 * Ubuntu Countdown Widget
 * Copyright (C) 2022 Roberto Leinardi
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this
 * program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Taken from https://github.com/JakeWharton/SdkSearch/blob/master/gradle/projectDependencyGraph.gradle
 */

def font = "Helvetica"

tasks.register("projectDependencyGraph") {
    doLast {
        def dot = new File(rootProject.buildDir, 'reports/dependencyGraph/project.dot')
        dot.parentFile.mkdirs()
        dot.delete()

        dot << 'digraph {\n'
        dot << "  graph [label=\"${rootProject.name}\\n \",labelloc=t,fontsize=30,ranksep=1.4,fontname=\"$font\"];\n"
        dot << "  node [style=filled, fillcolor=\"#bbbbbb\", fontname=\"$font\"];\n"
        dot << "  edge [fontname = \"$font\"];"
        dot << '  rankdir=TB;\n'

        def rootProjects = []
        def queue = [rootProject]
        while (!queue.isEmpty()) {
            def project = queue.remove(0)
            rootProjects.add(project)
            queue.addAll(project.childProjects.values())
        }

        def projects = new LinkedHashSet<Project>()
        def dependencies = new LinkedHashMap<Tuple2<Project, Project>, List<String>>()
        def featureProjects = []
        def coreProjects = []
        def multiplatformProjects = []
        def jsProjects = []
        def androidProjects = []
        def javaProjects = []

        queue = [rootProject]
        while (!queue.isEmpty()) {
            def project = queue.remove(0)
            queue.addAll(project.childProjects.values())

            if (project.plugins.hasPlugin('ubuntucountdownwidget.android-feature-conventions')) {
                featureProjects.add(project)
            }
            if (project.plugins.hasPlugin('ubuntucountdownwidget.android-core-conventions')) {
                coreProjects.add(project)
            }
            if (project.plugins.hasPlugin('org.jetbrains.kotlin.multiplatform')) {
                multiplatformProjects.add(project)
            }
            if (project.plugins.hasPlugin('org.jetbrains.kotlin.js')) {
                jsProjects.add(project)
            }
            if (project.plugins.hasPlugin('com.android.library') || project.plugins.hasPlugin('com.android.application')) {
                androidProjects.add(project)
            }
            if (project.plugins.hasPlugin('java-library') || project.plugins.hasPlugin('java')) {
                javaProjects.add(project)
            }

            project.configurations.all { config ->
                config.dependencies
                        .withType(ProjectDependency)
                        .collect { it.dependencyProject }
                        .each { dependency ->
                            projects.add(project)
                            projects.add(dependency)
                            rootProjects.remove(dependency)

                            def graphKey = new Tuple2<Project, Project>(project, dependency)
                            if (project != dependency) {
                                def traits = dependencies.computeIfAbsent(graphKey) { new ArrayList<String>() }
                                if (config.name.toLowerCase().endsWith('implementation')) {
                                    traits.add('style=dotted')
                                }
                            }
                        }
            }
        }

        projects = projects.sort { it.path }

        dot << '\n  # Projects\n\n'
        for (project in projects) {
            def traits = []

            if (rootProjects.contains(project)) {
                traits.add('shape=box')
            }

            if (featureProjects.contains(project)) {
                traits.add('fillcolor="#729500"')
            } else if (coreProjects.contains(project)) {
                traits.add('fillcolor="#d8f96b"')
            } else if (multiplatformProjects.contains(project)) {
                traits.add('fillcolor="#a69be4"')
            } else if (jsProjects.contains(project)) {
                traits.add('fillcolor="#f0db4f"')
            } else if (androidProjects.contains(project)) {
                traits.add('fillcolor="#a4c639"')
            } else if (javaProjects.contains(project)) {
                traits.add('fillcolor="#f89820"')
            } else {
                traits.add('fillcolor="#eeeeee"')
            }

            dot << "  \"${project.path}\" [${traits.join(", ")}];\n"
        }

        dot << '\n  {rank = same;'
        for (project in projects) {
            if (rootProjects.contains(project)) {
                dot << " \"${project.path}\";"
            }
        }
        dot << '}\n'

        dot << '\n  # Dependencies\n\n'
        dependencies.forEach { key, traits ->
            dot << "  \"${key.getV1().path}\" -> \"${key.getV2().path}\""
            if (!traits.isEmpty()) {
                dot << " [${traits.join(", ")}]"
            }
            dot << '\n'
        }

        dot << '}\n'

        def p = 'dot -Tpng -O project.dot'.execute([], dot.parentFile)
        p.waitFor()
        if (p.exitValue() != 0) {
            throw new RuntimeException(p.errorStream.text)
        }

        println("Project module dependency graph created at ${dot.absolutePath}.png")

        copy {
            from "${dot.absolutePath}.png"
            into rootProject.file("art")
        }
    }
}

// Add the task as check dependency only if dot is available
try {
    def p = 'dot -V'.execute([])
    p.waitFor()
    if (p.exitValue() == 0) {
        check.dependsOn tasks.named("projectDependencyGraph")
    } else {
        logger.warn("w: {}", p.errorStream.text)
    }
} catch (Exception e) {
    logger.warn("w: {}", e.message)
}
