#[==[.md
# `vtkModuleWrapClientServer`

This module includes logic necessary in order to wrap VTK modules using
ParaView's ClientServer "language". This allows for classes in the module to be
used as proxies between ParaView client and server programs.
#]==]

#[==[.md INTERNAL
## Wrapping a single module

This function generates the wrapped sources for a module. It places the list of
generated source files and classes in variables named in the second and third
arguments, respectively.

```
_vtk_module_wrap_client_server_sources(<module> <sources> <classes>)
```
#]==]

cmake_policy(PUSH)
cmake_policy(SET CMP0053 NEW)

if (POLICY CMP0116)
  cmake_policy(SET CMP0116 NEW)
endif()

function (_vtk_module_wrap_client_server_sources module sources classes)
  _vtk_module_get_module_property("${module}"
    PROPERTY  "exclude_wrap"
    VARIABLE  _vtk_client_server_exclude_wrap)
  if (_vtk_client_server_exclude_wrap)
    return ()
  endif ()
  _vtk_module_get_module_property("${module}"
    PROPERTY  "client_server_exclude"
    VARIABLE  _vtk_client_server_exclude)
  if (_vtk_client_server_exclude)
    return ()
  endif ()

  set(_vtk_client_server_args_file "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_vtk_client_server_library_name}-client-server.$<CONFIGURATION>.args")

  set(_vtk_client_server_genex_allowed 1)
  if (CMAKE_VERSION VERSION_LESS "3.19")
    get_property(_vtk_client_server_target_type
      TARGET   "${_vtk_client_server_target_name}"
      PROPERTY TYPE)
    if (_vtk_client_server_target_type STREQUAL "INTERFACE_LIBRARY")
      set(_vtk_client_server_genex_allowed 0)
    endif ()
  endif ()

  set(_vtk_client_server_genex_compile_definitions "")
  set(_vtk_client_server_genex_include_directories "")
  if (_vtk_client_server_genex_allowed)
    set(_vtk_client_server_genex_compile_definitions
      "$<TARGET_PROPERTY:${_vtk_client_server_target_name},COMPILE_DEFINITIONS>")
    set(_vtk_client_server_genex_include_directories
      "$<TARGET_PROPERTY:${_vtk_client_server_target_name},INCLUDE_DIRECTORIES>")
    set(_vtk_client_server_genex_interface_compile_definitions
      "$<TARGET_PROPERTY:${_vtk_client_server_target_name},INTERFACE_COMPILE_DEFINITIONS>")
    set(_vtk_client_server_genex_interface_include_directories
      "$<TARGET_PROPERTY:${_vtk_client_server_target_name},INTERFACE_INCLUDE_DIRECTORIES>")
    set(_vtk_client_server_genex_compile_definitions_all
      "$<IF:$<BOOL:${_vtk_client_server_genex_compile_definitions}>,${_vtk_client_server_genex_compile_definitions},${_vtk_client_server_genex_interface_compile_definitions}>")
    set(_vtk_client_server_genex_include_directories_all
      "$<IF:$<BOOL:${_vtk_client_server_genex_include_directories}>,${_vtk_client_server_genex_include_directories},${_vtk_client_server_genex_interface_include_directories}>")

  else ()
    if (NOT DEFINED ENV{CI})
      message(AUTHOR_WARNING
        "ClientServer wrapping is not using target-local compile definitions "
        "or include directories. This may affect generation of the Client "
        "Server wrapper sources for the ${module} module. Use CMake 3.19+ to "
        "guarantee intended behavior.")
    endif ()
  endif ()
  file(GENERATE
    OUTPUT  "${_vtk_client_server_args_file}"
    CONTENT "$<$<BOOL:${_vtk_client_server_genex_compile_definitions_all}>:\n-D\'$<JOIN:${_vtk_client_server_genex_compile_definitions_all},\'\n-D\'>\'>\n
$<$<BOOL:${_vtk_client_server_genex_include_directories_all}>:\n-I\'$<JOIN:${_vtk_client_server_genex_include_directories_all},\'\n-I\'>\'>\n")

  _vtk_module_get_module_property("${module}"
    PROPERTY  "hierarchy"
    VARIABLE  _vtk_client_server_hierarchy_file)

  get_property(_vtk_client_server_is_imported
    TARGET    "${_vtk_client_server_target_name}"
    PROPERTY  "IMPORTED")
  if (_vtk_client_server_is_imported OR CMAKE_GENERATOR MATCHES "Ninja")
    set(_vtk_client_server_command_depend "${_vtk_client_server_hierarchy_file}")
  else ()
    if (TARGET "${_vtk_client_server_library_name}-hierarchy")
      set(_vtk_client_server_command_depend "${_vtk_client_server_library_name}-hierarchy")
    else ()
      message(FATAL_ERROR
        "The ${module} hierarchy file is attached to a non-imported target "
        "and a hierarchy target "
        "(${_vtk_client_server_library_name}-hierarchy) is missing.")
    endif ()
  endif ()

  # create directory for wrapped source files
  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_vtk_client_server_library_name}CS")

  set(_vtk_client_server_sources)

  _vtk_module_get_module_property("${module}"
    PROPERTY  "headers"
    VARIABLE  _vtk_client_server_headers)
  set(_vtk_client_server_classes)
  foreach (_vtk_client_server_header IN LISTS _vtk_client_server_headers)
    get_filename_component(_vtk_client_server_basename "${_vtk_client_server_header}" NAME_WE)
    list(APPEND _vtk_client_server_classes
      "${_vtk_client_server_basename}")

    set(_vtk_client_server_source_output
      "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_vtk_client_server_library_name}CS/${_vtk_client_server_basename}ClientServer.cxx")
    set(_vtk_client_server_depfile_genex
      "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_vtk_client_server_library_name}CS/${_vtk_client_server_basename}ClientServer.cxx.$<CONFIG>.d")
    set(_vtk_client_server_depfile_nogenex
      "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_vtk_client_server_library_name}CS/${_vtk_client_server_basename}ClientServer.cxx.d")
    list(APPEND _vtk_client_server_sources
      "${_vtk_client_server_source_output}")

    _vtk_module_depfile_args(
      MULTI_CONFIG_NEEDS_GENEX
      TOOL_ARGS _vtk_client_server_depfile_flags
      CUSTOM_COMMAND_ARGS _vtk_client_server_depfile_args
      SOURCE "${_vtk_client_server_header}"
      DEPFILE_PATH "${_vtk_client_server_depfile_genex}"
      DEPFILE_NO_GENEX_PATH "${_vtk_client_server_depfile_nogenex}"
      TOOL_FLAGS "-MF")

    add_custom_command(
      OUTPUT  "${_vtk_client_server_source_output}"
      COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR}
              "$<TARGET_FILE:ParaView::WrapClientServer>"
              ${_vtk_client_server_depfile_flags}
              "@${_vtk_client_server_args_file}"
              -o "${_vtk_client_server_source_output}"
              "${_vtk_client_server_header}"
              ${_vtk_client_server_warning_args}
              --types "${_vtk_client_server_hierarchy_file}"
      ${_vtk_client_server_depfile_args}
      COMMENT "Generating client_server wrapper sources for ${_vtk_client_server_basename}"
      DEPENDS
        "$<TARGET_FILE:ParaView::WrapClientServer>"
        "${_vtk_client_server_header}"
        "${_vtk_client_server_args_file}"
        "${_vtk_client_server_command_depend}")
  endforeach ()

  set("${sources}"
    "${_vtk_client_server_sources}"
    PARENT_SCOPE)
  set("${classes}"
    "${_vtk_client_server_classes}"
    PARENT_SCOPE)
endfunction ()

#[==[.md INTERNAL
## Generating a client server library

A client server library may consist of the wrappings of multiple VTK modules.
This is useful for kit-based builds where the modules part of the same kit
belong to the same client server library as well.

```
_vtk_module_wrap_client_server_library(<name> <module>...)
```

The first argument is the name of the client server library. The remaining
arguments are VTK modules to include in the library.

The remaining information it uses is assumed to be provided by the
`vtk_module_wrap_client_server` function.
#]==]
function (_vtk_module_wrap_client_server_library name)
  set(_vtk_client_server_library_sources)
  set(_vtk_client_server_library_classes)
  foreach (_vtk_client_server_module IN LISTS ARGN)
    _vtk_module_get_module_property("${_vtk_client_server_module}"
      PROPERTY  "exclude_wrap"
      VARIABLE  _vtk_client_server_exclude_wrap)
    if (_vtk_client_server_exclude_wrap)
      continue ()
    endif ()
    _vtk_module_get_module_property("${_vtk_client_server_module}"
      PROPERTY  "client_server_exclude"
      VARIABLE  _vtk_client_server_exclude)
    if (_vtk_client_server_exclude)
      return ()
    endif ()

    _vtk_module_wrap_client_server_sources("${_vtk_client_server_module}" _vtk_client_server_sources _vtk_client_server_classes)
    list(APPEND _vtk_client_server_library_sources
      ${_vtk_client_server_sources})
    list(APPEND _vtk_client_server_library_classes
      ${_vtk_client_server_classes})
  endforeach ()

  if (NOT _vtk_client_server_library_sources)
    return ()
  endif ()

  set(_vtk_client_server_declarations)
  set(_vtk_client_server_calls)
  foreach (_vtk_client_server_class IN LISTS _vtk_client_server_library_classes)
    string(APPEND _vtk_client_server_declarations
      "extern \"C\" { void ${_vtk_client_server_class}_Init(vtkClientServerInterpreter*); }\n")
    string(APPEND _vtk_client_server_calls
      "  ${_vtk_client_server_class}_Init(csi);\n")
  endforeach ()
  set(_vtk_client_server_init_content
    "#include \"vtkABI.h\"
#include \"vtkClientServerInterpreter.h\"

${_vtk_client_server_declarations}
extern \"C\" VTK_ABI_EXPORT void ${name}_Initialize(vtkClientServerInterpreter* csi)
{
  (void)csi;
${_vtk_client_server_calls}}\n")

  set(_vtk_client_server_init_file
    "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${name}Init.cxx")
  file(GENERATE
    OUTPUT  "${_vtk_client_server_init_file}"
    CONTENT "${_vtk_client_server_init_content}")
  # XXX(cmake): Why is this necessary? One would expect that `file(GENERATE)`
  # would do this automatically.
  set_property(SOURCE "${_vtk_client_server_init_file}"
    PROPERTY
      GENERATED 1)

  add_library("${name}" STATIC)
  target_sources("${name}"
    PRIVATE
      ${_vtk_client_server_library_sources}
      "${_vtk_client_server_init_file}")
  # Add a dummy file set to optimize dependencies. See CMP0154.
  _vtk_module_add_file_set("${name}"
    BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}"
    NAME      dummy)
  if (BUILD_SHARED_LIBS)
    set_property(TARGET "${name}"
      PROPERTY
        POSITION_INDEPENDENT_CODE 1)
  endif ()
  set(_vtk_build_LIBRARY_NAME_SUFFIX "${_vtk_client_server_LIBRARY_NAME_SUFFIX}")
  set(_vtk_build_ARCHIVE_DESTINATION "${_vtk_client_server_DESTINATION}")
  _vtk_module_apply_properties("${name}")

  vtk_module_autoinit(
    MODULES ${ARGN}
    TARGETS "${name}")

  target_link_libraries("${name}"
    PRIVATE
      ${ARGN}
      ParaView::RemotingClientServerStream
      VTK::CommonCore)

  set(_vtk_client_server_export)
  if (_vtk_client_server_INSTALL_EXPORT)
    list(APPEND _vtk_client_server_export
      EXPORT "${_vtk_client_server_INSTALL_EXPORT}")
  endif ()

  install(
    TARGETS             "${name}"
    ${_vtk_client_server_export}
    COMPONENT           "${_vtk_client_server_COMPONENT}"
    ARCHIVE DESTINATION "${_vtk_client_server_DESTINATION}")
endfunction ()

#[==[.md
## Wrapping a set of VTK modules for ClientServer

```
vtk_module_wrap_client_server(
  MODULES <module>...
  TARGET <target>
  [WRAPPED_MODULES <varname>]

  [FUNCTION_NAME <function>]
  [DESTINATION <destination>]

  [INSTALL_EXPORT <export>]
  [COMPONENT <component>]

  [WARNINGS <warning>...]
)
```

  * `MODULES`: (Required) The list of modules to wrap.
  * `TARGET`: (Required) The target to create which represents all wrapped
    ClientServer modules. This is used to provide the function used to
    initialize the bindings.
  * `WRAPPED_MODULES`: (Recommended) Not all modules are wrappable. This
    variable will be set to contain the list of modules which were wrapped.
  * `FUNCTION_NAME`: (Recommended) (Defaults to `<TARGET>_initialize`) The
    function name to generate in order to initialize the client server
    bindings.A header with the name `<TARGET>.h` should be included in order to
    access the initialization function.
  * `DESTINATION`: (Defaults to `${CMAKE_INSTALL_LIBDIR}`) Where to install the
    generated libraries.
  * `INSTALL_EXPORT`: If provided, installs will add the installed
    libraries and generated interface target to the provided export set.
  * `COMPONENT`: (Defaults to `development`) All install rules created by this
    function will use this installation component.
  * ``WARNINGS``: Warnings to enable. Supported warnings: ``empty``.
#]==]
function (vtk_module_wrap_client_server)
  cmake_parse_arguments(_vtk_client_server
    ""
    "DESTINATION;INSTALL_EXPORT;TARGET;COMPONENT;FUNCTION_NAME;WRAPPED_MODULES"
    "MODULES;WARNINGS"
    ${ARGN})

  if (_vtk_client_server_UNPARSED_ARGUMENTS)
    message(FATAL_ERROR
      "Unparsed arguments for vtk_module_wrap_client_server: "
      "${_vtk_client_server_UNPARSED_ARGUMENTS}")
  endif ()

  if (NOT _vtk_client_server_MODULES)
    message(WARNING
      "No modules were requested for client server wrapping.")
    return ()
  endif ()

  if (NOT _vtk_client_server_TARGET)
    message(FATAL_ERROR
      "The `TARGET` argument is required.")
  endif ()

  set(_vtk_client_server_known_warnings
    empty)
  set(_vtk_client_server_warning_args)
  foreach (_vtk_client_server_warning IN LISTS _vtk_client_server_WARNINGS)
    if (NOT _vtk_client_server_warning IN_LIST _vtk_client_server_known_warnings)
      message(FATAL_ERROR
        "Unrecognized warning: ${_vtk_client_server_warning}")
    endif ()
    list(APPEND _vtk_client_server_warning_args
      "-W${_vtk_client_server_warning}")
  endforeach ()

  if (NOT DEFINED _vtk_client_server_DESTINATION)
    set(_vtk_client_server_DESTINATION "${CMAKE_INSTALL_LIBDIR}")
  endif ()

  if (NOT DEFINED _vtk_client_server_COMPONENT)
    set(_vtk_client_server_COMPONENT "development")
  endif ()

  if (NOT DEFINED _vtk_client_server_FUNCTION_NAME)
    set(_vtk_client_server_FUNCTION_NAME "${_vtk_client_server_TARGET}_initialize")
  endif ()

  # Disable CMake's automoc support for these targets.
  set(CMAKE_AUTOMOC 0)
  set(CMAKE_AUTORCC 0)
  set(CMAKE_AUTOUIC 0)

  # TODO: Install cmake properties?

  set(_vtk_client_server_all_modules)
  set(_vtk_client_server_all_wrapped_modules)
  foreach (_vtk_client_server_module IN LISTS _vtk_client_server_MODULES)
    _vtk_module_get_module_property("${_vtk_client_server_module}"
      PROPERTY  "exclude_wrap"
      VARIABLE  _vtk_client_server_exclude_wrap)
    if (_vtk_client_server_exclude_wrap)
      continue ()
    endif ()
    _vtk_module_get_module_property("${_vtk_client_server_module}"
      PROPERTY  "client_server_exclude"
      VARIABLE  _vtk_client_server_exclude)
    if (_vtk_client_server_exclude)
      continue ()
    endif ()
    _vtk_module_real_target(_vtk_client_server_target_name "${_vtk_client_server_module}")
    _vtk_module_get_module_property("${_vtk_client_server_module}"
      PROPERTY  "library_name"
      VARIABLE  _vtk_client_server_library_name)
    _vtk_module_wrap_client_server_library("${_vtk_client_server_library_name}CS" "${_vtk_client_server_module}")

    if (TARGET "${_vtk_client_server_library_name}CS")
      list(APPEND _vtk_client_server_all_modules
        "${_vtk_client_server_library_name}CS")
      list(APPEND _vtk_client_server_all_wrapped_modules
        "${_vtk_client_server_module}")
    endif ()
  endforeach ()

  if (NOT _vtk_client_server_all_modules)
    message(FATAL_ERROR
      "No modules given could be wrapped.")
  endif ()

  if (DEFINED _vtk_client_server_WRAPPED_MODULES)
    set("${_vtk_client_server_WRAPPED_MODULES}"
      "${_vtk_client_server_all_wrapped_modules}"
      PARENT_SCOPE)
  endif ()

  if (_vtk_client_server_TARGET)
    add_library("${_vtk_client_server_TARGET}" INTERFACE)
    target_include_directories("${_vtk_client_server_TARGET}"
      INTERFACE
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_vtk_client_server_TARGET}>")

    set(_vtk_client_server_all_modules_include_file
      "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_vtk_client_server_TARGET}/${_vtk_client_server_TARGET}.h")

    set(_vtk_client_server_declarations)
    set(_vtk_client_server_calls)
    foreach (_vtk_client_server_module IN LISTS _vtk_client_server_all_modules)
      string(APPEND _vtk_client_server_declarations
        "extern \"C\" void ${_vtk_client_server_module}_Initialize(vtkClientServerInterpreter*);\n")
      string(APPEND _vtk_client_server_calls
        "  ${_vtk_client_server_module}_Initialize(csi);\n")
    endforeach ()

    set(_vtk_client_server_all_modules_include_content
      "#ifndef ${_vtk_client_server_TARGET}_h
#define ${_vtk_client_server_TARGET}_h

#include \"vtkClientServerInterpreter.h\"

${_vtk_client_server_declarations}
inline void ${_vtk_client_server_FUNCTION_NAME}(vtkClientServerInterpreter* csi)
{
  (void)csi;
${_vtk_client_server_calls}}

#endif\n")

    file(GENERATE
      OUTPUT  "${_vtk_client_server_all_modules_include_file}"
      CONTENT "${_vtk_client_server_all_modules_include_content}")

    target_link_libraries("${_vtk_client_server_TARGET}"
      INTERFACE
        ${_vtk_client_server_all_modules})

    set(_vtk_client_server_export)
    if (_vtk_client_server_INSTALL_EXPORT)
      list(APPEND _vtk_client_server_export
        EXPORT "${_vtk_client_server_INSTALL_EXPORT}")
    endif ()

    install(
      TARGETS             "${_vtk_client_server_TARGET}"
      ${_vtk_client_server_export}
      COMPONENT           "${_vtk_client_server_COMPONENT}"
      ARCHIVE DESTINATION "${_vtk_client_server_DESTINATION}")
  endif ()
endfunction ()

#[==[.md
## Excluding a module from wrapping

Some modules should not be wrapped using client server bindings. Since this is
independent of general wrapping facilities, an additional property is used to
check. This may be set using the `vtk_module_client_server_exclude` function.

```
vtk_module_client_server_exclude(
  [MODULE <module>])
```

The `MODULE` defaults to the module currently being built. If a module is not
being built when this function is called, it must be provided.
#]==]
function (vtk_module_client_server_exclude)
  cmake_parse_arguments(_vtk_client_server_exclude
    ""
    "MODULE"
    ""
    ${ARGN})

  if (_vtk_client_server_exclude_UNPARSED_ARGUMENTS)
    message(FATAL_ERROR
      "Unparsed arguments for vtk_module_wrap_client_server_exclude: "
      "${_vtk_client_server_exclude_UNPARSED_ARGUMENTS}")
  endif ()

  if (NOT DEFINED _vtk_client_server_exclude_MODULE)
    if (NOT DEFINED _vtk_build_module)
      message(FATAL_ERROR
        "The `MODULE` argument must be provided outside of a module build.")
    endif ()
    set(_vtk_client_server_exclude_MODULE "${_vtk_build_module}")
  endif ()

  _vtk_module_set_module_property("${_vtk_client_server_exclude_MODULE}"
    PROPERTY  "client_server_exclude"
    VALUE     1)
endfunction ()

cmake_policy(POP)
