function(clang_format_setup)
  cmake_parse_arguments(
        CF_ARG # prefix 
        "" # no boolean args
        "VERSION" # clang-format version
        "" # no multi-value args
        ${ARGN} 
    )
  if(NOT CF_ARG_VERSION)
    set(CF_NAME clang-format)
  else()
    set(CF_NAME clang-format-${CF_ARG_VERSION})
  endif()
  find_program(CLANG_FORMAT_EXECUTABLE NAMES ${CF_NAME})
  
  if(NOT CLANG_FORMAT_EXECUTABLE)
    message(STATUS "Looking for clang-format - NOT found, please install "
      "${CF_NAME} to enable automatic code formatting."
    )
    return()
  endif()

  message(STATUS "Found ${CF_NAME}.")
endfunction()

#===============================================================================
function(_property_add property_name)
  get_property(is_defined GLOBAL PROPERTY ${property_name} DEFINED)
  if(NOT is_defined)
    define_property(GLOBAL PROPERTY ${property_name}
      BRIEF_DOCS "${property_name}"
      FULL_DOCS "Global properties for ${property_name}"
    )
  endif()
  foreach(item ${ARGN})
    set_property(GLOBAL APPEND PROPERTY ${property_name} "${item}")
  endforeach()
endfunction()

#===============================================================================
function(clang_format_add_sources)
  foreach(source ${ARGN})
    if(IS_ABSOLUTE "${source}")
      set(source_abs "${source}")
    else()
      get_filename_component(source_abs
        "${CMAKE_CURRENT_LIST_DIR}/${source}" ABSOLUTE)
    endif()
    if(EXISTS "${source_abs}")
      _property_add(CLANG_FORMAT_FORMAT_FILES "${source_abs}")
    else()
      message(FATAL_ERROR
        "Source file '${source}' does not exist at absolute path"
        " '${source_abs}'. This should never happen. Did you recently delete"
        " this file or modify 'CMAKE_CURRENT_LIST_DIR'")
    endif()
  endforeach()
endfunction()

#===============================================================================
function(clang_format_add_targets)
  get_property(formatting_files GLOBAL PROPERTY CLANG_FORMAT_FORMAT_FILES)
  list(LENGTH formatting_files formatting_files_length)

  if(formatting_files)
    message(STATUS "Formatting on ${formatting_files_length} source files.")

    add_custom_target(format
      COMMAND ${CMAKE_COMMAND} -E echo "Formatting ${formatting_files_length} files..."
      COMMAND ${CLANG_FORMAT_EXECUTABLE} -style=file -i ${formatting_files}
      COMMAND ${CMAKE_COMMAND} -E echo "Done."
      DEPENDS ${CLANG_FORMAT_EXECUTABLE}
      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    )
    add_custom_target(check-format
      COMMAND ${CMAKE_COMMAND} -E echo "Checking ${formatting_files_length} files..."
      COMMAND ${CLANG_FORMAT_EXECUTABLE} -style=file --dry-run --Werror ${formatting_files} >/dev/null
      COMMAND ${CMAKE_COMMAND} -E echo "Done."
      DEPENDS ${CLANG_FORMAT_EXECUTABLE}
      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    )
  else()
    add_custom_target(format
        COMMAND ${CMAKE_COMMAND} -E echo "No file to format code style."
    )
    add_custom_target(check-format
        COMMAND ${CMAKE_COMMAND} -E echo "No file to check code style."
    )
  endif()
endfunction()
