#---------------------------------------------------------------------
# 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
      xoutlib
      elxCommon
      elxCore
      ${ITK_LIBRARIES} )
  endif()
endif()

# Interestingly, visual c++ and MAC-GCC do need param
if( WIN32 OR APPLE )
  set( elxLinkLibs
    param
    xoutlib
    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} )
    install( TARGETS ${name}
      ARCHIVE DESTINATION ${ELASTIX_ARCHIVE_DIR}
      LIBRARY DESTINATION ${ELASTIX_LIBRARY_DIR}
      RUNTIME DESTINATION ${ELASTIX_RUNTIME_DIR} )

    # 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()

