cmake_minimum_required (VERSION 3.4)

# CMake utils
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

# Forbids in-source builds
include(CheckNotInSources)

#------------------------------------------------------
#----------------- Main configuration -----------------
#------------------------------------------------------

# custom variable allowing to define version suffixes such as -rc*, -beta*, ...
set(PUGS_VERSION "0.4.0")

# deduce PUGS_SHORT_VERSION using regex
string(REGEX MATCH "^[0-9]+\.[0-9]+\.[0-9]+" PUGS_SHORT_VERSION ${PUGS_VERSION})
if("${PUGS_SHORT_VERSION}" STREQUAL "")
  message(FATAL_ERROR "Unable to compute short version from PUGS_VERSION=${PUGS_VERSION}")
endif()

# set project version as PUGS_SHORT_VERSION
project (Pugs VERSION ${PUGS_SHORT_VERSION})

#------------------------------------------------------

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

#------------------------------------------------------

set(PUGS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(PUGS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")

# Change RelWithDebInfo to compile assertions
SET("CMAKE_CXX_FLAGS_RELWITHDEBINFO"
   "-g -O2"
  CACHE STRING "Flags used by the compiler during release builds with debug info and assertions"
  FORCE )
SET("CMAKE_C_FLAGS_RELWITHDEBINFO"
   "-g -O2"
  CACHE STRING "Flags used by the compiler during release builds with debug info and assertions"
  FORCE )

# Add new build types
set(CMAKE_CXX_FLAGS_COVERAGE
  "-g -O0 --coverage"
  CACHE STRING "Flags used by the C++ compiler during coverage builds."
  FORCE )
set(CMAKE_C_FLAGS_COVERAGE
  "-g -O0 --coverage"
  CACHE STRING "Flags used by the C compiler during coverage builds."
  FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
  "--coverage"
  CACHE STRING "Flags used for linking binaries during coverage builds."
  FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
  "--coverage"
  CACHE STRING "Flags used by the shared libraries linker during coverage builds."
  FORCE )
mark_as_advanced(
  CMAKE_CXX_FLAGS_COVERAGE
  CMAKE_C_FLAGS_COVERAGE
  CMAKE_EXE_LINKER_FLAGS_COVERAGE
  CMAKE_SHARED_LINKER_FLAGS_COVERAGE )

if(CMAKE_BUILD_TYPE)
  string(REGEX MATCH "(Debug|Release|RelWithDebInfo|MinSizeRel|Coverage)" VALID_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
  if(NOT VALID_BUILD_TYPE)
    message(FATAL_ERROR "Invalid CMAKE_BUILD_TYPE: '${CMAKE_BUILD_TYPE}'")
  endif()
endif()

# Default build type is RelWIthDebInfo
if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
      "Choose the type of build: Debug Release RelWithDebInfo MinSizeRel Coverage."
      FORCE)
endif()

#------------------------------------------------------

# Checks if compiler version is compatible with Pugs sources
set(GNU_CXX_MIN_VERSION "7.0.0")
set(CLANG_CXX_MIN_VERSION "5.0.0")

# Pugs default compiler flags
set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS} -Wall -Wextra")

if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${GNU_CXX_MIN_VERSION}")
    message(FATAL_ERROR "Pugs build requires at least g++-${GNU_CXX_MIN_VERSION}")
  endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${CLANG_CXX_MIN_VERSION}")
    message(FATAL_ERROR "Pugs build requires at least llvm/clang ++-${CLANG_CXX_MIN_VERSION}")
  endif()
  set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS} -Wsign-compare -Wunused -Wunused-member-function -Wunused-private-field")
endif()

#------------------------------------------------------
# defaults use of MPI
set(PUGS_ENABLE_MPI AUTO CACHE STRING
  "Choose one of: AUTO ON OFF")

if (NOT PUGS_ENABLE_MPI MATCHES "^(AUTO|ON|OFF)$")
  message(FATAL_ERROR "PUGS_ENABLE_MPI='${PUGS_ENABLE_MPI}'. Must be set to one of AUTO, ON or OFF")
endif()

# checks for MPI
if (PUGS_ENABLE_MPI MATCHES "^(AUTO|ON)$")
  find_package(MPI)
endif()

if (${MPI_FOUND})
  set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS} ${MPI_CXX_COMPILER_FLAGS}")
  include_directories(SYSTEM ${MPI_CXX_INCLUDE_DIRS})
elseif(PUGS_ENABLE_MPI STREQUAL "ON")
  message(FATAL_ERROR "Could not find MPI library while requested")
endif()

set(PUGS_HAS_MPI ${MPI_FOUND})

#------------------------------------------------------
# search for clang-format

find_program(CLANG_FORMAT clang-format)
if (CLANG_FORMAT)
  add_custom_target(clang-format
    COMMAND echo "running ${CLANG_FORMAT} ..."
    COMMAND ${CMAKE_COMMAND}
    -DPUGS_SOURCE_DIR="${PUGS_SOURCE_DIR}"
    -DCLANG_FORMAT="${CLANG_FORMAT}"
    -P ${PUGS_SOURCE_DIR}/cmake/ClangFormatProcess.cmake)
else ()
  message(WARNING "clang-format no found!")
endif()

#------------------------------------------------------

# setting Kokkos defaults to OpenMP when available
include(FindOpenMP)
if(${OPENMP_FOUND})
  set(KOKKOS_ENABLE_OPENMP ON CACHE BOOL "")
endif()

# do not build ETI by default
set(KOKKOS_ENABLE_EXPLICIT_INSTANTIATION OFF CACHE BOOL "")

# do not use Kokkos deprecated code
set(KOKKOS_ENABLE_DEPRECATED_CODE OFF CACHE BOOL "")

# Kokkos compiler flags
add_subdirectory(${PUGS_SOURCE_DIR}/packages/kokkos)
include_directories(SYSTEM ${Kokkos_INCLUDE_DIRS_RET})
include(GetKokkosCompilerFlags)

# do not pollute compilation with Kokkos internal warnings
set_target_properties(kokkos PROPERTIES COMPILE_FLAGS "-w")

# sets Kokkos debug flags when non release build
if (CMAKE_BUILD_TYPE MATCHES "^Release$")
  set (KOKKOS_ENABLE_DEBUG OFF)
else()
  set (KOKKOS_ENABLE_DEBUG ON)
endif()

# C++ 17 flags
if(${CMAKE_VERSION} VERSION_LESS "3.8.0")
  message(WARNING "Please consider to switch to CMake >= 3.8")
  set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS}  -std=gnu++1z")
  if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
    set(PUGS_CXX_FLAGS "${PUGS_CXX_FLAGS}  -Wno-c++17-extensions")
  endif()
else()
  set(CMAKE_CXX_STANDARD "17")
endif()

# Compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PUGS_CXX_FLAGS}")

# Add debug mode for Standard C++ library
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -D_LIBCPP_DEBUG=1 ${PUGS_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -D_GLIBCXX_DEBUG -D_LIBCPP_DEBUG=1 ${PUGS_CXX_FLAGS}")

#------------------------------------------------------

# Rang (colors? Useless thus necessary!)
include_directories(${PUGS_SOURCE_DIR}/packages/rang/include)

# CLI11
include_directories(${PUGS_SOURCE_DIR}/packages/CLI11/include)

# PGETL
include_directories(${PUGS_SOURCE_DIR}/packages/PEGTL/include/tao)

# Pugs src
add_subdirectory(src)

include_directories(src)
include_directories(src/algebra)
include_directories(src/language)
include_directories(src/mesh)
include_directories(src/output)
include_directories(src/scheme)
include_directories(src/utils)

# Pugs generated sources
include_directories(${PUGS_BINARY_DIR}/src/utils)

# Pugs tests

set(CATCH_MODULE_PATH "${PUGS_SOURCE_DIR}/packages/Catch2")
set(CATCH_INCLUDE_PATH "${CATCH_MODULE_PATH}/single_include/catch2")

include("${CATCH_MODULE_PATH}/contrib/ParseAndAddCatchTests.cmake")
add_subdirectory("${CATCH_MODULE_PATH}")

add_subdirectory(tests)
enable_testing()

# unit tests coverage

if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")
  find_program(GCOVR gcovr)
  if(NOT GCOVR)
    message(FATAL_ERROR "gcovr not found, cannot perform coverage.")
  endif()

  if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
    string(REPLACE "clang++" "llvm-cov" LLVM_COV "${CMAKE_CXX_COMPILER}")
    if(NOT EXISTS "${LLVM_COV}")
      message(FATAL_ERROR "could not find ${LLVM_COV}, cannot perform coverage (using g++ for coverage is recommended).")
    endif()

    set(GCOVR_EXTRA "--gcov-executable=${LLVM_COV} gcov")
  endif()

  set (GCOVR_EXCLUDE
    -e "${PUGS_SOURCE_DIR}/src/main.cpp"
    -e "${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.cpp"
    -e "${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.hpp"
    )

  set(GCOVR_OPTIONS --object-directory="${PUGS_BINARY_DIR}" -r "${PUGS_SOURCE_DIR}/src" ${GCOVR_EXCLUDE} ${GCOVR_EXTRA})

  add_custom_target(run_unit_tests
    ALL
    COMMAND ${CMAKE_CTEST_COMMAND} -j ${PROCESSOR_COUNT}
    DEPENDS unit_tests mpi_unit_tests pugs
    COMMENT "Executing unit tests."
    )

  add_custom_target(coverage
    ALL
    COMMAND ${GCOVR} ${GCOVR_OPTIONS} --exclude-unreachable-branches --sort-percentage
    DEPENDS run_unit_tests
    COMMENT "Running gcovr to build coverage report."
    )

  add_custom_target(coverage-report
    ALL
    COMMAND ${CMAKE_COMMAND} -E remove_directory "${PUGS_BINARY_DIR}/coverage"
    COMMAND ${CMAKE_COMMAND} -E make_directory "${PUGS_BINARY_DIR}/coverage"
    COMMAND ${GCOVR} ${GCOVR_OPTIONS} --delete --html --html-details -o "${PUGS_BINARY_DIR}/coverage/index.html"
    DEPENDS coverage
    COMMENT "Building coverage html report."
    WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
    )

endif()

#------------------------------------------------------
# Search for ParMETIS

if(${MPI_FOUND})
  find_package(ParMETIS REQUIRED)
endif()

# -----------------------------------------------------

link_libraries("-rdynamic")

# ------------------- Source files --------------------
# Pugs binary
add_executable(
  pugs
  src/main.cpp)

# Libraries
target_link_libraries(
  pugs
  PugsMesh
  PugsUtils
  PugsLanguage
  kokkos
  ${PARMETIS_LIBRARIES}
  ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES}
)