# ==== Define cmake build policies that affect compilation and linkage default behaviors
#
# Set the NEWEST_VALIDATED_POLICIES_VERSION string to the newest cmake version
# policies that provide successful builds. By setting NEWEST_VALIDATED_POLICIES_VERSION
# to a value greater than the oldest policies, all policies between
# OLDEST_VALIDATED_POLICIES_VERSION and CMAKE_VERSION (used for this build)
# are set to their NEW behavior, thereby suppressing policy warnings related to policies
# between the OLDEST_VALIDATED_POLICIES_VERSION and CMAKE_VERSION.
#
# CMake versions greater than the NEWEST_VALIDATED_POLICIES_VERSION policies will
# continue to generate policy warnings "CMake Warning (dev)...Policy CMP0XXX is not set:"
#
set(OLDEST_VALIDATED_POLICIES_VERSION "3.28.0")
set(NEWEST_VALIDATED_POLICIES_VERSION "3.28.0")
cmake_minimum_required(VERSION ${OLDEST_VALIDATED_POLICIES_VERSION}...${NEWEST_VALIDATED_POLICIES_VERSION} FATAL_ERROR)

#-----------------------------------------------------------------------------
# Setting C++ Standard
#-----------------------------------------------------------------------------
set(_msg "Setting C++ standard")
message(STATUS "${_msg}")
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 17)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
message(STATUS "${_msg} - C++${CMAKE_CXX_STANDARD}")
if(NOT CMAKE_CXX_STANDARD MATCHES "^(17|20)$")
  message(FATAL_ERROR "CMAKE_CXX_STANDARD must be set to 17 or 20")
endif()

# Prevent unnecessary re-linking on Linux
#
# See https://discourse.slicer.org/t/build-when-git-commit-is-changed/29607/12
#
# For example, on Linux, after modifying one implementation file
#
# $ touch ../../Slicer/Libs/MRML/Core/vtkMRMLScene.cxx
#
# We can observe that beside the rebuilding of libMRMLCore.so no relinking
# happen for any of the dependencies:
#
# $ make -j10
# [...]
# [ 24%] Building CXX object Libs/MRML/Core/CMakeFiles/MRMLCore.dir/vtkMRMLScene.cxx.o
# [ 24%] Linking CXX shared library ../../../bin/libMRMLCore.so
# [ 26%] Built target MRMLCore
# [ 26%] Built target MRMLCLI
# [...]
# [100%] Built target qSlicerVolumeRenderingModuleCxxTests
#
if(NOT DEFINED CMAKE_LINK_DEPENDS_NO_SHARED AND UNIX AND NOT APPLE)
  set(CMAKE_LINK_DEPENDS_NO_SHARED ON)
endif()

#-----------------------------------------------------------------------------
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})
set(SUPERBUILD_TOPLEVEL_PROJECT Slicer)
list(APPEND EXTERNAL_PROJECT_ADDITIONAL_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild)
include(ExternalProject)
include(ExternalProjectDependency)
include(ExternalProjectDependencyForPython)
include(ExternalProjectGenerateProjectDescription)

#-----------------------------------------------------------------------------
if(APPLE)
  # Note: By setting CMAKE_OSX_* variables before any enable_language() or project() calls,
  #       we ensure that the bitness, and C++ standard library will be properly detected.
  include(SlicerInitializeOSXVariables)
  mark_as_superbuild(
    VARS CMAKE_OSX_ARCHITECTURES:STRING CMAKE_OSX_SYSROOT:PATH CMAKE_OSX_DEPLOYMENT_TARGET:STRING
    ALL_PROJECTS
    )

  set(CMAKE_INSTALL_NAME_TOOL "" CACHE FILEPATH "" FORCE)
  # Due to the possibility of external projects (e.g., LibFFI) enabling languages (e.g., ASM)
  # that necessitate `install_name_tool` via the "CMakeFindBinUtils" module, we opt to clear
  # CMAKE_INSTALL_NAME_TOOL on a per-project basis.
  # mark_as_superbuild(VARS CMAKE_INSTALL_NAME_TOOL:FILEPATH ALL_PROJECTS)

  set(CMAKE_MACOSX_RPATH 0)
  mark_as_superbuild(VARS CMAKE_MACOSX_RPATH:BOOL ALL_PROJECTS)
endif()

#-----------------------------------------------------------------------------
if(NOT DEFINED Slicer_VERSION_MAJOR)
  set(Slicer_VERSION_MAJOR "5")
endif()
if(NOT DEFINED Slicer_VERSION_MINOR)
  set(Slicer_VERSION_MINOR "13")
endif()
if(NOT DEFINED Slicer_VERSION_PATCH)
  set(Slicer_VERSION_PATCH "0")
endif()
project(Slicer VERSION "${Slicer_VERSION_MAJOR}.${Slicer_VERSION_MINOR}.${Slicer_VERSION_PATCH}")

#-----------------------------------------------------------------------------
# Update CMake module path
#------------------------------------------------------------------------------
set(CMAKE_MODULE_PATH
  ${Slicer_SOURCE_DIR}/Extensions/CMake
  ${CMAKE_MODULE_PATH}
  )

set(Slicer_CMAKE_DIR ${Slicer_SOURCE_DIR}/CMake)
set(Slicer_EXTENSIONS_CMAKE_DIR ${Slicer_SOURCE_DIR}/Extensions/CMake)

#-----------------------------------------------------------------------------
# https://www.slicer.org/w/index.php/Documentation/Nightly/Developers/DevelopmentWithGit
#-----------------------------------------------------------------------------
include(SlicerCheckSourceTree)

#-----------------------------------------------------------------------------
# Superbuild Option - Enabled by default
#-----------------------------------------------------------------------------
option(Slicer_SUPERBUILD "Build ${PROJECT_NAME} and the projects it depends on." ON)
mark_as_advanced(Slicer_SUPERBUILD)
set(Slicer_BINARY_INNER_SUBDIR Slicer-build)

#-----------------------------------------------------------------------------
include(CTestUseLaunchers OPTIONAL)
if(${CTEST_USE_LAUNCHERS})
  message(STATUS "CTest launchers enabled")
endif()

#-----------------------------------------------------------------------------
# Sanity checks
#------------------------------------------------------------------------------
if(WIN32)
  set(${PROJECT_NAME}_ROOT_DIR_MAX_LENGTH 40)
  if(NOT ${PROJECT_NAME}_SUPERBUILD)
    string(LENGTH ${Slicer_BINARY_INNER_SUBDIR} _inner_subdir_length)
    math(EXPR ${PROJECT_NAME}_ROOT_DIR_MAX_LENGTH "${${PROJECT_NAME}_ROOT_DIR_MAX_LENGTH} + ${_inner_subdir_length}")
  endif()
  include(PreventDirWithTooManyChars)
endif()
include(PreventInSourceBuilds)
include(PreventInBuildInstalls)
include(PreventDirWithSpaces)
mark_as_superbuild(${PROJECT_NAME}_SKIP_DIR_WITH_SPACES_CHECK:BOOL)

#-----------------------------------------------------------------------------
# Overwrite default options
#-----------------------------------------------------------------------------
set(_overwrite_options_file ${Slicer_CMAKE_DIR}/SlicerOverwriteDefaultOptions.cmake)
if(EXISTS ${_overwrite_options_file})
  include(${_overwrite_options_file})
endif()

#-----------------------------------------------------------------------------
# Check if the linker will resolve symbols of underlinked libraries
#-----------------------------------------------------------------------------
if(UNIX AND NOT APPLE)
  include(SlicerLinkerAsNeededFlagCheck)
  if(Slicer_LINKER_NO_AS_NEEDED_FLAG_REQUIRED)
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-as-needed"
      CACHE STRING "Flags used by the linker"
      FORCE)
  endif()
endif()

#-----------------------------------------------------------------------------
# Slicer Build and Release type
#-----------------------------------------------------------------------------
include(SlicerInitializeBuildType)
include(SlicerInitializeReleaseType)

#-----------------------------------------------------------------------------
# Set the header template which defines custom export/import macros
# for shared libraries
#-----------------------------------------------------------------------------
set(Slicer_EXPORT_HEADER_TEMPLATE "${Slicer_SOURCE_DIR}/CMake/qSlicerExport.h.in")
set(Slicer_LOGOS_RESOURCE "${Slicer_SOURCE_DIR}/Resources/qSlicer.qrc")

#-----------------------------------------------------------------------------
# Platform checks
#-----------------------------------------------------------------------------
include(SlicerBlockPlatformCheck)
mark_as_superbuild(Slicer_PLATFORM_CHECK:BOOL)

#-----------------------------------------------------------------------------
# Prerequisites
#-----------------------------------------------------------------------------
find_package(Git)
if(NOT GIT_FOUND)
  message(FATAL_ERROR "error: Install Git and try to re-configure")
endif()
mark_as_superbuild(GIT_EXECUTABLE)

find_package(Patch REQUIRED)
mark_as_superbuild(Patch_EXECUTABLE)

#-----------------------------------------------------------------------------
# Qt requirements
#-----------------------------------------------------------------------------
if(NOT DEFINED Slicer_REQUIRED_QT_VERSION)
  set(_required_qt_version "5.15")
  if(DEFINED Qt6_DIR)
    set(_required_qt_version "6.8")
  endif()
  set(Slicer_REQUIRED_QT_VERSION ${_required_qt_version} CACHE STRING "Minimum required Qt version" FORCE)
endif()
mark_as_superbuild(Slicer_REQUIRED_QT_VERSION)

#-----------------------------------------------------------------------------
# QtTesting compatibility check
#-----------------------------------------------------------------------------
# QtTesting is now supported under both Qt 5 and Qt 6 by the upstream
# commontk/QtTesting and CTK projects. The previous Qt 6 force-disable
# has been removed; Slicer_USE_QtTesting follows its user-facing default
# regardless of Qt major version. See https://github.com/Slicer/Slicer/issues/8839

#-----------------------------------------------------------------------------
# Python requirements
#-----------------------------------------------------------------------------
if(NOT DEFINED Slicer_REQUIRED_PYTHON_VERSION)
  set(Slicer_REQUIRED_PYTHON_VERSION "3.12.10")
endif()
mark_as_superbuild(Slicer_REQUIRED_PYTHON_VERSION)

if(NOT Slicer_REQUIRED_PYTHON_VERSION MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
  message(FATAL_ERROR "Slicer_REQUIRED_PYTHON_VERSION [${Slicer_REQUIRED_PYTHON_VERSION}] is invalid. Value expected to be formatted as X.Y.Z")
endif()

set(Slicer_REQUIRED_PYTHON_VERSION_MAJOR ${CMAKE_MATCH_1})
set(Slicer_REQUIRED_PYTHON_VERSION_MINOR ${CMAKE_MATCH_2})
set(Slicer_REQUIRED_PYTHON_VERSION_PATCH ${CMAKE_MATCH_3})

set(Slicer_REQUIRED_PYTHON_VERSION_DOT ${Slicer_REQUIRED_PYTHON_VERSION_MAJOR}.${Slicer_REQUIRED_PYTHON_VERSION_MINOR})

set(Slicer_REQUIRED_PYTHON_ABIFLAGS "")
if(Slicer_REQUIRED_PYTHON_VERSION_DOT VERSION_LESS "3.8")
  set(Slicer_REQUIRED_PYTHON_ABIFLAGS "m")
endif()

#-----------------------------------------------------------------------------
# Build shared lib by default
#------------------------------------------------------------------------------
# option(BUILD_SHARED_LIBS "Build Slicer with shared libraries." ON)
set(BUILD_SHARED_LIBS ON)
mark_as_superbuild(BUILD_SHARED_LIBS:BOOL)
set(Slicer_BUILD_SHARED ${BUILD_SHARED_LIBS})

#-----------------------------------------------------------------------------
# Slicer application options
#-----------------------------------------------------------------------------
include(SlicerApplicationOptions)

#-----------------------------------------------------------------------------
# Append the library version information to the library target properties.
#------------------------------------------------------------------------------
option(Slicer_WITH_LIBRARY_VERSION "Build with library version information" OFF)
mark_as_advanced(Slicer_WITH_LIBRARY_VERSION)
mark_as_superbuild(Slicer_WITH_LIBRARY_VERSION)
if(Slicer_WITH_LIBRARY_VERSION)
  set(Slicer_LIBRARY_PROPERTIES ${Slicer_LIBRARY_PROPERTIES}
    VERSION ${Slicer_VERSION_FULL}
    SOVERSION ${Slicer_VERSION}
  )
endif()

#-----------------------------------------------------------------------------
# General Slicer Options
#-----------------------------------------------------------------------------
include(CMakeDependentOption)

option(BUILD_TESTING "Test the project" ON)
mark_as_superbuild(BUILD_TESTING)

#option(WITH_MEMCHECK "Run tests through valgrind." OFF)
#mark_as_superbuild(WITH_MEMCHECK)

option(WITH_COVERAGE "Enable/Disable coverage" OFF)
mark_as_superbuild(WITH_COVERAGE)

option(Slicer_USE_VTK_DEBUG_LEAKS "Enable VTKs Debug Leaks functionality in both VTK and Slicer." ON)
mark_as_superbuild(Slicer_USE_VTK_DEBUG_LEAKS:BOOL)
set(VTK_DEBUG_LEAKS ${Slicer_USE_VTK_DEBUG_LEAKS})

option(Slicer_BUILD_DICOM_SUPPORT "Build Slicer with DICOM support" ON)
mark_as_superbuild(Slicer_BUILD_DICOM_SUPPORT)

option(Slicer_BUILD_DIFFUSION_SUPPORT "Build Slicer with diffusion (DWI, DTI) support" ON)
mark_as_superbuild(Slicer_BUILD_DIFFUSION_SUPPORT)

option(Slicer_BUILD_I18N_SUPPORT "Build Slicer with Internationalization support" ON)
mark_as_superbuild(Slicer_BUILD_I18N_SUPPORT)

option(Slicer_BUILD_USAGE_LOGGING_SUPPORT "Build Slicer with support for software usage logging" ON)
mark_as_superbuild(Slicer_BUILD_USAGE_LOGGING_SUPPORT)

option(Slicer_BUILD_WEBENGINE_SUPPORT "Build Slicer with Qt WebEngine support" ON)
mark_as_superbuild(Slicer_BUILD_WEBENGINE_SUPPORT)

option(Slicer_BUILD_QTLOADABLEMODULES "Build Slicer Qt Loadable Modules" ON)
mark_as_advanced(Slicer_BUILD_QTLOADABLEMODULES)
mark_as_superbuild(Slicer_BUILD_QTLOADABLEMODULES)

option(Slicer_USE_PYTHONQT "Integrate a Python-Qt interpreter into Slicer." ON)
mark_as_superbuild(Slicer_USE_PYTHONQT)

CMAKE_DEPENDENT_OPTION(
  Slicer_USE_SLICERRC "Load application startup file (.<appname>rc.py or custom app equivalent)" ON
  "Slicer_USE_PYTHONQT" OFF)
mark_as_superbuild(Slicer_USE_SLICERRC)

CMAKE_DEPENDENT_OPTION(
  Slicer_BUILD_QTSCRIPTEDMODULES "Build Slicer Python Qt Modules" ON
  "Slicer_USE_PYTHONQT" OFF)
mark_as_advanced(Slicer_BUILD_QTSCRIPTEDMODULES)
mark_as_superbuild(Slicer_BUILD_QTSCRIPTEDMODULES)

# CLI support is always enabled and cannot be disabled.
# This variable is retained only for backward compatibility with dependent projects (e.g., Slicer extensions).
# For more details, see: https://github.com/Slicer/Slicer/issues/8477
# Warn if a project attempts to disable CLI support.
if(DEFINED Slicer_BUILD_CLI_SUPPORT AND NOT Slicer_BUILD_CLI_SUPPORT)
  message(WARNING
    "Setting Slicer_BUILD_CLI_SUPPORT to OFF is not supported. "
    "CLI support is always enabled. See: "
    "https://github.com/Slicer/Slicer/issues/8477"
  )
endif()
set(Slicer_BUILD_CLI_SUPPORT ON CACHE BOOL "Build Slicer with CLI support" FORCE)
mark_as_superbuild(Slicer_BUILD_CLI_SUPPORT)
CMAKE_DEPENDENT_OPTION(
  Slicer_BUILD_CLI "Build Slicer CLI Plugins" ON
  "Slicer_BUILD_CLI_SUPPORT" OFF
  )

mark_as_superbuild(Slicer_BUILD_CLI)
CMAKE_DEPENDENT_OPTION(
  Slicer_BUILD_LEGACY_CLI "Build Slicer LEGACY_CLI Plugins" ON
  "Slicer_BUILD_CLI" OFF
  )

option(Slicer_BUILD_QT_DESIGNER_PLUGINS "Build Qt designer plugins" ON)
mark_as_superbuild(Slicer_BUILD_QT_DESIGNER_PLUGINS)

option(Slicer_BUILD_EXTENSIONMANAGER_SUPPORT "Build Slicer extensions manager" ON)
mark_as_superbuild(Slicer_BUILD_EXTENSIONMANAGER_SUPPORT)

option(Slicer_BUILD_APPLICATIONUPDATE_SUPPORT "Build Slicer with application update support" ON)
mark_as_superbuild(Slicer_BUILD_APPLICATIONUPDATE_SUPPORT)

option(Slicer_BUILD_MULTIMEDIA_SUPPORT "Build Slicer with Multimedia support" ON)
mark_as_superbuild(Slicer_BUILD_MULTIMEDIA_SUPPORT)

CMAKE_DEPENDENT_OPTION(Slicer_UPDATE_TRANSLATION "update translation" OFF "Slicer_BUILD_I18N_SUPPORT" OFF)
mark_as_advanced(Slicer_UPDATE_TRANSLATION)
mark_as_superbuild(Slicer_UPDATE_TRANSLATION)

set(Slicer_FORCED_WC_LAST_CHANGED_DATE "" CACHE STRING "Overwrite value of auto-discovered Slicer_WC_LAST_CHANGED_DATE (format: YYYY-MM-DD)")
mark_as_advanced(Slicer_FORCED_WC_LAST_CHANGED_DATE)
mark_as_superbuild(Slicer_FORCED_WC_LAST_CHANGED_DATE)

set(Slicer_FORCED_REVISION "" CACHE STRING "Overwrite value of automatically set Slicer_REVISION")
mark_as_advanced(Slicer_FORCED_REVISION)
mark_as_superbuild(Slicer_FORCED_REVISION)

set(Slicer_REVISION_TYPE "CommitCount" CACHE STRING "How to set Slicer_REVISION - from commit count or git hash." FORCE)
set_property(CACHE Slicer_REVISION_TYPE PROPERTY STRINGS
  "CommitCount"
  "Hash"
  )
mark_as_advanced(Slicer_REVISION_TYPE)
mark_as_superbuild(Slicer_REVISION_TYPE)

# Default Slicer_WC_COMMIT_COUNT_OFFSET

set(_commit_count_offsets
  # Value is chosen to provide continuity of revisions when switching from SVN to git
  # at SVN revision=28825 / git hash 47deb76d7556e40de4e25e585c4b24a63a153da5 in official Slicer repository
  # (https://github.com/Slicer/Slicer.git).
  "3037"
  # Allocate revisions for patch releases between v5.0.0 and v5.1.0
  "100"
  # Allocate revisions for patch releases between v5.2.0 and v5.3.0
  "100"
  # Allocate revisions for patch releases between v5.4.0 and v5.5.0
  "200"
  # Allocate revisions for patch releases between v5.6.0 and v5.7.0
  "200"
  # Allocate revisions for patch releases between v5.8.0 and v5.9.0
  "200"
  # Allocate revisions for patch releases between v5.10.0 and v5.11.0
  "200"
  # Allocate revisions for patch releases between v5.12.0 and v5.13.0
  "200"
  )
list(JOIN _commit_count_offsets "+" _commit_count_offsets_expr)
math(EXPR _commit_count_offset "${_commit_count_offsets_expr}")
set(Slicer_WC_COMMIT_COUNT_OFFSET "${_commit_count_offset}" CACHE INTERNAL
  "This value is added to commit count to compute Slicer_COMMIT_COUNT (that may be used to set Slicer_REVISION).")
mark_as_advanced(Slicer_WC_COMMIT_COUNT_OFFSET)
mark_as_superbuild(Slicer_WC_COMMIT_COUNT_OFFSET)

option(Slicer_USE_FOLDERS "Organize build targets into folders" ON)
mark_as_superbuild(Slicer_USE_FOLDERS)
mark_as_advanced(Slicer_USE_FOLDERS)

#-----------------------------------------------------------------------------
# Slicer version number
#-----------------------------------------------------------------------------
include(SlicerVersion)

#-----------------------------------------------------------------------------
# External projects related options
#-----------------------------------------------------------------------------
CMAKE_DEPENDENT_OPTION(
  Slicer_USE_PYTHONQT_WITH_OPENSSL "Enable PythonQt SSL support" ON
  "Slicer_USE_PYTHONQT" OFF)
mark_as_superbuild(Slicer_USE_PYTHONQT_WITH_OPENSSL)

CMAKE_DEPENDENT_OPTION(
  Slicer_USE_DCMTK_WITH_OPENSSL "Enable DCMTK SSL support" OFF
  "Slicer_USE_PYTHONQT_WITH_OPENSSL" OFF)
mark_as_superbuild(Slicer_USE_DCMTK_WITH_OPENSSL)

CMAKE_DEPENDENT_OPTION(
  Slicer_USE_NUMPY "Build Slicer with NumPy" ON
  "Slicer_USE_PYTHONQT" OFF)
mark_as_superbuild(Slicer_USE_NUMPY)

CMAKE_DEPENDENT_OPTION(
  Slicer_USE_SCIPY "Build Slicer with SciPy" ON
  "Slicer_USE_PYTHONQT;Slicer_USE_NUMPY" OFF)
mark_as_superbuild(Slicer_USE_SCIPY)

option(Slicer_USE_CTKAPPLAUNCHER "Configure ctkAppLauncher." ON)
mark_as_advanced(Slicer_USE_CTKAPPLAUNCHER)
mark_as_superbuild(Slicer_USE_CTKAPPLAUNCHER)

option(Slicer_USE_QtTesting    "Integrate QtTesting framework into Slicer." ON)
mark_as_advanced(Slicer_USE_QtTesting)
mark_as_superbuild(Slicer_USE_QtTesting)

CMAKE_DEPENDENT_OPTION(
  Slicer_USE_SimpleITK "Build Slicer with SimpleITK support" ON
  "Slicer_USE_PYTHONQT" OFF)
mark_as_superbuild(Slicer_USE_SimpleITK)

option(Slicer_USE_IDImageIO "Build Slicer with IDImageIO support" OFF)
mark_as_superbuild(Slicer_USE_IDImageIO)

option(Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT "Build Slicer with parameter serializer support" OFF)
mark_as_superbuild(Slicer_BUILD_PARAMETERSERIALIZER_SUPPORT)

set(_default_vtk_major_version "9")
set(Slicer_VTK_VERSION_MAJOR ${_default_vtk_major_version} CACHE STRING "VTK major version (9)")
set_property(CACHE Slicer_VTK_VERSION_MAJOR PROPERTY STRINGS "9")
if(NOT "${Slicer_VTK_VERSION_MAJOR}" MATCHES "^(9)$")
  message(FATAL_ERROR "error: Slicer_VTK_VERSION_MAJOR must be 9.")
endif()
mark_as_superbuild(Slicer_VTK_VERSION_MAJOR)

set(_default_vtk_minor_version "6")
set(Slicer_VTK_VERSION_MINOR ${_default_vtk_minor_version} CACHE STRING "VTK minor version (4, 5 or 6)")
set_property(CACHE Slicer_VTK_VERSION_MINOR PROPERTY STRINGS "4" "5" "6")
if(NOT "${Slicer_VTK_VERSION_MINOR}" MATCHES "^(4|5|6)$")
  message(FATAL_ERROR "error: Slicer_VTK_VERSION_MINOR must be 4, 5 or 6.")
endif()
mark_as_superbuild(Slicer_VTK_VERSION_MINOR)

set(Slicer_VTK_MINIMUM_SUPPORTED_VERSION "9.4")

#-----------------------------------------------------------------------------
# Install no development files by default, but allow the user to get
# them installed by setting Slicer_INSTALL_DEVELOPMENT to true.
#-----------------------------------------------------------------------------
option(Slicer_INSTALL_DEVELOPMENT "Install Slicer extension development files." OFF)
mark_as_advanced(Slicer_INSTALL_DEVELOPMENT)

if(NOT Slicer_INSTALL_DEVELOPMENT)
  set(Slicer_INSTALL_NO_DEVELOPMENT 1)
else()
  set(Slicer_INSTALL_NO_DEVELOPMENT 0)
endif()

#-----------------------------------------------------------------------------
# Documentation
#-----------------------------------------------------------------------------
option(Slicer_BUILD_DOCUMENTATION "Build documentation (Doxygen, sphinx, ...)" ON)
mark_as_advanced(Slicer_BUILD_DOCUMENTATION)
mark_as_superbuild(Slicer_BUILD_DOCUMENTATION)

if(Slicer_BUILD_DOCUMENTATION)
  find_package(Doxygen QUIET)

  if(DOXYGEN_DOT_FOUND)
    mark_as_superbuild(DOXYGEN_EXECUTABLE)

    set(DOCUMENTATION_ARCHIVES_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
     CACHE PATH "Where documentation archives should be stored")
    mark_as_advanced(DOCUMENTATION_ARCHIVES_OUTPUT_DIRECTORY)
    mark_as_superbuild(DOCUMENTATION_ARCHIVES_OUTPUT_DIRECTORY)

    option(DOCUMENTATION_TARGET_IN_ALL "Include the custom target for building documentation in 'all'" OFF)
    mark_as_advanced(DOCUMENTATION_TARGET_IN_ALL)
    mark_as_superbuild(DOCUMENTATION_TARGET_IN_ALL)
  endif()
endif()

#-----------------------------------------------------------------------------
# CTestCustom
#-----------------------------------------------------------------------------
configure_file(
  CMake/CTestCustom.cmake.in
  ${CMAKE_BINARY_DIR}/CTestCustom.cmake
  COPYONLY
  )

#-----------------------------------------------------------------------------
# Additional CXX/C Flags
#-----------------------------------------------------------------------------
set(ADDITIONAL_C_FLAGS "" CACHE STRING "Additional C Flags")
mark_as_advanced(ADDITIONAL_C_FLAGS)
set(ADDITIONAL_CXX_FLAGS "" CACHE STRING "Additional CXX Flags")
mark_as_advanced(ADDITIONAL_CXX_FLAGS)

#-----------------------------------------------------------------------------
# CMake Function(s) and Macro(s)
#-----------------------------------------------------------------------------
include(CMakeParseArguments)
include(SlicerMacroBuildScriptedCLI)
include(SlicerMacroBuildScriptedModule)
include(SlicerMacroGetOperatingSystemArchitectureBitness)
if(Slicer_BUILD_I18N_SUPPORT)
  include(SlicerMacroTranslation)
endif()
include(SlicerFunctionInstallLibrary)
include(SlicerFunctionAddPythonQtResources)

set(Slicer_QRCC_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/Scripts/qrcc.py" CACHE INTERNAL "Path to qrcc.py script used in slicerFunctionAddPythonQtResources() CMake function")

#-----------------------------------------------------------------------------
# Internationalization
#-----------------------------------------------------------------------------
if(Slicer_BUILD_I18N_SUPPORT)
  set(Slicer_LANGUAGES
    "fr"
    CACHE STRING "Semicolon separated list of supported Slicer languages. Expected format : language[_country] (both en and en_US are supported)."
    )
  mark_as_advanced(Slicer_LANGUAGES)
  mark_as_superbuild(Slicer_LANGUAGES)
endif()

#-----------------------------------------------------------------------------
# Set Slicer_{C,CXX}_REQUIRED_FLAGS variables
#-----------------------------------------------------------------------------
include(SlicerBlockCXXRequiredFlags)

#-----------------------------------------------------------------------------
# Slicer directories
#-----------------------------------------------------------------------------
include(SlicerDirectories)

# SlicerExecutionModel output directories
if(Slicer_SUPERBUILD)
  set(_sem_output_dir ${CMAKE_BINARY_DIR}/${Slicer_BINARY_INNER_SUBDIR})
else()
  set(_sem_output_dir ${CMAKE_BINARY_DIR})
endif()
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_RUNTIME_OUTPUT_DIRECTORY)
  set(SlicerExecutionModel_DEFAULT_CLI_RUNTIME_OUTPUT_DIRECTORY ${_sem_output_dir}/${Slicer_CLIMODULES_BIN_DIR})
endif()
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_LIBRARY_OUTPUT_DIRECTORY)
  set(SlicerExecutionModel_DEFAULT_CLI_LIBRARY_OUTPUT_DIRECTORY ${_sem_output_dir}/${Slicer_CLIMODULES_LIB_DIR})
endif()
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_ARCHIVE_OUTPUT_DIRECTORY)
  set(SlicerExecutionModel_DEFAULT_CLI_ARCHIVE_OUTPUT_DIRECTORY ${_sem_output_dir}/${Slicer_CLIMODULES_LIB_DIR})
endif()


#-----------------------------------------------------------------------------
# Extension directories
#-----------------------------------------------------------------------------
# NOTE: Make sure to update vtkSlicerApplicationLogic::IsEmbeddedModule if
#       the following variable is changed.
set(Slicer_EXTENSIONS_DIRBASENAME "Extensions")

#-----------------------------------------------------------------------------
# Slicer OpenGL Options
#-----------------------------------------------------------------------------
if(UNIX AND NOT APPLE)
  set(OpenGL_GL_PREFERENCE "LEGACY")
  mark_as_superbuild(OpenGL_GL_PREFERENCE:STRING)
endif()

#-----------------------------------------------------------------------------
# Slicer VTK Options
#-----------------------------------------------------------------------------

# Slicer build is only tested with Sequential and TBB. OpenMP might work.
set(Slicer_DEFAULT_VTK_SMP_IMPLEMENTATION_TYPE "TBB")
set(Slicer_VTK_SMP_IMPLEMENTATION_TYPE ${Slicer_DEFAULT_VTK_SMP_IMPLEMENTATION_TYPE}
  CACHE STRING "Which multi-threaded parallelism implementation to use in VTK. Options are Sequential or TBB.")
set_property(CACHE Slicer_VTK_SMP_IMPLEMENTATION_TYPE
  PROPERTY
    STRINGS Sequential TBB)
mark_as_superbuild(Slicer_VTK_SMP_IMPLEMENTATION_TYPE)
if(${Slicer_VTK_SMP_IMPLEMENTATION_TYPE} STREQUAL "TBB")
  set(Slicer_USE_TBB TRUE)
else()
  set(Slicer_USE_TBB FALSE)
endif()

message(STATUS "Configuring VTK")
message(STATUS "  Slicer_VTK_SMP_IMPLEMENTATION_TYPE is ${Slicer_VTK_SMP_IMPLEMENTATION_TYPE}")
message(STATUS "  Slicer_VTK_VERSION_MAJOR is ${Slicer_VTK_VERSION_MAJOR}")
message(STATUS "  Slicer_VTK_VERSION_MINOR is ${Slicer_VTK_VERSION_MINOR}")
message(STATUS "  Slicer_VTK_MINIMUM_SUPPORTED_VERSION is ${Slicer_VTK_MINIMUM_SUPPORTED_VERSION}")

#-----------------------------------------------------------------------------
# Slicer_VTK_COMPONENTS
#-----------------------------------------------------------------------------
set(Slicer_VTK_COMPONENTS
  FiltersExtraction
  FiltersFlowPaths
  FiltersGeometry
  FiltersParallel
  GUISupportQtSQL
  IOExport
  IOImage
  IOLegacy
  IOPLY
  IOXML
  ImagingMath
  ImagingMorphological
  ImagingStatistics
  ImagingStencil
  InteractionImage
  RenderingContextOpenGL2
  RenderingQt
  RenderingVolumeOpenGL2
  TestingRendering
  ViewsQt
  zlib
  )

if(Slicer_USE_PYTHONQT)
  list(APPEND Slicer_VTK_COMPONENTS
    WrappingPythonCore
    )
endif()

#-----------------------------------------------------------------------------
# Qt - Slicer_REQUIRED_QT_MODULES
#-----------------------------------------------------------------------------
# Module name should be specified as they appear in FindQt5.cmake.
# For example, the module name associated with the variable QT_USE_QTXML is QTXML.
# Note that the modules will be installed when packaging.
  set(Slicer_REQUIRED_QT_MODULES
    Core Widgets
    Network OpenGL
    PrintSupport
    UiTools #no dll
    Xml
    Svg Sql
    )

  if(Slicer_USE_QtTesting AND Slicer_REQUIRED_QT_VERSION VERSION_LESS "6")
    # XmlPatterns was removed in Qt 6; CTK's QtTesting wrapper already guards
    # its usage. Only require the module for Qt 5 builds.
    list(APPEND Slicer_REQUIRED_QT_MODULES
      XmlPatterns
      )
  endif()

  if(Slicer_BUILD_MULTIMEDIA_SUPPORT)
    list(APPEND Slicer_REQUIRED_QT_MODULES
      Multimedia
      MultimediaWidgets
      )
  endif()

  if(Slicer_REQUIRED_QT_VERSION VERSION_GREATER_EQUAL "6")
    list(APPEND Slicer_REQUIRED_QT_MODULES
      Core5Compat
      OpenGLWidgets
      StateMachine
      )
  endif()

  # Add the Qt5X11Extras component for Linux systems
  # This component is needed for building VTK
  if(UNIX AND NOT APPLE AND Slicer_REQUIRED_QT_VERSION VERSION_LESS "6")
    list(APPEND Slicer_REQUIRED_QT_MODULES X11Extras)
  endif()

  if(Slicer_REQUIRED_QT_VERSION VERSION_GREATER_EQUAL "6")
    find_package(Qt6 COMPONENTS Core QUIET)
    set(_webengine_qt_component "WebEngineCore")
    # Qt 6.9+ requires GuiPrivate for accessing QWindowsWindow (in qSlicerApplication::setHasBorderInFullScreen)
    if(WIN32 AND Qt6_VERSION VERSION_GREATER_EQUAL "6.9")
      list(APPEND Slicer_REQUIRED_QT_MODULES GuiPrivate)
    endif()
  else()
    find_package(Qt5 COMPONENTS Core QUIET)
    set(_webengine_qt_component "WebEngine")
  endif()
  if(Slicer_BUILD_WEBENGINE_SUPPORT)
    list(APPEND Slicer_REQUIRED_QT_MODULES
      ${_webengine_qt_component}
      WebEngineWidgets
      WebChannel
      Quick
      QuickWidgets
      )
  endif()
  # Both "extension manager" and "application update" require qRestApi external
  # project itself depending on Qt's Qml module
  if(Slicer_BUILD_EXTENSIONMANAGER_SUPPORT OR Slicer_BUILD_APPLICATIONUPDATE_SUPPORT)
    list(APPEND Slicer_REQUIRED_QT_MODULES Qml)
  endif()
  if(Slicer_BUILD_I18N_SUPPORT)
    list(APPEND Slicer_REQUIRED_QT_MODULES LinguistTools) # no dll
  endif()
  if(BUILD_TESTING)
    list(APPEND Slicer_REQUIRED_QT_MODULES Test)
  endif()
list(APPEND Slicer_REQUIRED_QT_MODULES ${Slicer_ADDITIONAL_REQUIRED_QT_MODULES})

  set(QT_LIBRARIES)
  foreach(lib IN LISTS Slicer_REQUIRED_QT_MODULES)
    if(lib MATCHES "^(LinguistTools)$")
      continue()
    endif()
    if(Slicer_REQUIRED_QT_VERSION VERSION_GREATER_EQUAL "6")
      list(APPEND QT_LIBRARIES Qt6::${lib})
    else()
      list(APPEND QT_LIBRARIES Qt5::${lib})
    endif()
  endforeach()

if(DEFINED Slicer_ADDITIONAL_REQUIRED_QT_MODULES)
  mark_as_superbuild(Slicer_ADDITIONAL_REQUIRED_QT_MODULES:STRING)
endif()

mark_as_superbuild(Slicer_USE_SYSTEM_QT:BOOL)

#-----------------------------------------------------------------------------
# Qt plugins (designer, imageformats, ...) relative directories
#-----------------------------------------------------------------------------
set(Slicer_QtPlugins_DIR "${CMAKE_INSTALL_LIBDIR}/QtPlugins")
set(Slicer_INSTALL_QtPlugins_DIR "${Slicer_INSTALL_ROOT}${Slicer_QtPlugins_DIR}")

#-----------------------------------------------------------------------------
# Qt
#-----------------------------------------------------------------------------
include(SlicerBlockFindQtAndCheckVersion)
if(Slicer_REQUIRED_QT_VERSION VERSION_GREATER_EQUAL "6")
  mark_as_superbuild(VARS Qt6_DIR LABELS "FIND_PACKAGE")
  set(Slicer_HAVE_QT6 1)
else()
  mark_as_superbuild(VARS Qt5_DIR LABELS "FIND_PACKAGE")
  set(Slicer_HAVE_QT5 1)
endif()

    set(Slicer_HAVE_WEBKIT_SUPPORT 0)

#
# If qmake or Qt5Config are associated with a system location, explicitly mark Qt as such. Doing so
# will prevent system path from being prepended to PATH or (DY)LD_LIBRARY_PATH
# when generating the launcher settings and avoid system libraries symbols from
# conflicting with Slicer version of these libraries.
#
# See https://issues.slicer.org/view.php?id=3574
#
if(NOT DEFINED Slicer_USE_SYSTEM_QT)
  if(Slicer_REQUIRED_QT_VERSION VERSION_GREATER_EQUAL "6")
    set(_qt_dir_varname "Qt6_DIR")
  else()
    set(_qt_dir_varname "Qt5_DIR")
  endif()
  foreach(_path IN ITEMS
      "/usr/lib/"
      "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/"
      # The following line is a consequence of multiarch usr merge (https://wiki.debian.org/UsrMerge)
      # /usr/lib will bye symlinked to /lib. In this case Qt5_DIR could be reported as
      # /lib/${CMAKE_LIBRARY_ARCHITECTURE}/
      "/lib/"
      "/lib/${CMAKE_LIBRARY_ARCHITECTURE}/"
      "/usr/lib32/"
      "/usr/lib64/"
      "/usr/local/lib/"
      # homebrew
      "/usr/local/Cellar/lib/"
      # macport
      "/opt/lib"
      "/opt/local/lib"
      )
    set(_qt_dir "${${_qt_dir_varname}}")
    if("${_qt_dir}" MATCHES "^${_path}")
      set(Slicer_USE_SYSTEM_QT ON)
      message(STATUS "")
      message(STATUS "Initializing Slicer_USE_SYSTEM_QT to ON (${_qt_dir_varname} [${_qt_dir}] associated with a system location: ${_path})")
      message(STATUS "")
      break()
    endif()
  endforeach()
endif()

#-----------------------------------------------------------------------------
# Option for krb5-gssapi-stub
#
# This controls whether a stub library named `libgssapi_krb5.so.2` is built
# to satisfy GSSAPI symbol requirements in Qt5Network (see below).
#
# By default, the option is automatically set to ON only on Linux when
# Slicer is **not using a system Qt**.
#
# You may override the default by configuring with:
#   -DSlicer_BUILD_KRB5_GSSAPI_STUB:BOOL=ON or OFF
#-----------------------------------------------------------------------------
if(DEFINED Slicer_BUILD_KRB5_GSSAPI_STUB)
  if(Slicer_USE_SYSTEM_QT AND Slicer_BUILD_KRB5_GSSAPI_STUB)
    message(WARNING "Unsetting Slicer_BUILD_KRB5_GSSAPI_STUB because Slicer_USE_SYSTEM_QT is 'ON'")
    unset(Slicer_BUILD_KRB5_GSSAPI_STUB CACHE)
  endif()
endif()

if(NOT DEFINED Slicer_BUILD_KRB5_GSSAPI_STUB)
  if(UNIX AND NOT APPLE AND NOT Slicer_USE_SYSTEM_QT)
    set(Slicer_BUILD_KRB5_GSSAPI_STUB ON)
  else()
    set(Slicer_BUILD_KRB5_GSSAPI_STUB OFF)
  endif()
endif()
mark_as_superbuild(Slicer_BUILD_KRB5_GSSAPI_STUB:BOOL)

message(STATUS "Setting Slicer_BUILD_KRB5_GSSAPI_STUB to '${Slicer_BUILD_KRB5_GSSAPI_STUB}'")

#-----------------------------------------------------------------------------
# Testing
#-----------------------------------------------------------------------------
# NOTE: For CTEST_USE_LAUNCHER to work, it's required to include CTest at the superbuild level.
#       See https://www.kitware.com/blog/home/post/11

set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) # Do not add CTest default targets
include(CTest)

# Add Experimental target - Adapted from CTestTargets.cmake
set(__conf_types "")
if(CMAKE_CONFIGURATION_TYPES)
  # We need to pass the configuration type on the test command line.
  set(__conf_types -C "$<CONFIG>")
endif()
add_custom_target(Experimental ${CMAKE_CTEST_COMMAND} ${__conf_types} -D Experimental)

if(Slicer_SUPERBUILD)
  set(_slicer_dir ${CMAKE_BINARY_DIR}/${Slicer_BINARY_INNER_SUBDIR})
else()
  set(_slicer_dir ${Slicer_BINARY_DIR})
endif()
set(Slicer_LAUNCHER_EXECUTABLE ${_slicer_dir}/${Slicer_MAIN_PROJECT_APPLICATION_NAME}${CMAKE_EXECUTABLE_SUFFIX})
set(Slicer_LAUNCH_COMMAND ${Slicer_LAUNCHER_EXECUTABLE} --launch)

#-----------------------------------------------------------------------------
# ExternalData Object Stores configuration
#-----------------------------------------------------------------------------
set(ExternalData_OBJECT_STORES_DEFAULT "${Slicer_BINARY_DIR}/ExternalData/Objects")
if(DEFINED "ENV{ExternalData_OBJECT_STORES}")
  set(ExternalData_OBJECT_STORES_DEFAULT $ENV{ExternalData_OBJECT_STORES})
endif()
if(NOT DEFINED ExternalData_OBJECT_STORES)
  set(ExternalData_OBJECT_STORES "${ExternalData_OBJECT_STORES_DEFAULT}")
endif()
message(STATUS "Setting ExternalData_OBJECT_STORES to '${ExternalData_OBJECT_STORES}'")

set(Slicer_ExternalData_DATA_MANAGEMENT_TARGET ${PROJECT_NAME}Data)

#-----------------------------------------------------------------------------
# Get operating system, architecture and bitness
#-----------------------------------------------------------------------------
SlicerMacroGetOperatingSystemArchitectureBitness(VAR_PREFIX Slicer)
message(STATUS "Configuring ${Slicer_MAIN_PROJECT_APPLICATION_NAME} for [${Slicer_OS}-${Slicer_ARCHITECTURE}]")

#-----------------------------------------------------------------------------
# Superbuild script
#-----------------------------------------------------------------------------
if(Slicer_SUPERBUILD)
  include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake")
  return()
endif()

foreach(dep QT ${Slicer_DEPENDENCIES})
  if(Slicer_USE_SYSTEM_${dep})
    message(STATUS "Using system ${dep}")
  endif()
endforeach()

#-----------------------------------------------------------------------------
# Update CMake module path
#------------------------------------------------------------------------------
set(CMAKE_MODULE_PATH
  ${vtkAddon_CMAKE_DIR}
  ${CMAKE_MODULE_PATH}
  )

#-----------------------------------------------------------------------------
# CMake Qt automatic code generation
#-----------------------------------------------------------------------------
set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP "Generated")

# Ensure response files are used earlier to avoid "name too long" errors
# with Automatic MOC and UIC for large projects like qSlicerBaseQTCore,
# which can generate very long command lines, by limiting AUTOGEN command
# line length. Use 16000 as a conservative value (notably on Windows,
# where practical limits are below 32k).
if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.29.0")
  set(CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX "16000")
endif()

#-----------------------------------------------------------------------------
# Folders
#-----------------------------------------------------------------------------
set_property(GLOBAL PROPERTY USE_FOLDERS ${Slicer_USE_FOLDERS})

#-----------------------------------------------------------------------------
# Clear SlicerTargets.cmake
#-----------------------------------------------------------------------------
file(WRITE "${Slicer_BINARY_DIR}/SlicerTargets.cmake" "")

#-----------------------------------------------------------------------------
# Output directories.
#------------------------------------------------------------------------------
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${Slicer_BINARY_DIR}/${Slicer_BIN_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${Slicer_BINARY_DIR}/${Slicer_BIN_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${Slicer_BINARY_DIR}/${Slicer_LIB_DIR})

#-----------------------------------------------------------------------------
# Slicer Home
#------------------------------------------------------------------------------
set(Slicer_HOME "${Slicer_BINARY_DIR}")

#-----------------------------------------------------------------------------
# Slicer include and libraries subdirectory
#-----------------------------------------------------------------------------
set(Slicer_Base_LIBRARIES CACHE INTERNAL "Slicer Base libraries" FORCE)
set(Slicer_Base_INCLUDE_DIRS CACHE INTERNAL "Slicer Base includes" FORCE)
set(Slicer_ModuleLogic_INCLUDE_DIRS CACHE INTERNAL "Slicer Module logic includes" FORCE)
set(Slicer_ModuleMRML_INCLUDE_DIRS CACHE INTERNAL "Slicer Module MRML includes" FORCE)
set(Slicer_ModuleWidgets_INCLUDE_DIRS CACHE INTERNAL "Slicer Module Widgets includes" FORCE)

#-----------------------------------------------------------------------------
# ExternalData configuration for test data
#-----------------------------------------------------------------------------
list(APPEND ExternalData_URL_TEMPLATES "https://github.com/Slicer/SlicerTestingData/releases/download/%(algo)/%(hash)")
include(ExternalData)
set(Slicer_ExternalData_OBJECT_STORES ${ExternalData_OBJECT_STORES})
set(Slicer_ExternalData_URL_TEMPLATES ${ExternalData_URL_TEMPLATES})

#-----------------------------------------------------------------------------
# Testing
#-----------------------------------------------------------------------------
include(SlicerMacroConfigureModuleCxxTestDriver)
include(SlicerMacroSimpleTest)
include(SlicerMacroPythonTesting)
include(SlicerMacroConfigureGenericCxxModuleTests)
include(SlicerMacroConfigureGenericPythonModuleTests)

if(BUILD_TESTING)
  # The variable CXX_TEST_PATH should *NOT* be used when adding tests.
  # Instead the the $<TARGET_FILE:...> generator expression should be
  # considered. See r16586 for more details.
  set(CXX_TEST_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) # XXX

  configure_file(
    ${Slicer_SOURCE_DIR}/CTestConfig.cmake
    ${Slicer_BINARY_DIR}/${Slicer_BIN_DIR}/CTestConfig.cmake
    COPYONLY)

  add_subdirectory(CMake/Testing)
  add_subdirectory(Extensions/CMake/Testing)

  set(SEM_LAUNCH_COMMAND ${Slicer_LAUNCH_COMMAND})
  set(SEM_DATA_MANAGEMENT_TARGET ${Slicer_ExternalData_DATA_MANAGEMENT_TARGET})
endif()

#-----------------------------------------------------------------------------
# NOTE: How to include dependent packages ?
#-----------------------------------------------------------------------------

# Every Slicer sub project, library or module is responsible
# to call find_package (optionally specifying a list of components)
# and (if it applies) include the associated "use file".
#
# This means that moving forward most of the find_package calls listed below
# will be removed.

#-----------------------------------------------------------------------------
# VTK
#-----------------------------------------------------------------------------
find_package(VTK REQUIRED)
set(VTK_LIBRARIES "")
find_package(VTK ${Slicer_VTK_MINIMUM_SUPPORTED_VERSION} COMPONENTS ${Slicer_VTK_COMPONENTS} REQUIRED)
set(VTK_GUI_SUPPORT_QT_TARGET_NAME "VTK::GUISupportQt")

if(NOT TARGET ${VTK_GUI_SUPPORT_QT_TARGET_NAME})
  message(FATAL_ERROR "error: VTK was not configured to use Qt, you probably need "
                    "to recompile it with VTK_USE_GUISUPPORT ON, VTK_Group_Qt ON, "
                    "Note that Qt >= ${Slicer_REQUIRED_QT_VERSION} is *required*")
endif()

#-----------------------------------------------------------------------------
# CTK
#-----------------------------------------------------------------------------
find_package(CTK REQUIRED)
include(${CTK_USE_FILE})

#-----------------------------------------------------------------------------
# Update CMake module path
#-----------------------------------------------------------------------------
set(CMAKE_MODULE_PATH
  ${CTK_CMAKE_DIR}
  ${CMAKE_MODULE_PATH}
  ${CTK_CMAKE_UTILITIES_DIR})

#-----------------------------------------------------------------------------
# CMake Function(s) and Macro(s) from CTK
#-----------------------------------------------------------------------------
include(ctkFunctionAddExecutableUtf8)

#-----------------------------------------------------------------------------
# Set Slicer_PYTHON_VERSION and Slicer_PYTHON_VERSION_DOT
#-----------------------------------------------------------------------------
if(Slicer_USE_PYTHONQT)
  # CMP0148 (CMake 3.27) removes FindPythonLibs/FindPythonInterp when NEW.
  # Retain them until the migration to find_package(Python3) is completed.
  cmake_policy(SET CMP0148 OLD)
  find_package(PythonLibs ${Slicer_REQUIRED_PYTHON_VERSION_DOT} REQUIRED)
  find_package(PythonInterp ${Slicer_REQUIRED_PYTHON_VERSION_DOT} REQUIRED)

  # Extract python lib path
  get_filename_component(PYTHON_DIR_PATH ${PYTHON_EXECUTABLE} PATH)
  set(PYTHON_LIBRARY_PATH ${PYTHON_DIR_PATH}/../lib)
  if(WIN32)
    set(PYTHON_LIBRARY_PATH ${PYTHON_DIR_PATH})
  endif()

  # Extract python version components
  ctkMacroSetPaths("${PYTHON_LIBRARY_PATH}")
  function(_python_sys_attribute attribute varname)
    execute_process(
      COMMAND ${PYTHON_EXECUTABLE} -c "import sys; print(sys.${attribute})"
      OUTPUT_VARIABLE value
      OUTPUT_STRIP_TRAILING_WHITESPACE)
    set(${varname} ${value} PARENT_SCOPE)
  endfunction()

  _python_sys_attribute("version_info.major" Slicer_PYTHON_VERSION_MAJOR)
  _python_sys_attribute("version_info.minor" Slicer_PYTHON_VERSION_MINOR)
  _python_sys_attribute("version_info.micro" Slicer_PYTHON_VERSION_PATCH)
  _python_sys_attribute("abiflags" Slicer_PYTHON_ABIFLAGS)

  set(Slicer_PYTHON_VERSION_DOT "${Slicer_PYTHON_VERSION_MAJOR}.${Slicer_PYTHON_VERSION_MINOR}")
  set(Slicer_PYTHON_VERSION "${Slicer_PYTHON_VERSION_MAJOR}${Slicer_PYTHON_VERSION_MINOR}")

  message(STATUS "Configuring ${Slicer_MAIN_PROJECT_APPLICATION_NAME} with python ${Slicer_PYTHON_VERSION_DOT}${Slicer_PYTHON_ABIFLAGS}")

  set(CTK_COMPILE_PYTHON_SCRIPTS_GLOBAL_TARGET_NAME "Slicer")
endif()

#-----------------------------------------------------------------------------
# PythonQt
#-----------------------------------------------------------------------------
if(Slicer_USE_PYTHONQT)
  # Find PythonQt package so that PYTHONQT_USE_RELEASE_PYTHON_FALLBACK is defined
  # See https://github.com/commontk/PythonQt/issues/7
  # and https://github.com/commontk/PythonQt/issues/8
  find_package(PythonQt REQUIRED)
  link_directories(${PYTHONQT_INSTALL_DIR}/lib)
  include(ctkMacroWrapPythonQt)
  include(ctkMacroCompilePythonScript)

  # Enable qMRMLWidgets python wrapping
  set(MRMLWidgets_WRAP_PYTHON ON)

  # See vtkAddon/CMake/vtkMacroKitPythonWrap.cmake
  set(Slicer_VTK_WRAP_HIERARCHY_DIR ${Slicer_BINARY_DIR})
endif()

#-----------------------------------------------------------------------------
# Teem
#-----------------------------------------------------------------------------
# XXX Waiting teem provide a correct TeemConfig.cmake exporting targets, this is needed
#     to ensure the link_directories for teem is defined and avoid error like:
#     warning: libteem.so.1, needed by ../../../../bin/libvtkTeem.so, may conflict with libteem.so.2
find_package(Teem REQUIRED)
include(${Teem_USE_FILE})

#-----------------------------------------------------------------------------
# SlicerExecutionModel settings
#-----------------------------------------------------------------------------
set(SlicerExecutionModel_EXTRA_INCLUDE_DIRECTORIES "" CACHE INTERNAL "SlicerExecutionModel extra includes" FORCE)
set(SlicerExecutionModel_EXTRA_EXECUTABLE_TARGET_LIBRARIES "" CACHE INTERNAL "SlicerExecutionModel extra executable target libraries" FORCE)

#-----------------------------------------------------------------------------
# Set Slicer builtin libraries *_DIR variables
#-----------------------------------------------------------------------------
set(GenerateLM_DIR ${Slicer_BINARY_DIR}/Libs/GenerateLM)
set(vtkITK_DIR ${Slicer_BINARY_DIR}/Libs/vtkITK)

#-----------------------------------------------------------------------------
# Set COVERAGE_{C,CXX}_FLAGS variables
#-----------------------------------------------------------------------------
include(SlicerBlockCXXCoverageFlags)

#-----------------------------------------------------------------------------
# Set ITK_REQUIRED_{C,CXX}_FLAGS variables
#-----------------------------------------------------------------------------
include(ITKPlatformSpecificChecks)

#-----------------------------------------------------------------------------
# Set CMAKE_{C,CXX}_FLAGS variables
#-----------------------------------------------------------------------------
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_INIT} ${Slicer_REQUIRED_C_FLAGS} ${ITK_REQUIRED_C_FLAGS} ${COVERAGE_C_FLAGS} ${ADDITIONAL_C_FLAGS}" CACHE STRING "CMake C Flags" FORCE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_INIT} ${Slicer_REQUIRED_CXX_FLAGS} ${ITK_REQUIRED_CXX_FLAGS} ${COVERAGE_CXX_FLAGS} ${ADDITIONAL_CXX_FLAGS}" CACHE STRING "CMake CXX Flags" FORCE)

#-----------------------------------------------------------------------------
# Set the header template which defines custom export/import macros
# for shared libraries
#-----------------------------------------------------------------------------
set(QMRML_EXPORT_HEADER_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/Libs/MRML/Widgets/CMake/qMRMLExport.h.in")

#-----------------------------------------------------------------------------
# Configure testing scripts
#-----------------------------------------------------------------------------
set(files
  Utilities/Scripts/computeCodeCoverageLocally.sh
  Utilities/Scripts/computeCodeCoverageForOneTest.sh
  Utilities/Scripts/runExperimentalOffScreen.sh
  Utilities/Scripts/setupSlicerEnvironment.sh
  )
foreach(f ${files})
  configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/${f}.in
    ${CMAKE_CURRENT_BINARY_DIR}/${f}
    )
endforeach()

#-----------------------------------------------------------------------------
# Includes
#-----------------------------------------------------------------------------
include_directories(
  ${CMAKE_CURRENT_SOURCE_DIR}/CMake
  ${CMAKE_CURRENT_SOURCE_DIR}
  ${CMAKE_CURRENT_BINARY_DIR}
  )

#-----------------------------------------------------------------------------
# krb5-gssapi-stub
#-----------------------------------------------------------------------------
# Build a minimal stub library that exports the GSSAPI symbol
# `GSS_C_NT_HOSTBASED_SERVICE` expected by Qt5Network when it is built
# with GSSAPI support enabled.
#
# This avoids shipping or linking against system Kerberos libraries that may
# introduce OpenSSL version mismatches or be missing entirely.
#
# This situation commonly occurs when using a non-system Qt installation,
# such as one installed via the Qt online installer.
#
# Since Slicer does not use GSSAPI/Kerberos authentication, this stub safely
# prevents runtime errors from unresolved symbols.
if(Slicer_BUILD_KRB5_GSSAPI_STUB)
  add_subdirectory(Libs/krb5-gssapi-stub)
endif()

#-----------------------------------------------------------------------------
# Subdirectories
#-----------------------------------------------------------------------------

add_subdirectory(Libs)
add_subdirectory(Base)
add_subdirectory(Resources)
add_subdirectory(Utilities/Scripts)
if(Slicer_BUILD_EXTENSIONMANAGER_SUPPORT)
  add_subdirectory(Utilities/Scripts/SlicerWizard)
endif()

#-----------------------------------------------------------------------------
# Macro useful to build ModuleLogic, ModuleMRML and ModuleWidgets libraries
#-----------------------------------------------------------------------------
include(SlicerMacroBuildModuleQtLibrary)
include(SlicerMacroBuildModuleVTKLibrary)
include(SlicerMacroPythonWrapModuleVTKLibrary)
include(SlicerMacroBuildModuleLogic)
include(SlicerMacroBuildModuleMRML)
include(SlicerMacroBuildModuleWidgets)

#-----------------------------------------------------------------------------
# Slicer Core and GUI library
#-----------------------------------------------------------------------------
set(Slicer_CORE_LIBRARY qSlicerBaseQTCore)
set(Slicer_GUI_LIBRARY qSlicerBaseQTApp)

#-----------------------------------------------------------------------------
# Applications
#-----------------------------------------------------------------------------
add_subdirectory(Modules/Core)
add_subdirectory(Base/QTApp)
add_subdirectory(Modules)
add_subdirectory(
  ${Slicer_APPLICATIONS_DIR}
  ${CMAKE_CURRENT_BINARY_DIR}/Applications
  )

#-----------------------------------------------------------------------------
# Templates
#-----------------------------------------------------------------------------
if(Slicer_BUILD_EXTENSIONMANAGER_SUPPORT)
  add_subdirectory(Utilities/Templates)
endif()

#-----------------------------------------------------------------------------
set(Slicer_USE_FILE ${Slicer_BINARY_DIR}/UseSlicer.cmake)
configure_file(
  ${Slicer_SOURCE_DIR}/CMake/UseSlicer.cmake.in
  ${Slicer_USE_FILE} COPYONLY)

configure_file(
  ${Slicer_SOURCE_DIR}/CMake/SlicerConfigVersion.cmake.in
  ${Slicer_BINARY_DIR}/SlicerConfigVersion.cmake @ONLY)

set(Slicer_EXTENSION_GENERATE_CONFIG ${Slicer_SOURCE_DIR}/CMake/SlicerExtensionGenerateConfig.cmake)
set(Slicer_EXTENSION_CPACK ${Slicer_SOURCE_DIR}/CMake/SlicerExtensionCPack.cmake)

# --------------------------------------------------------------------------
# Bundle remote modules and extensions adding source directories.
# --------------------------------------------------------------------------

set(extensions_build_dir "${Slicer_BINARY_DIR}/E")

# Configure a no-op SlicerConfig and SlicerConfigVersion for bundled projects
set(Slicer_DIR ${extensions_build_dir})
configure_file(
  ${Slicer_SOURCE_DIR}/CMake/SlicerConfig.cmake.in
  ${Slicer_DIR}/SlicerConfig.cmake @ONLY)

configure_file(
  ${Slicer_SOURCE_DIR}/CMake/SlicerConfigVersion.cmake.in
  ${Slicer_DIR}/SlicerConfigVersion.cmake @ONLY)

#
# Directories can be set in three ways:
#
# (1) Automatically by specifying the label 'REMOTE_MODULE' when
#     calling 'Slicer_Remote_Add' from SuperBuild.cmake.
#
# (2) Automatically by specifying the label 'REMOTE_EXTENSION' when
#     calling 'Slicer_Remote_Add' from SuperBuild.cmake.
#
# (3) Explicitly by configuring the project using the following syntax:
#  cmake -DSlicer_EXTENSION_SOURCE_DIRS:STRING=/path/to/ExtensionA;/path/to/ExtensionB /path/to/source/Slicer
#

#
# Support for "SuperBuild-type" extension:
#
# * An extension is considered to be of type "SuperBuild" if a directory
#   "<extension_dir>/SuperBuild" or "<extension_dir>/Superbuild" exists.
#   Corresponding directory is appended to EXTERNAL_PROJECT_ADDITIONAL_DIRS.
#
# * If variable "<extension_name>_EXTERNAL_PROJECT_EXCLUDE_ALL" is set to TRUE, corresponding SuperBuild directory
#   is not appended to EXTERNAL_PROJECT_ADDITIONAL_DIRS.
#
# * Associated external projects are globbed using expression of the form
#   "<extension_dir>/(SuperBuild|Superbuild)/External_*.cmake".
#
# * List of external project names is extracted from the "External_<projectName>.cmake"
#   files and appended to Slicer_DEPENDENCIES. This ensures they are build before Slicer inner build.
#   Setting variable "<extension_name>_EXTERNAL_PROJECT_DEPENDENCIES" to a list of <projectName> allows
#   to override the list of <projectName> appended to Slicer_DEPENDENCIES.
#
# * Variable Slicer_BUNDLED_EXTENSION_NAMES is updated with the names of external project
#   and passed to Slicer inner build. It is then used in SlicerCPack. to package associated
#   external projects if the cache variable <extensionName>_CPACK_INSTALL_CMAKE_PROJECTS
#   was defined in the extension sources.
#
# Corresponding logic is implemented in SuperBuild.cmake
#

function(_add_extension_source_dir extension_source_dir what)
  get_filename_component(extension_source_dir ${extension_source_dir} REALPATH)
  get_filename_component(extension_source_dirname ${extension_source_dir} NAME_WE)
  message(STATUS "--------------------------------------------------")
  message(STATUS "Configuring ${what}: ${extension_source_dirname}")
  set(ExternalData_SOURCE_ROOT ${extension_source_dir})
  set(${extension_source_dirname}_SOURCE_DIR ${extension_source_dir})
  set(${extension_source_dirname}_BINARY_DIR ${extensions_build_dir}/${extension_source_dirname})
  add_subdirectory(
    ${${extension_source_dirname}_SOURCE_DIR}
    ${${extension_source_dirname}_BINARY_DIR}
    )
endfunction()

foreach(varname ${Slicer_EP_LABEL_REMOTE_MODULE})
  _add_extension_source_dir(${${varname}} "remote module")
endforeach()
foreach(varname ${Slicer_EP_LABEL_REMOTE_EXTENSION})
  _add_extension_source_dir(${${varname}} "remote extension")
endforeach()
foreach(extension_source_dir ${Slicer_EXTENSION_SOURCE_DIRS})
  _add_extension_source_dir(${extension_source_dir} "extension directory")
endforeach()

unset(Slicer_DIR)

# --------------------------------------------------------------------------
# Recover the QM output directories
# --------------------------------------------------------------------------
get_property(Slicer_QM_OUTPUT_DIRS GLOBAL PROPERTY Slicer_QM_OUTPUT_DIRS)

# --------------------------------------------------------------------------
# Configure and install headers
# --------------------------------------------------------------------------
set(files
  vtkSlicerConfigure.h
  vtkSlicerVersionConfigureMinimal.h
  )
foreach(f ${files})
  configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/CMake/${f}.in
    ${CMAKE_CURRENT_BINARY_DIR}/${f}
    )
endforeach()

# Add target configuring 'vtkSlicerVersionConfigure.h' at build time.
include(SlicerConfigureVersionHeaderTarget)
add_dependencies(SlicerBaseLogic SlicerConfigureVersionHeader)
add_dependencies(MRMLCore SlicerConfigureVersionHeader)

list(APPEND files vtkSlicerVersionConfigure.h)

if(NOT Slicer_INSTALL_NO_DEVELOPMENT)
foreach(f ${files})
  install(
    FILES ${CMAKE_CURRENT_BINARY_DIR}/${f}
    DESTINATION ${Slicer_INSTALL_INCLUDE_DIR} COMPONENT Development
    )
endforeach()
endif()

#-----------------------------------------------------------------------------
# Testing
#-----------------------------------------------------------------------------
if(BUILD_TESTING)
  add_subdirectory(Testing)
endif()

# Install testing data
if(NOT Slicer_INSTALL_NO_DEVELOPMENT)
  install(DIRECTORY
    Testing
    DESTINATION ${Slicer_INSTALL_SHARE_DIR} COMPONENT Development
    PATTERN "CMakeLists.txt" EXCLUDE
    )
endif()

#-----------------------------------------------------------------------------
# Dynamic analysis
#-----------------------------------------------------------------------------
set(Slicer_VALGRIND_SUPPRESSIONS_FILE "${Slicer_SOURCE_DIR}/CMake/SlicerValgrind.supp")
get_property(Slicer_MAIN_PROJECT_APPLICATION_EXECUTABLE GLOBAL PROPERTY "${Slicer_MAIN_PROJECT_APPLICATION_NAME}_EXECUTABLE")
set(script "Utilities/Scripts/runValgrind.sh")
configure_file(
  ${CMAKE_CURRENT_SOURCE_DIR}/${script}.in
  ${CMAKE_CURRENT_BINARY_DIR}/${script}
  @ONLY
  )

#-----------------------------------------------------------------------------
# Documentation
#-----------------------------------------------------------------------------
if(Slicer_BUILD_DOCUMENTATION)
  # Automatically generate documentation at build time
  set(all_arg)
  if(DOCUMENTATION_TARGET_IN_ALL)
    set(all_arg "ALL")
  endif()
  add_custom_target(doc ${all_arg})
  add_custom_target(doc-tarballs ${all_arg})

  add_subdirectory(Utilities/Doxygen)
  add_subdirectory(Utilities/Scripts/SlicerWizard/doc)
endif()

#-----------------------------------------------------------------------------
# Install CMake modules
#-----------------------------------------------------------------------------
if(NOT Slicer_INSTALL_NO_DEVELOPMENT)
  file(GLOB cmake_files "${CMAKE_CURRENT_SOURCE_DIR}/CMake/*.cmake")
  install(
    FILES ${cmake_files}
    DESTINATION ${Slicer_INSTALL_LIB_DIR}/CMake COMPONENT Development
    )

  install(FILES
    ${Slicer_BINARY_DIR}/UseSlicer.cmake
    ${Slicer_BINARY_DIR}/SlicerConfig.cmake
    ${Slicer_BINARY_DIR}/SlicerConfigVersion.cmake
    DESTINATION ${Slicer_INSTALL_LIB_DIR}
    COMPONENT Development
    )
endif()

#-----------------------------------------------------------------------------
# Create target to download data from the <Slicer_ExternalData_DATA_MANAGEMENT_TARGET> group.
# This must come after all tests have been added that reference the group, so we put it last.
ExternalData_Add_Target(${Slicer_ExternalData_DATA_MANAGEMENT_TARGET})

#-----------------------------------------------------------------------------
# Create targets CopySlicerPython{Resource, Script}Files, CompileSlicerPythonFiles
if(Slicer_USE_PYTHONQT)
  slicerFunctionAddPythonQtResourcesTargets(SlicerPythonResources)
  ctkFunctionAddCompilePythonScriptTargets(
    ${CTK_COMPILE_PYTHON_SCRIPTS_GLOBAL_TARGET_NAME}
    DEPENDS SlicerPythonResources
    )

  set(_python_dir ${Slicer_SUPERBUILD_DIR}/python-install)
  if(NOT Slicer_USE_SYSTEM_python OR NOT EXISTS ${_python_dir})
    # Custom target to ensure there are cached byte-code files for all the python
    # standard library and site-packages modules.
    #
    # Considering (1) some of the tests purposefully include invalid code
    # that would lead the compilation to fail and (2) test directories are
    # explicitly excluded from packages in "CMake/SlicerBlockInstallPython.cmake",
    # the corresponding files are excluded from the compilation passing the "-x"
    # argument.
    add_custom_command(
      COMMAND ${PYTHON_EXECUTABLE} -m compileall -q -x "[\/\\\\]test[s]?[\/\\\\]" ${_python_dir}/${PYTHON_STDLIB_SUBDIR}
      OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/python_compile_stdlib_and_sitepackages_complete
      COMMENT "Compiling python stdlib and site-packages modules: ${_python_dir}/${PYTHON_STDLIB_SUBDIR}"
      VERBATIM
      )
    add_custom_target(CompileStdLibAndSitePackagesPythonFiles ALL
      DEPENDS
        ${CMAKE_CURRENT_BINARY_DIR}/python_compile_stdlib_and_sitepackages_complete
      )
  endif()
endif()

#-----------------------------------------------------------------------------
# The commands in this directory are intended to be executed as
# the end of the whole configuration process, as a "last step".
# This directory is typically the last add_subdirectory in the main CMakeLists.txt.
add_subdirectory(CMake/LastConfigureStep)
