/**
 * Copyright 2016 Jake Wharton
 *
 * 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.
 */
task projectDependencyGraph {
  doLast {
    def dot = new File(rootProject.rootDir, "docs/static/img/dependency-graph.dot")
    dot.parentFile.mkdirs()
    dot.delete()

    dot << 'digraph {\n'
    dot << "  graph [label=\"${rootProject.name}\\n \",labelloc=t,fontsize=30,ranksep=1.4];\n"
    dot << '  node [style=filled, fillcolor="#bbbbbb"];\n'
    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 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('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)
          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 (project.getPath().startsWith(":core")) {
        traits.add('fillcolor="#94c1ff"')
      } else if (project.getPath().startsWith(":app")) {
        traits.add('fillcolor="#baffc9"')
      } else if (project.getPath().startsWith(":data")) {
        traits.add('fillcolor="#fff694"')
      } else if (project.getPath().startsWith(":services")) {
        traits.add('fillcolor="#ff9498"')
      } else if (project.getPath().startsWith(":libs")) {
        traits.add('fillcolor="#ad94ff"')
      }

      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.first.path}\" -> \"${key.second.path}\""
      if (!traits.isEmpty()) {
        dot << " [${traits.join(", ")}]"
      }
      dot << '\n'
    }

    dot << '}\n'

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

    println("Project module dependency graph created.")
  }
}
