#[==[
@ingroup module-impl
@brief Output a node in the graph

Queries the properties for modules and generates the node for it in the graph
and its outgoing dependency edges.
#]==]
function (_vtk_module_graphviz_module_node var module)
  get_property(_vtk_graphviz_file GLOBAL
    PROPERTY "_vtk_module_${module}_file")
  if (_vtk_graphviz_file)
    get_property(_vtk_graphviz_module_third_party GLOBAL
      PROPERTY "_vtk_module_${module}_third_party")
    get_property(_vtk_graphviz_module_exclude_wrap GLOBAL
      PROPERTY "_vtk_module_${module}_exclude_wrap")
    get_property(_vtk_graphviz_module_depends GLOBAL
      PROPERTY "_vtk_module_${module}_depends")
    get_property(_vtk_graphviz_module_private_depends GLOBAL
      PROPERTY "_vtk_module_${module}_private_depends")
    get_property(_vtk_graphviz_module_optional_depends GLOBAL
      PROPERTY "_vtk_module_${module}_optional_depends")
    get_property(_vtk_graphviz_module_implements GLOBAL
      PROPERTY "_vtk_module_${module}_implements")
    get_property(_vtk_graphviz_module_implementable GLOBAL
      PROPERTY "_vtk_module_${module}_implementable")
  else ()
    get_property(_vtk_graphviz_module_third_party
      TARGET    "${module}"
      PROPERTY  "INTERFACE_vtk_module_third_party")
    get_property(_vtk_graphviz_module_exclude_wrap
      TARGET    "${module}"
      PROPERTY  "INTERFACE_vtk_module_exclude_wrap")
    get_property(_vtk_graphviz_module_depends
      TARGET    "${module}"
      PROPERTY  "INTERFACE_vtk_module_depends")
    set(_vtk_graphviz_module_private_depends)
    set(_vtk_graphviz_module_optional_depends)
    get_property(_vtk_graphviz_module_implements
      TARGET    "${module}"
      PROPERTY  "INTERFACE_vtk_module_implements")
    get_property(_vtk_graphviz_module_implementable
      TARGET    "${module}"
      PROPERTY  "INTERFACE_vtk_module_implementable")
  endif ()

  if (_vtk_graphviz_module_third_party)
    set(_vtk_graphviz_shape "${_vtk_graphviz_third_party}")
  else ()
    set(_vtk_graphviz_shape "${_vtk_graphviz_first_party}")
  endif ()

  if (_vtk_graphviz_file)
    if (DEFINED "VTK_MODULE_USE_EXTERNAL_${module}" AND VTK_MODULE_USE_EXTERNAL_${module})
      set(_vtk_graphviz_fillcolor "${_vtk_graphviz_external}")
    else ()
      set(_vtk_graphviz_fillcolor "${_vtk_graphviz_internal}")
    endif ()
  else ()
    set(_vtk_graphviz_fillcolor "${_vtk_graphviz_external}")
  endif ()

  if (_vtk_graphviz_module_exclude_wrap)
    set(_vtk_graphviz_penwidth "${_vtk_graphviz_exclude_wrap}")
  else ()
    set(_vtk_graphviz_penwidth "${_vtk_graphviz_include_wrap}")
  endif ()

  if (_vtk_graphviz_module_implementable)
    set(_vtk_graphviz_color "${_vtk_graphviz_implementable}")
  else ()
    set(_vtk_graphviz_color "${_vtk_graphviz_not_implementable}")
  endif ()

  set(_vtk_graphviz_node_block "\"${module}\" [
    label=\"${module}\"
    shape=${_vtk_graphviz_shape}
    style=filled
    color=${_vtk_graphviz_color}
    fillcolor=${_vtk_graphviz_fillcolor}
    penwidth=${_vtk_graphviz_penwidth}
];\n")

  foreach (_vtk_graphviz_module_implement IN LISTS _vtk_graphviz_module_implements)
    string(APPEND _vtk_graphviz_node_block
      "\"${module}\" -> \"${_vtk_graphviz_module_implement}\" [style=${_vtk_graphviz_implements}, arrowhead=${_vtk_graphviz_required_depends}];\n")
  endforeach ()

  foreach (_vtk_graphviz_module_depend IN LISTS _vtk_graphviz_module_depends)
    string(APPEND _vtk_graphviz_node_block
      "\"${module}\" -> \"${_vtk_graphviz_module_depend}\" [style=${_vtk_graphviz_public_depends}, arrowhead=${_vtk_graphviz_required_depends}];\n")
  endforeach ()

  if (_vtk_graphviz_PRIVATE_DEPENDENCIES)
    foreach (_vtk_graphviz_module_private_depend IN LISTS _vtk_graphviz_module_private_depends)
      string(APPEND _vtk_graphviz_node_block
        "\"${module}\" -> \"${_vtk_graphviz_module_private_depend}\" [style=${_vtk_graphviz_private_depends}, arrowhead=${_vtk_graphviz_required_depends}];\n")
    endforeach ()

    foreach (_vtk_graphviz_module_optional_depend IN LISTS _vtk_graphviz_module_optional_depends)
      string(APPEND _vtk_graphviz_node_block
        "\"${module}\" -> \"${_vtk_graphviz_module_optional_depend}\" [style=${_vtk_graphviz_optional_depends}, arrowhead=${_vtk_graphviz_optional_depends}];\n")
    endforeach ()
  endif ()

  set("${var}" "${_vtk_graphviz_node_block}" PARENT_SCOPE)
endfunction ()

#[==[
@ingroup module-support
@brief Generate graphviz output for a module dependency graph

Information about the modules built and/or available may be dumped to a
Graphviz `.dot` file.

~~~
vtk_module_graphviz(
  MODULES   <module>...
  OUTPUT    <path>

  [PRIVATE_DEPENDENCIES <ON|OFF>]
  [KIT_CLUSTERS <ON|OFF>])
~~~

  * `MODULES`: (Required) The modules to output information for.
  * `OUTPUT`: (Required) A Graphviz file describing the modules built will
    be output to this path. Relative paths are rooted to `CMAKE_BINARY_DIR`.
  * `PRIVATE_DEPENDENCIES`: (Default `ON`) Whether to draw private dependency
    edges or not..
  * `KIT_CLUSTERS`: (Default `OFF`) Whether to draw modules as part of a kit as
    a cluster or not.
#]==]
function (vtk_module_graphviz)
  cmake_parse_arguments(_vtk_graphviz
    ""
    "PRIVATE_DEPENDENCIES;KIT_CLUSTERS;OUTPUT"
    "MODULES"
    ${ARGN})

  if (_vtk_graphviz_UNPARSED_ARGUMENTS)
    message(FATAL_ERROR
      "Unparsed arguments for vtk_module_graphviz: "
      "${_vtk_graphviz_UNPARSED_ARGUMENTS}")
  endif ()

  if (NOT DEFINED _vtk_graphviz_OUTPUT)
    message(FATAL_ERROR
      "The `OUTPUT` argument is required.")
  endif ()

  if (NOT _vtk_graphviz_MODULES)
    message(FATAL_ERROR "No modules given to output.")
  endif ()

  if (NOT DEFINED _vtk_graphviz_PRIVATE_DEPENDENCIES)
    set(_vtk_graphviz_PRIVATE_DEPENDENCIES ON)
  endif ()

  if (NOT DEFINED _vtk_graphviz_KIT_CLUSTERS)
    set(_vtk_graphviz_KIT_CLUSTERS OFF)
  endif ()

  if (NOT IS_ABSOLUTE "${_vtk_graphviz_OUTPUT}")
    set(_vtk_graphviz_OUTPUT "${CMAKE_BINARY_DIR}/${_vtk_graphviz_OUTPUT}")
  endif ()

  set(_vtk_graphviz_kits)
  set(_vtk_graphviz_no_kit_modules)

  if (_vtk_graphviz_KIT_CLUSTERS)
    # Get a list of all kits.
    foreach (_vtk_graphviz_module IN LISTS _vtk_graphviz_MODULES)
      get_property(_vtk_graphviz_kit GLOBAL
        PROPERTY "_vtk_module_${_vtk_graphviz_module}_kit")
      if (_vtk_graphviz_kit)
        list(APPEND _vtk_graphviz_kits
          "${_vtk_graphviz_kit}")
      else ()
        list(APPEND _vtk_graphviz_no_kit_modules
          "${_vtk_graphviz_module}")
      endif ()
    endforeach ()
    if (_vtk_graphviz_kits)
      list(REMOVE_DUPLICATES _vtk_graphviz_kits)
    endif ()
  else ()
    set(_vtk_graphviz_no_kit_modules "${_vtk_graphviz_MODULES}")
  endif ()

  # Shapes
  set(_vtk_graphviz_first_party "rectangle")
  set(_vtk_graphviz_third_party "cds")
  set(_vtk_graphviz_internal "\"/svg/white\"")
  set(_vtk_graphviz_external "\"/svg/cyan\"")

  # Border style
  set(_vtk_graphviz_include_wrap "5")
  set(_vtk_graphviz_exclude_wrap "1")
  set(_vtk_graphviz_implementable "\"/svg/darkorchid\"")
  set(_vtk_graphviz_not_implementable "\"/svg/coral\"")

  # Dependencies
  set(_vtk_graphviz_public_depends "solid")
  set(_vtk_graphviz_private_depends "dotted")
  set(_vtk_graphviz_implements "bold")

  set(_vtk_graphviz_required_depends "normal")
  set(_vtk_graphviz_optional_depends "empty")

  set(_vtk_graphviz_contents "strict digraph modules {\nclusterrank=local;\nrankdir=TB;\n")

  # Output modules not part of a kit.
  string(APPEND _vtk_graphviz_contents
    "subgraph \"modules_without_kits\" {\n")
  foreach (_vtk_graphviz_module IN LISTS _vtk_graphviz_no_kit_modules)
    _vtk_module_graphviz_module_node(_vtk_graphviz_node "${_vtk_graphviz_module}")
    string(APPEND _vtk_graphviz_contents
      "${_vtk_graphviz_node}\n")
  endforeach ()
  string(APPEND _vtk_graphviz_contents
    "}\n")

  # Output kits as clusters.
  foreach (_vtk_graphviz_kit IN LISTS _vtk_graphviz_kits)
    string(APPEND _vtk_graphviz_contents
      "subgraph \"cluster_${_vtk_graphviz_kit}\" {\nlabel=\"${_vtk_graphviz_kit}\"\n")

    get_property(_vtk_graphviz_kit_modules GLOBAL
      PROPERTY "_vtk_kit_${_vtk_graphviz_kit}_kit_modules")
    foreach (_vtk_graphviz_kit_module IN LISTS _vtk_graphviz_kit_modules)
      if (NOT _vtk_graphviz_kit_module IN_LIST _vtk_graphviz_MODULES)
        continue ()
      endif ()

      _vtk_module_graphviz_module_node(_vtk_graphviz_node "${_vtk_graphviz_kit_module}")
      string(APPEND _vtk_graphviz_contents
        "${_vtk_graphviz_node}\n")

    endforeach ()

    string(APPEND _vtk_graphviz_contents
      "}\n")
  endforeach ()

  # Write the key cluster.
  string(APPEND _vtk_graphviz_contents "
subgraph cluster_key {
  label=Key;
  subgraph cluster_party {
    first_party [
      label=\"First party\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    third_party [
      label=\"Third party\"
      shape=${_vtk_graphviz_third_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
  }
  subgraph cluster_whence {
    internal [
      label=\"Internal module\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    external [
      label=\"External module\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_external}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
  }
  subgraph cluster_wrapping {
    include_wrap [
      label=\"Wrappable\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    exclude_wrap [
      label=\"Not wrappable\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_exclude_wrap}
    ];
  }
  subgraph cluster_implementable {
    implementable [
      label=\"Implementable\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    not_implementable [
      label=\"Not implementable\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
  }
  subgraph cluster_dependencies {
    dependent [
      label=\"Dependent\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    private_dependee [
      label=\"Private Dependee\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    optional_dependee [
      label=\"Optional Dependee\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    public_dependee [
      label=\"Public Dependee\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_not_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    implemented [
      label=\"Implemented\"
      shape=${_vtk_graphviz_first_party}
      style=filled
      color=${_vtk_graphviz_implementable}
      fillcolor=${_vtk_graphviz_internal}
      penwidth=${_vtk_graphviz_include_wrap}
    ];
    dependent -> private_dependee [style=${_vtk_graphviz_private_depends}, arrowhead=${_vtk_graphviz_required_depends}];
    dependent -> optional_dependee [style=${_vtk_graphviz_private_depends}, arrowhead=${_vtk_graphviz_optional_depends}];
    dependent -> public_dependee [style=${_vtk_graphviz_public_depends}, arrowhead=${_vtk_graphviz_required_depends}];
    dependent -> implemented [style=${_vtk_graphviz_implements}, arrowhead=${_vtk_graphviz_required_depends}];
  }
}
")
  string(APPEND _vtk_graphviz_contents "}\n")

  #file(GENERATE
  #  OUTPUT  "${_vtk_graphviz_OUTPUT}"
  #  CONTENT "${_vtk_graphviz_contents}")
  file(WRITE "${_vtk_graphviz_OUTPUT}" "${_vtk_graphviz_contents}")
endfunction ()
