include_guard(GLOBAL)

include(Messages)

include(ConfigurationCheck)

macro(add_configuration)
  set(options VERBOSE)
  set(oneValueArgs
      CONFIG
      BASE_CONFIG
      ARCHIVE_OUTPUT_DIRECTORY
      COMPILE_PDB_OUTPUT_DIRECTORY
      POSTFIX
      FRAMEWORK_MULTI_CONFIG_POSTFIX
      INTERPROCEDURAL_OPTIMIZATION
      LIBRARY_OUTPUT_DIRECTORY
      MAP_IMPORTED_CONFIG
      PDB_OUTPUT_DIRECTORY
      RUNTIME_OUTPUT_DIRECTORY
    )
  set(multiValueArgs
      EXE_LINKER_FLAGS
      MODULE_LINKER_FLAGS
      SHARED_LINKER_FLAGS
      STATIC_LINKER_FLAGS
      C_FLAGS
      CXX_FLAGS
      CUDA_FLAGS
      OBJC_FLAGS
      OBJCXX_FLAGS
      Fortran_FLAGS
      ASM_FLAGS
    )
  cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
  if(NOT DEFINED ARG_CONFIG)
    message(FATAL_ERROR "CONFIG variable is mandatory")
  endif()
  if(NOT DEFINED ARG_VERBOSE)
    set(ARG_VERBOSE FALSE)
  endif()
  if(CMAKE_CONFIGURATION_TYPES)
    if(NOT ${ARG_CONFIG} IN_LIST CMAKE_CONFIGURATION_TYPES)
      list(APPEND CMAKE_CONFIGURATION_TYPES ${ARG_CONFIG})
    endif()
  else()
    list(APPEND ALLOWABLE_BUILD_TYPES ${ALLOWABLE_BUILD_TYPES} ${ARG_CONFIG})
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${ALLOWABLE_BUILD_TYPES}")
  endif()
  if(NOT ARG_BASE_CONFIG)

  elseif(NOT ARG_BASE_CONFIG IN_LIST ALLOWABLE_BUILD_TYPES)
    list(APPEND ALLOWABLE_BUILD_TYPES_DUMB ${ALLOWABLE_BUILD_TYPES})
    list(REMOVE_ITEM ALLOWABLE_BUILD_TYPES_DUMB ${ARG_CONFIG})
    list(JOIN ALLOWABLE_BUILD_TYPES_DUMB ", " STRING_ALLOWABLE_BUILD_TYPES_DUMB)
    message(FATAL_ERROR "Invalid BASE_CONFIG : ${ARG_BASE_CONFIG}\nBASE_CONFIG should be on the list : ${STRING_ALLOWABLE_BUILD_TYPES_DUMB}")
  else()
    string(TOUPPER ${ARG_BASE_CONFIG} ARG_BASE_CONFIG)
  endif()
  set(OLD_ARG_CONFIG ${ARG_CONFIG})
  string(TOUPPER ${ARG_CONFIG} ARG_CONFIG)

  # Add the cmake_variable
  #CMAKE_ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>
  #if(NOT DEFINED ARG_ARCHIVE_OUTPUT_DIRECTORY AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${ARG_CONFIG} "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${ARG_BASE_CONFIG}}" CACHE PATH "Where to put all the ARCHIVE target files")
  #elseif(DEFINED ARG_CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
  #  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${ARG_CONFIG} "${ARG_ARCHIVE_OUTPUT_DIRECTORY}" CACHE PATH "Where to put all the ARCHIVE target files")
  #endif()
  #CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_<CONFIG>
  #if(NOT DEFINED ARG_COMPILE_PDB_OUTPUT_DIRECTORY AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_${ARG_CONFIG} "${CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_${ARG_BASE_CONFIG}}" CACHE PATH "Per-configuration output directory for MS debug symbol .pdb files generated by the compiler while building source files")
  #elseif(DEFINED ARG_COMPILE_PDB_OUTPUT_DIRECTORY)
  #  set(CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_${ARG_CONFIG} "${ARG_COMPILE_PDB_OUTPUT_DIRECTORY}" CACHE PATH "Per-configuration output directory for MS debug symbol .pdb files generated by the compiler while building source files")
  #endif()
  #CMAKE_<CONFIG>_POSTFIX
  #if(NOT DEFINED ARG_POSTFIX AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_${ARG_CONFIG}_POSTFIX "${CMAKE_${ARG_BASE_CONFIG}_POSTFIX}" CACHE STRING "Default filename postfix for libraries")
  #elseif(DEFINED ARG_POSTFIX)
  #  set(CMAKE_${ARG_CONFIG}_POSTFIX "${ARG_POSTFIX}" CACHE PATH "Default filename postfix for libraries")
  #endif()
  #CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>
  #if(NOT DEFINED ARG_FRAMEWORK_MULTI_CONFIG_POSTFIX AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_${ARG_CONFIG} "${CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_${ARG_BASE_CONFIG}}" CACHE STRING "Default framework filename postfix when using a multi-config generator.")
  #elseif(DEFINED ARG_FRAMEWORK_MULTI_CONFIG_POSTFIX)
  #  set(CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_${ARG_CONFIG} "${ARG_FRAMEWORK_MULTI_CONFIG_POSTFIX}" CACHE STRING "Default framework filename postfix when using a multi-config generator.")
  #endif()
  #CMAKE_INTERPROCEDURAL_OPTIMIZATION_<CONFIG>
  #if(NOT DEFINED ARG_INTERPROCEDURAL_OPTIMIZATION AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_${ARG_CONFIG} "${CMAKE_INTERPROCEDURAL_OPTIMIZATION_${ARG_BASE_CONFIG}}" CACHE BOOL "Default value for INTERPROCEDURAL_OPTIMIZATION")
  #elseif(DEFINED ARG_INTERPROCEDURAL_OPTIMIZATION)
  #  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_${ARG_CONFIG} "${ARG_INTERPROCEDURAL_OPTIMIZATION}" CACHE BOOL "Default value for INTERPROCEDURAL_OPTIMIZATION")
  #endif()
  #CMAKE_LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
  #if(NOT DEFINED ARG_LIBRARY_OUTPUT_DIRECTORY AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${ARG_CONFIG} "${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${ARG_BASE_CONFIG}}" CACHE PATH "Where to put all the LIBRARY target files")
  #elseif(DEFINED ARG_LIBRARY_OUTPUT_DIRECTORY)
  #  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${ARG_CONFIG} "${ARG_LIBRARY_OUTPUT_DIRECTORY}" CACHE PATH "Where to put all the LIBRARY target files")
  #endif()
  #CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>
  #if(NOT DEFINED ARG_MAP_IMPORTED_CONFIG AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_MAP_IMPORTED_CONFIG_${ARG_CONFIG} "${CMAKE_MAP_IMPORTED_CONFIG_${ARG_BASE_CONFIG}}" CACHE STRING "Default value for MAP_IMPORTED_CONFIG_<CONFIG> of targets")
  #elseif(DEFINED ARG_MAP_IMPORTED_CONFIG)
  #  set(CMAKE_MAP_IMPORTED_CONFIG_${ARG_CONFIG} "${ARG_MAP_IMPORTED_CONFIG}" CACHE STRING "Default value for MAP_IMPORTED_CONFIG_<CONFIG> of targets")
  #endif()
  #CMAKE_PDB_OUTPUT_DIRECTORY_<CONFIG>
  #if(NOT DEFINED ARG_PDB_OUTPUT_DIRECTORY AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_PDB_OUTPUT_DIRECTORY_${ARG_CONFIG} "${CMAKE_PDB_OUTPUT_DIRECTORY_${ARG_BASE_CONFIG}}" CACHE PATH "Per-configuration output directory for MS debug symbol .pdb files generated by the linker for executable and shared library targets.")
  #elseif(DEFINED ARG_PDB_OUTPUT_DIRECTORY)
  #  set(CMAKE_ARG_PDB_OUTPUT_DIRECTORY_${ARG_CONFIG} "${ARG_ARG_PDB_OUTPUT_DIRECTORY}" CACHE PATH "Per-configuration output directory for MS debug symbol .pdb files generated by the linker for executable and shared library targets.")
  #endif()
  #CMAKE_RUNTIME_OUTPUT_DIRECTORY_<CONFIG>
  #if(NOT DEFINED ARG_RUNTIME_OUTPUT_DIRECTORY AND DEFINED ARG_BASE_CONFIG)
  #  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${ARG_CONFIG} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${ARG_BASE_CONFIG}}" CACHE PATH "Where to put all the RUNTIME target files when built for a specific configuration.")
  #elseif(DEFINED RUNTIME_OUTPUT_DIRECTORY)
  #  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${ARG_CONFIG} "${ARG_RUNTIME_OUTPUT_DIRECTORY}" CACHE PATH "Where to put all the RUNTIME target files when built for a specific configuration.")
  #endif()
  #CMAKE_SHARED_LINKER_FLAGS_<CONFIG>
  if(NOT DEFINED ARG_SHARED_LINKER_FLAGS AND DEFINED ARG_BASE_CONFIG)
    list(JOIN CMAKE_SHARED_LINKER_FLAGS_${ARG_BASE_CONFIG} " " FLAGS)
    set(CMAKE_SHARED_LINKER_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags to be used to create static libraries." FORCE)
  elseif(DEFINED ARG_SHARED_LINKER_FLAGS AND DEFINED ARG_BASE_CONFIG)
    list(APPEND ALL_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_${ARG_BASE_CONFIG}}" "${ARG_SHARED_LINKER_FLAGS}")
    list(JOIN ALL_SHARED_LINKER_FLAGS " " FLAGS)
    set(CMAKE_SHARED_LINKER_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags to be used to create static libraries." FORCE)
  elseif(DEFINED ARG_SHARED_LINKER_FLAGS AND NOT DEFINED ARG_BASE_CONFIG)
    list(JOIN ARG_SHARED_LINKER_FLAGS " " FLAGS)
    set(CMAKE_SHARED_LINKER_FLAGS_${ARG_CONFIG} "${FLAGS}" CACHE STRING "Flags to be used to create static libraries." FORCE)
  endif()
  #CMAKE_STATIC_LINKER_FLAGS_<CONFIG>
  if(NOT DEFINED ARG_STATIC_LINKER_FLAGS AND DEFINED ARG_BASE_CONFIG)
    list(JOIN CMAKE_STATIC_LINKER_FLAGS_${ARG_BASE_CONFIG} " " FLAGS)
    set(CMAKE_STATIC_LINKER_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags to be used to create shared libraries." FORCE)
  elseif(DEFINED ARG_STATIC_LINKER_FLAGS AND DEFINED ARG_BASE_CONFIG)
    list(APPEND ALL_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS_${ARG_BASE_CONFIG}}" "${ARG_STATIC_LINKER_FLAGS}")
    list(JOIN ALL_STATIC_LINKER_FLAGS " " FLAGS)
    set(CMAKE_STATIC_LINKER_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags to be used to create shared libraries." FORCE)
  elseif(DEFINED ARG_STATIC_LINKER_FLAGS AND NOT DEFINED ARG_BASE_CONFIG)
    list(JOIN ARG_STATIC_LINKER_FLAGS " " FLAGS)
    set(CMAKE_STATIC_LINKER_FLAGS_${ARG_CONFIG} "${FLAGS}" CACHE STRING "Flags to be used to create shared libraries." FORCE)
  endif()
  #CMAKE_EXE_LINKER_FLAGS_<CONFIG>
  if(NOT DEFINED ARG_EXE_LINKER_FLAGS AND DEFINED ARG_BASE_CONFIG)
    list(JOIN CMAKE_EXE_LINKER_FLAGS_${ARG_BASE_CONFIG} " " FLAGS)
    set(CMAKE_EXE_LINKER_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags to be used when linking an executable." FORCE)
  elseif(DEFINED ARG_EXE_LINKER_FLAGS AND DEFINED ARG_BASE_CONFIG)
    list(APPEND ALL_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_${ARG_BASE_CONFIG}}" "${ARG_EXE_LINKER_FLAGS}")
    list(JOIN ALL_EXE_LINKER_FLAGS " " FLAGS)
    set(CMAKE_EXE_LINKER_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags to be used when linking an executable." FORCE)
  elseif(DEFINED ARG_EXE_LINKER_FLAGS AND NOT DEFINED ARG_BASE_CONFIG)
    list(JOIN ARG_EXE_LINKER_FLAGS " " FLAGS)
    set(CMAKE_EXE_LINKER_FLAGS_${ARG_CONFIG} "${FLAGS}" CACHE STRING "Flags to be used when linking an executable." FORCE)
  endif()
  #CMAKE_MODULE_LINKER_FLAGS_<CONFIG>
  if(NOT DEFINED ARG_MODULE_LINKER_FLAGS AND DEFINED ARG_BASE_CONFIG)
    list(JOIN CMAKE_MODULE_LINKER_FLAGS_${ARG_BASE_CONFIG} " " FLAGS)
    set(CMAKE_MODULE_LINKER_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags to be used when linking a module." FORCE)
  elseif(DEFINED ARG_MODULE_LINKER_FLAGS AND DEFINED ARG_BASE_CONFIG)
    list(APPEND ALL_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS_${ARG_BASE_CONFIG}}" "${ARG_MODULE_LINKER_FLAGS}")
    list(JOIN ALL_MODULE_LINKER_FLAGS " " FLAGS)
    set(CMAKE_MODULE_LINKER_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags to be used when linking a module." FORCE)
  elseif(DEFINED ARG_MODULE_LINKER_FLAGS AND NOT DEFINED ARG_BASE_CONFIG)
    list(JOIN ARG_MODULE_LINKER_FLAGS " " FLAGS)
    set(CMAKE_MODULE_LINKER_FLAGS_${ARG_CONFIG} "${FLAGS}" CACHE STRING "Flags to be used when linking a module." FORCE)
  endif()
  #CMAKE_<LANG>_FLAGS_<CONFIG>
  foreach(LANGUAGE IN LISTS IN LANGUAGES)
    if(NOT DEFINED ARG_${LANGUAGE}_FLAGS AND DEFINED ARG_BASE_CONFIG)
      list(JOIN CMAKE_${LANGUAGE}_FLAGS_${ARG_BASE_CONFIG} " " FLAGS)
      set(CMAKE_${LANGUAGE}_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags for language ${LANGUAGE} when building for the ${ARG_CONFIG} configuration." FORCE)
      continue()
    elseif(DEFINED ARG_${LANGUAGE}_FLAGS AND DEFINED ARG_BASE_CONFIG)
      list(APPEND ALL_FLAGS_${LANGUAGE} "${CMAKE_${LANGUAGE}_FLAGS_${ARG_BASE_CONFIG}}" "${ARG_${LANGUAGE}_FLAGS}")
      list(JOIN ALL_FLAGS_${LANGUAGE} " " FLAGS)
      set(CMAKE_${LANGUAGE}_FLAGS_${ARG_CONFIG} ${FLAGS} CACHE STRING "Flags for language ${LANGUAGE} when building for the ${ARG_CONFIG} configuration." FORCE)
      continue()
    elseif(DEFINED ARG_${LANGUAGE}_FLAGS AND NOT DEFINED ARG_BASE_CONFIG)
      list(JOIN ARG_${LANGUAGE}_FLAGS " " FLAGS)
      set(CMAKE_${LANGUAGE}_FLAGS_${ARG_CONFIG} "${FLAGS}" CACHE STRING "Flags for language ${LANGUAGE} when building for the ${ARG_CONFIG} configuration." FORCE)
      continue()
    endif()
  endforeach()
  if(ARG_VERBOSE)
    message(NOTE "Configuration : ${config}")
    print_configuration(${OLD_ARG_CONFIG})
    message(NOTE "----------------")
  endif()
endmacro()
