macro(reason_set_or KEY VALUE DEFAULT)
  if(VALUE)
    set("${KEY}" "${VALUE}")
  else()
    set("${KEY}" "${DEFAULT}")
  endif()
endmacro()

macro(reason_set_if KEY VALUE)
  if(NOT "${VALUE}" STREQUAL "")
    set("${KEY}" "${VALUE}")
  endif()
endmacro()

macro(reason_set_check KEY ERRMSG)
  if(NOT ${KEY})
    reason_message(FATAL_ERROR "${ERRMSG}")
  endif()
endmacro()

function(reason_check_incompatible)
  set(ANY_OF_ARG_IS_DEF FALSE)
  foreach(ARG IN LISTS ARGV)
    if(${ANY_OF_ARG_IS_DEF})
      if("${ARG}")
        reason_message(FATAL_ERROR "${ARGV} are not compatible")
      endif()
    else()
      if("${ARG}")
        set(ANY_OF_ARG_IS_DEF TRUE)
      endif()
    endif()
  endforeach()
endfunction()

function(reason_message)
  reason_define_colors()
  list(GET ARGV 0 MessageType)
  if(MessageType STREQUAL HELP)
    reason_message(AUTHOR_WARNING "reason_message: reasonable cmake message with colors in tty.
available message type and corresponding colors:
  - No Type        silver
  - VERBOSE        fuchsia
  - STATUS         green
  - AUTHOR_WARNING aqua
  - WARNING        yellow
  - FATAL_ERROR    red
  - SEND_ERROR     red
example:
  1. reason_message(STATUS \"This is a status message (green)\")
  2. reason_message(\"Simple message in silver.\")
  3. reason_message(FATAL_ERROR \"An error has occurred ...\")")
    reason_message(FATAL_ERROR)
  elseif(MessageType STREQUAL FATAL_ERROR OR MessageType STREQUAL SEND_ERROR)
    list(REMOVE_AT ARGV 0)
    message("${MessageType}" "${c_bred}${ARGV}${c_reset}")
  elseif(MessageType STREQUAL WARNING)
    list(REMOVE_AT ARGV 0)
    message("${MessageType}" "${c_byellow}${ARGV}${c_reset}")
  elseif(MessageType STREQUAL AUTHOR_WARNING)
    list(REMOVE_AT ARGV 0)
    message("${MessageType}" "${c_baqua}${ARGV}${c_reset}")
  elseif(MessageType STREQUAL STATUS)
    list(REMOVE_AT ARGV 0)
    message("${MessageType}" "${c_green}${ARGV}${c_reset}")
  elseif(MessageType STREQUAL VERBOSE)
    list(REMOVE_AT ARGV 0)
    if(REASON_BRIEF_PATH)
      string(REPLACE "${CMAKE_SOURCE_DIR}" "~" ARGV "${ARGV}")
    endif()
    message("${c_bfuchsia}${ARGV}${c_reset}")
  else()
    message("${c_silver}${ARGV}${c_reset}")
  endif()
endfunction()

function(reason_verbose)
  if(REASON_VERBOSE)
    reason_message(VERBOSE "${ARGV}")
  endif()
endfunction()

function(reason_print_help)
  reason_message(AUTHOR_WARNING "${ARGV}")
  reason_message(FATAL_ERROR)
endfunction()

function(reason_print_help_file FILE_NAME)
  file(READ "${FILE_NAME}" HELP_STR)
  reason_print_help("${HELP_STR}")
endfunction()

function(reason_util_configure_and_include IN_FILE OUT_FILE)
  set(OUT_FILE_FULL "${CMAKE_BINARY_DIR}/reason.cmake/${OUT_FILE}")
  if(EXISTS OUT_FILE_FULL)
    include("${OUT_FILE_FULL}")
  else()
    configure_file("${REASON_MODULE_DIR}/${IN_FILE}" "${OUT_FILE_FULL}" @ONLY)
    include("${OUT_FILE_FULL}")
  endif()
endfunction()

function(reason_extract_dependency_properties_to_target TARGET_NAME DEP)
  function(reason_extract_include_dirs TARGET_NAME DEP PROP_NAME)
    get_target_property(props "${DEP}" "${PROP_NAME}")
    if((NOT "${props}" STREQUAL "props-NOTFOUND") AND (NOT "${props}" STREQUAL ""))
      foreach(prop IN LISTS props)
        target_include_directories("${TARGET_NAME}" PRIVATE "${prop}")
        reason_verbose("    [private-include=${prop}]")
      endforeach()
    endif()
  endfunction()

  function(reason_extract_links TARGET_NAME DEP PROP_NAME)
    get_target_property(props "${DEP}" "${PROP_NAME}")
    if((NOT "${props}" STREQUAL "props-NOTFOUND") AND (NOT "${props}" STREQUAL ""))
      foreach(prop IN LISTS props)
        target_link_libraries("${TARGET_NAME}" PRIVATE "${prop}")
        reason_verbose("    [private-link=${prop}]")
      endforeach()
    endif()
  endfunction()

  if(NOT TARGET "${DEP}")
    reason_verbose("  exclude dependency: [dep=${DEP}] [reason=not a target]")
    return()
  endif()

  list(FIND REASON_EXCLUDE_PULL_DEPENDENCY "${DEP}" DEP_IN_EXCLUDE)
  if(NOT "${DEP_IN_EXCLUDE}" STREQUAL "-1")
    reason_verbose("  exclude dependency: [dep=${DEP}] [reason=found in REASON_EXCLUDE_PULL_DEPENDENCY]")
    return()
  endif()

  get_target_property(DEP_TYPE "${DEP}" "TYPE")
  reason_verbose("  dependency type: [type=${DEP_TYPE}]")

  reason_verbose("  add dep interface include directories: [dep=${DEP}]")
  reason_extract_include_dirs("${TARGET_NAME}" "${DEP}" "INTERFACE_INCLUDE_DIRECTORIES")
  reason_verbose("  add dep interface links: [dep=${DEP}]")
  reason_extract_links("${TARGET_NAME}" "${DEP}" "INTERFACE_LINK_LIBRARIES")

  if(NOT "${DEP_TYPE}" STREQUAL "INTERFACE_LIBRARY")
    reason_verbose("  add dep include directories: [dep=${DEP}]")
    reason_extract_include_dirs("${TARGET_NAME}" "${DEP}" "INCLUDE_DIRECTORIES")
    reason_verbose("  add dep links: [dep=${DEP}]")
    reason_extract_links("${TARGET_NAME}" "${DEP}" "LINK_LIBRARIES")
  endif()
endfunction()

function(reason_assign_property_between_targets TARGET_FROM TARGET_TO PROP_NAME)
  get_target_property(props "${TARGET_FROM}" "${PROP_NAME}")
  if((NOT "${props}" STREQUAL "props-NOTFOUND") AND (NOT "${props}" STREQUAL ""))
    set_target_properties("${TARGET_TO}" PROPERTIES "${PROP_NAME}" "${props}")
  endif()
endfunction()

function(reason_unique_target_properties TARGET_NAME)
  function(reason_unique_target_properties_one TARGET_NAME PROP_NAME)
    get_target_property(props "${TARGET_NAME}" "${PROP_NAME}")
    if("${props}" STREQUAL "props-NOTFOUND")
      return()
    endif()
    list(REMOVE_DUPLICATES props)
    # string(REGEX REPLACE ";+" ";" props "${props}") # @TODO Need testing
    # string(REGEX REPLACE ";$" ""  props "${props}") # @TODO Need testing
    set_target_properties("${TARGET_NAME}" PROPERTIES "${PROP_NAME}" "${props}")
  endfunction()

  get_target_property(TARGET_TYPE "${TARGET_NAME}" "TYPE")
  reason_unique_target_properties_one("${TARGET_NAME}" "INTERFACE_COMPILE_OPTIONS")
  reason_unique_target_properties_one("${TARGET_NAME}" "INTERFACE_INCLUDE_DIRECTORIES")
  reason_unique_target_properties_one("${TARGET_NAME}" "INTERFACE_LINK_LIBRARIES")

  if(NOT "${TARGET_TYPE}" STREQUAL "INTERFACE_LIBRARY")
    reason_unique_target_properties_one("${TARGET_NAME}" "COMPILE_OPTIONS")
    reason_unique_target_properties_one("${TARGET_NAME}" "INCLUDE_DIRECTORIES")
    reason_unique_target_properties_one("${TARGET_NAME}" "LINK_LIBRARIES")
  endif()
endfunction()

function(reason_show_target_properties LEVEL TARGET_NAME)
  if(("${LEVEL}" STREQUAL "VERBOSE") AND (NOT REASON_VERBOSE))
    return()
  endif()

  function(reason_show_target_properties_one LEVEL TARGET_NAME PROP_NAME)
    get_target_property(props "${TARGET_NAME}" "${PROP_NAME}")
    if((NOT "${props}" STREQUAL "props-NOTFOUND") AND (NOT "${props}" STREQUAL ""))
      reason_message("${LEVEL}" "  ${PROP_NAME}:")
      foreach(prop IN LISTS props)
        reason_message("${LEVEL}" "    ${prop}")
      endforeach()
    else()
      reason_message("${LEVEL}" "  ${PROP_NAME}: None")
    endif()
  endfunction()

  get_target_property(TARGET_TYPE "${TARGET_NAME}" "TYPE")
  reason_message("${LEVEL}" "${TARGET_NAME} properties: [type=${TARGET_TYPE}]")

  reason_show_target_properties_one("${LEVEL}" "${TARGET_NAME}" "INTERFACE_COMPILE_OPTIONS")
  reason_show_target_properties_one("${LEVEL}" "${TARGET_NAME}" "INTERFACE_INCLUDE_DIRECTORIES")
  reason_show_target_properties_one("${LEVEL}" "${TARGET_NAME}" "INTERFACE_LINK_LIBRARIES")

  if(NOT "${TARGET_TYPE}" STREQUAL "INTERFACE_LIBRARY")
    reason_show_target_properties_one("${LEVEL}" "${TARGET_NAME}" "COMPILE_OPTIONS")
    reason_show_target_properties_one("${LEVEL}" "${TARGET_NAME}" "INCLUDE_DIRECTORIES")
    reason_show_target_properties_one("${LEVEL}" "${TARGET_NAME}" "LINK_LIBRARIES")
  endif()
endfunction()
