#---------------------------------------------------------------------
# Sub project name

project(elxComponents)

#---------------------------------------------------------------------
# Set the libraries to be linked.

# Linux gave an error, if param was linked. This was only
# when we still used .dll's. I'm not sure if it will still
# give an error, but as long as linux does not complain
# about the missing param, just leave it out.
if(UNIX)
  if(NOT APPLE)
    set(elxLinkLibs
      elxCommon
      elxCore
      ${ITK_LIBRARIES})
  endif()
endif()

# Interestingly, visual c++ and MAC-GCC do need param
if(WIN32 OR APPLE)
  set(elxLinkLibs
    param
    elxCommon
    elxCore
    ${ITK_LIBRARIES})
endif()

#--------------------------------------------------------------------
# Prepare elxInstallComponentFunctionDeclarations.h and
# elxInstallComponentFunctionCalls.h
# In the ADD_ELXCOMPONENT macro a line is added
#

set(InstallFunctionDeclarationFile
  ${elastix_BINARY_DIR}/elxInstallComponentFunctionDeclarations.h)
set(InstallFunctionCallFile
  ${elastix_BINARY_DIR}/elxInstallComponentFunctionCalls.h)

file(WRITE ${InstallFunctionDeclarationFile}
  "\n" "//This file is generated by CMake. Do not edit manually!\n\n")
file(WRITE ${InstallFunctionCallFile}
  "\n" "//This file is generated by CMake. Do not edit manually!\n\n")


#---------------------------------------------------------------------
# Macros that simplify the addition (and removal) of elx-components
#
# Usage:
# ADD_ELXCOMPONENT( <name_of_lib> <list_of_source_and_header_files>)
#
# The name_of_lib should be equal to the name of the elx class,
# which also should be the name used in the elxInstallMacro, in the
# the .cxx file of the component.
#
# The REMOVE_ELXCOMPONENT is in some cases useful, if you want to
# skip building the component in case some other cmake setting is not
# correct. Most users will not need to invoke this macro though.
#
# Usage:
# REMOVE_ELXCOMPONENT( <name_of_lib>)
#
include(CMakeDependentOption)

macro(REMOVE_ELXCOMPONENT name)
  # remove lib from link list
  if(DEFINED AllComponentLibs)
    set(dummy ${AllComponentLibs})
    if(NOT "${dummy}" STREQUAL "")
      list(REMOVE_ITEM dummy ${name})
    endif()
  else()
    set(dummy "")
  endif()
  set(AllComponentLibs ${dummy} CACHE INTERNAL "All component libraries for elastix/transformix"  )
endmacro(REMOVE_ELXCOMPONENT)

macro(ADD_ELXCOMPONENT name)

  # Compile this component or not
  set(defaultValue ON)
  if(${ARGV1} STREQUAL "OFF")
    set(defaultValue OFF)
  endif()
  mark_as_advanced(USE_${name})
  set(USE_${name} ${defaultValue} CACHE BOOL "Compile this component")

  # If USE_ALL_COMPONENTS is turned ON, we make a backup of the
  # current value, and force the use_var to ON. If USE_ALL_COMPONENTS
  # is OFF, and there is a backup, set the use_var to the backed-up
  # value and remove the backup; otherwise, leave the use_var to its
  # current (default or user-specified) value.
  if(USE_ALL_COMPONENTS)
    # make backup
    if(NOT DEFINED USE_${name}_BACKUP)
      if(USE_${name})
        set(USE_${name}_BACKUP ON CACHE INTERNAL "Backup of USE_var" FORCE)
      else()
        set(USE_${name}_BACKUP OFF CACHE INTERNAL "Backup of USE_var" FORCE)
      endif()
    endif()
    # turn use_var to on;
    set(USE_${name} ON CACHE BOOL "Compile this component" FORCE)
  else()
    # if there is a backup, set the use_var to the backed-up value
    # and remove the backup.
    if(DEFINED USE_${name}_BACKUP)
      # this unset command is important; it makes sure the use_var is reset
      unset(USE_${name} CACHE)
      if(USE_${name}_BACKUP)
        set(USE_${name} ON CACHE BOOL "Compile this component")
      else()
        set(USE_${name} OFF CACHE BOOL "Compile this component")
      endif()
      unset(USE_${name}_BACKUP CACHE)
    endif()
  endif()

  if(USE_${name})
    # Create the list of files which create the library
    set(filelist ${ARGN})
    list(REMOVE_ITEM filelist "ON" "OFF")

    # Create project and static library
    project(${name})
    add_library(${name} STATIC ${filelist})
    target_link_libraries(${name} ${elxLinkLibs})
    if(NOT ELASTIX_NO_INSTALL_DEVELOPMENT)
      install(TARGETS ${name}
        ARCHIVE DESTINATION ${ELASTIX_ARCHIVE_DIR}
        LIBRARY DESTINATION ${ELASTIX_LIBRARY_DIR}
        RUNTIME DESTINATION ${ELASTIX_RUNTIME_DIR}
        COMPONENT Development)
    endif()

    # Group in IDE's like Visual Studio
    string(REGEX MATCH "[a-zA-Z]+" comp_group ${relative_path_to_comp})
    set_property(TARGET ${name} PROPERTY FOLDER ${comp_group})

    # Update AllComponentLibs, while avoiding duplicates:
    if(DEFINED AllComponentLibs)
      set(dummy ${AllComponentLibs})
      if(NOT "${dummy}" STREQUAL "")
        list(REMOVE_ITEM dummy ${name})
      endif()
      set(dummy ${dummy} ${name})
    else()
      set(dummy ${name})
    endif()
    set(AllComponentLibs ${dummy} CACHE INTERNAL "All component libraries for elastix/transformix")

    # Write lines to two files
    file(APPEND ${InstallFunctionDeclarationFile}
      "elxInstallComponentFunctionDeclarationMacro(" ${name} ");\n\n")
    file(APPEND ${InstallFunctionCallFile}
      "elxInstallComponentFunctionCallMacro(" ${name} ");\n\n")

  else(USE_${name})
    # Remove from link list
    REMOVE_ELXCOMPONENT( ${name})
  endif()

endmacro(ADD_ELXCOMPONENT)

#---------------------------------------------------------------------
# Option to turn on all components by default
mark_as_advanced(USE_ALL_COMPONENTS)
set(USE_ALL_COMPONENTS OFF CACHE BOOL "Compile all components")


#---------------------------------------------------------------------
# Search for all components in the elastix source directory
#

# Initialize the list of link libs.
set(AllComponentLibs "" CACHE INTERNAL "All component libraries for elastix/transformix"  )

file(GLOB ListOfComponents "*/*/CMakeLists.txt")

foreach(component ${ListOfComponents})
  get_filename_component(path_to_comp ${component} PATH)
  # Make the path relative to the elxComponents_SOURCE_DIR
  string(REGEX REPLACE "${elxComponents_SOURCE_DIR}/" "" relative_path_to_comp ${path_to_comp})
  add_subdirectory(${relative_path_to_comp})
endforeach()


#----------------------------------------------------------------------
# Search for user components
# These are components defined in external directories
# outside elastix, defined by the user.

foreach(userComponentDir ${ELASTIX_USER_COMPONENT_DIRS})

  message(STATUS "Searching for user components in: ${userComponentDir}")
  file(GLOB_RECURSE listOfUserComponents "${userComponentDir}/CMakeLists.txt")

  foreach(userComponent ${listOfUserComponents})

    # Get the path name
    get_filename_component(path_to_comp ${userComponent} PATH)

    # Read project name from cmakelists.txt
    # Find the first line that has the following structure:
    # ADD_ELXCOMPONENT( projectname [....]
    # spaces before/after/inbetween are allowed.
    # ADD_ELXCOMPONENT does not have to be capitalized.
    # Commented lines (where the first nonwhite character is a #) are ignored.
    file(STRINGS ${userComponent} addElxComponentLine LIMIT_COUNT 1 REGEX
      "^[ \t]*[Aa][Dd][Dd]_[Ee][Ll][Xx][Cc][Oo][Mm][Pp][Oo][Nn][Ee][Nn][Tt][ \t]*[(][ \t]*[^ #\t()]+")

    # Extract the projectname.
    string(REGEX REPLACE
      "^[ \t]*[Aa][Dd][Dd]_[Ee][Ll][Xx][Cc][Oo][Mm][Pp][Oo][Nn][Ee][Nn][Tt][ \t]*[(][ \t]*([^ #\t()]+).*"
      "\\1" componentName "${addElxComponentLine}")

    # Process the current component
    if(NOT "${componentName}" STREQUAL "")
      # For non-subdirectories, the binarydir should be specified.
      # We use the name of the component for that.
      set(binaryDir "${elastix_BINARY_DIR}/UserComponents/${componentName}")

      # Add. Make sure that the path relative to the ELASTIX_USER_COMPONENT_DIRS is set,
      # so that the component ends up in the correct source group.
      string(REGEX REPLACE "${ELASTIX_USER_COMPONENT_DIRS}/" "" relative_path_to_comp ${path_to_comp})
      add_subdirectory(${path_to_comp} ${binaryDir})

      message(STATUS "Found user component: ${relative_path_to_comp}")
    else()
      message(STATUS "No user component definition found in: ${userComponent}")
    endif()

  endforeach()
endforeach()

