#-----------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.13.4...3.14.2)
cmake_policy(VERSION 3.13.4...3.14.2)

#-----------------------------------------------------------------------------
# Enable C++11
#-----------------------------------------------------------------------------
#####
##  Set the default target language standard properties
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 11) # Supported values are ``11``, ``14``, and ``17``.
endif()
if(NOT CMAKE_CXX_STANDARD_REQUIRED)
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
if(NOT CMAKE_CXX_EXTENSIONS)
  set(CMAKE_CXX_EXTENSIONS OFF)
endif()
set(VALID_CXX_STANDARDS "11" "14" "17")
if(NOT CMAKE_CXX_STANDARD IN_LIST VALID_CXX_STANDARDS )
   message(FATAL_ERROR "CMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} not in know standards list\n ${VALID_CXX_STANDARDS}.")
endif()

project(SlicerExecutionModel
        VERSION 2.0.0  #Starting at version 2.0.0, C++11 is required
        DESCRIPTION    "An open-source CMake-based project that provides macros and associated tools for the easy building of 3D Slicer command line interface (CLI) modules."
        HOMEPAGE_URL   "https://github.com/Slicer/SlicerExecutionModel"
        LANGUAGES C CXX)

#-----------------------------------------------------------------------------

include(CMakeDependentOption)

set(CMAKE_MODULE_PATH ${SlicerExecutionModel_SOURCE_DIR}/CMake ${CMAKE_MODULE_PATH})

if(DEFINED SlicerExecutionModel_INSTALL_BIN_DIR)
  set(GenerateCLP_INSTALL_BIN_DIR ${SlicerExecutionModel_INSTALL_BIN_DIR})
  set(ModuleDescriptionParser_INSTALL_BIN_DIR ${SlicerExecutionModel_INSTALL_BIN_DIR})
  set(tclap_INSTALL_BIN_DIR ${SlicerExecutionModel_INSTALL_BIN_DIR})
endif()

if(DEFINED SlicerExecutionModel_INSTALL_LIB_DIR)
  set(GenerateCLP_INSTALL_LIB_DIR ${SlicerExecutionModel_INSTALL_LIB_DIR})
  set(ModuleDescriptionParser_INSTALL_LIB_DIR ${SlicerExecutionModel_INSTALL_LIB_DIR})
  set(tclap_INSTALL_LIB_DIR ${SlicerExecutionModel_INSTALL_LIB_DIR})
endif()

if(DEFINED SlicerExecutionModel_INSTALL_NO_DEVELOPMENT)
  set(GenerateCLP_INSTALL_NO_DEVELOPMENT ${SlicerExecutionModel_INSTALL_NO_DEVELOPMENT})
  set(ModuleDescriptionParser_INSTALL_NO_DEVELOPMENT ${SlicerExecutionModel_INSTALL_NO_DEVELOPMENT})
  set(tclap_INSTALL_NO_DEVELOPMENT ${SlicerExecutionModel_INSTALL_NO_DEVELOPMENT})
endif()

if(NOT DEFINED SlicerExecutionModel_EXPORT_FILE)
  set(SlicerExecutionModel_EXPORT_FILE ${CMAKE_CURRENT_BINARY_DIR}/SlicerExecutionModelTargets.cmake)
  # Clear SlicerExecutionModel.cmake
  file(WRITE ${SlicerExecutionModel_EXPORT_FILE} "# Generated by CMake, do not edit!")
endif()

if(DEFINED SlicerExecutionModel_LIBRARY_PROPERTIES)
  set(ModuleDescriptionParser_LIBRARY_PROPERTIES ${SlicerExecutionModel_LIBRARY_PROPERTIES})
endif()

#-----------------------------------------------------------------------------
# Set a default build type if none was specified
#-----------------------------------------------------------------------------
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  message(STATUS "Setting build type to 'Debug' as none was specified.")
  set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
  mark_as_advanced(CMAKE_BUILD_TYPE)
  # Set the possible values of build type for cmake-gui
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
    "MinSizeRel" "RelWithDebInfo")
endif()

#-----------------------------------------------------------------------------
# ParameterSerializer support for ModuleDescriptionParser and GenerateCLP subprojects
#-----------------------------------------------------------------------------
option(SlicerExecutionModel_USE_UTF8 "Make applications use UTF-8 as code page." OFF)
set(SlicerExecutionModel_CMAKE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/CMake)
set(GenerateCLP_USE_UTF8 ${SlicerExecutionModel_USE_UTF8})
include(SEMFunctionAddExecutable)

#-----------------------------------------------------------------------------
# Output directories associated with ModuleDescriptionParser and GenerateCLP subprojects
#-----------------------------------------------------------------------------
foreach(type LIBRARY RUNTIME ARCHIVE)
  # Make sure the directory exists
  if(DEFINED SlicerExecutionModel_CMAKE_${type}_OUTPUT_DIRECTORY
     AND NOT EXISTS ${SlicerExecutionModel_CMAKE_${type}_OUTPUT_DIRECTORY})
    message(FATAL_ERROR "SlicerExecutionModel_CMAKE_${type}_OUTPUT_DIRECTORY is set to a non-existing directory [${SlicerExecutionModel_CMAKE_${type}_OUTPUT_DIRECTORY}]")
  endif()

  if(DEFINED SlicerExecutionModel_CMAKE_${type}_OUTPUT_DIRECTORY)
    set(ModuleDescriptionParser_CMAKE_${type}_OUTPUT_DIRECTORY ${SlicerExecutionModel_CMAKE_${type}_OUTPUT_DIRECTORY})
    set(GenerateCLP_CMAKE_${type}_OUTPUT_DIRECTORY ${GenerateCLP_CMAKE_${type}_OUTPUT_DIRECTORY})
  endif()
endforeach()

#-----------------------------------------------------------------------------
# ParameterSerializer support for ModuleDescriptionParser and GenerateCLP subprojects
#-----------------------------------------------------------------------------
option(SlicerExecutionModel_USE_JSONCPP "Enable JSON serialization support for GenerateCLP." OFF)
if(SlicerExecutionModel_USE_JSONCPP)
  find_package(JsonCpp REQUIRED)
  set(GenerateCLP_USE_JSONCPP ON)
endif()
cmake_dependent_option(SlicerExecutionModel_USE_SERIALIZER
  "Enable ParameterSerializer support for ModuleDescriptionParser and GenerateCLP." OFF
  "SlicerExecutionModel_USE_JSONCPP" OFF)
if(SlicerExecutionModel_USE_SERIALIZER)
  find_package(ParameterSerializer REQUIRED)
  set(ModuleDescriptionParser_USE_SERIALIZER ON)
  set(GenerateCLP_USE_SERIALIZER ON)
endif()


#-----------------------------------------------------------------------------
# Note: The default values defined below could be overwritten if passed when configuring
#       SlicerExecutionModel project.
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Define default "output directories" used by SEMMacroBuildCLI.cmake
#-----------------------------------------------------------------------------
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_RUNTIME_OUTPUT_DIRECTORY)
  set(SlicerExecutionModel_DEFAULT_CLI_RUNTIME_OUTPUT_DIRECTORY bin)
endif()
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_LIBRARY_OUTPUT_DIRECTORY)
  set(SlicerExecutionModel_DEFAULT_CLI_LIBRARY_OUTPUT_DIRECTORY lib)
endif()
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_ARCHIVE_OUTPUT_DIRECTORY)
  set(SlicerExecutionModel_DEFAULT_CLI_ARCHIVE_OUTPUT_DIRECTORY lib)
endif()

#-----------------------------------------------------------------------------
# Define default "install destination" used by SEMMacroBuildCLI.cmake
#-----------------------------------------------------------------------------
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_INSTALL_RUNTIME_DESTINATION)
  set(SlicerExecutionModel_DEFAULT_CLI_INSTALL_RUNTIME_DESTINATION bin)
endif()
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_INSTALL_LIBRARY_DESTINATION)
  set(SlicerExecutionModel_DEFAULT_CLI_INSTALL_LIBRARY_DESTINATION lib)
endif()
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_INSTALL_ARCHIVE_DESTINATION)
  set(SlicerExecutionModel_DEFAULT_CLI_INSTALL_ARCHIVE_DESTINATION lib)
endif()

#-----------------------------------------------------------------------------
# Define default "cli library wrapper" used by SEMMacroBuildCLI.cmake
#-----------------------------------------------------------------------------
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_LIBRARY_WRAPPER_CXX)
  set(SlicerExecutionModel_DEFAULT_CLI_LIBRARY_WRAPPER_CXX
    ${CMAKE_CURRENT_SOURCE_DIR}/CMake/SEMCommandLineLibraryWrapper.cxx)
endif()

#-----------------------------------------------------------------------------
# Define default "cli targets folder prefix" used by SEMMacroBuildCLI.cmake
#-----------------------------------------------------------------------------
if(NOT DEFINED SlicerExecutionModel_DEFAULT_CLI_TARGETS_FOLDER_PREFIX)
  set(SlicerExecutionModel_DEFAULT_CLI_TARGETS_FOLDER_PREFIX "")
endif()

#-----------------------------------------------------------------------------
# Testing
#-----------------------------------------------------------------------------
include(CTest)

# --------------------------------------------------------------------------
# SETIFEMPTY
# --------------------------------------------------------------------------
## A simple macro to set variables ONLY if it has not been set
## This is needed when stand-alone packages are combined into
## a larger package, and the desired behavior is that all the
## binary results end up in the combined directory.
## The signature has the same signature as the set, it just checks if the
## value is already set before executing.
if(NOT SETIFEMPTY)
macro(SETIFEMPTY)
  set(KEY ${ARGV0})
  set(VALUE ${ARGV1})
  if(NOT ${KEY})
    set(${ARGV})
  endif()
endmacro()
endif()
###

SETIFEMPTY(SlicerExecutionModel_ADDITIONAL_FORWARD_PATHS_BUILD  "" CACHE PATH "Specify additional forward build directory paths for GenerateCLPLauncher.")

SETIFEMPTY(SlicerExecutionModel_ADDITIONAL_FORWARD_PATHS_INSTALL  "" CACHE PATH "Specify additional forward install directory paths for GenerateCLPLauncher.")

SETIFEMPTY(TCLAP_DIR ${CMAKE_CURRENT_BINARY_DIR}/tclap CACHE PATH "Location of TCLAPConfig.cmake" FORCE)
add_subdirectory(tclap)

SETIFEMPTY(ModuleDescriptionParser_DIR ${CMAKE_CURRENT_BINARY_DIR}/ModuleDescriptionParser CACHE PATH "Location of ModuleDescriptionParserConfig.cmake" FORCE)
add_subdirectory(ModuleDescriptionParser)

SETIFEMPTY(GenerateCLP_DIR ${CMAKE_CURRENT_BINARY_DIR}/GenerateCLP CACHE PATH "Location of GenerateCLPConfig.cmake" FORCE)
add_subdirectory(GenerateCLP)




# --------------------------------------------------------------------------
# Set INCLUDE_DIRS variable
# --------------------------------------------------------------------------
set(_additional_include_dirs )
if(SlicerExecutionModel_USE_JSONCPP)
  list(APPEND _additional_include_dirs ${JsonCpp_INCLUDE_DIRS})
endif()
if(SlicerExecutionModel_USE_SERIALIZER)
  list(APPEND _additional_include_dirs ${ParameterSerializer_INCLUDE_DIRS})
endif()
set(${PROJECT_NAME}_INCLUDE_DIRS
  ${ModuleDescriptionParser_INCLUDE_DIRS}
  ${GenerateCLP_INCLUDE_DIRS}
  ${TCLAP_INCLUDE_DIRS}
  ${_additional_include_dirs}
  CACHE INTERNAL "${PROJECT_NAME} include dirs" FORCE
  )

# --------------------------------------------------------------------------
# Export target
# --------------------------------------------------------------------------
export(TARGETS ${ModuleDescriptionParser_TARGETS} ${GenerateCLP_TARGETS}
  FILE ${SlicerExecutionModel_EXPORT_FILE})

# --------------------------------------------------------------------------
# Configure support files
# --------------------------------------------------------------------------
include(GenerateSlicerExecutionModelConfig.cmake)

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/UseSlicerExecutionModel.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/UseSlicerExecutionModel.cmake COPYONLY)

set(allscripts
  CMake/FindJsonCpp.cmake
  CMake/SEMCommandLineLibraryWrapper.cxx
  CMake/SEMFunctionAddExecutable.cmake
  CMake/SEMMacroBuildCLI.cmake
  CMake/SEMToolTestName.cxx.in
  CMake/WindowsApplicationUseUtf8.manifest
  )
foreach(SCRIPT ${allscripts})
  get_filename_component(_fileName ${SCRIPT} NAME)
  configure_file(${SCRIPT}
    ${CMAKE_CURRENT_BINARY_DIR}/CMake/${_fileName} COPYONLY IMMEDIATE)
endforeach()
