cmake_minimum_required (VERSION 3.10)
project (ftk)

file(READ "version.txt" FTK_VERSION)

include(GNUInstallDirs)
# include (ExternalProject)

if (FTK_BUILD_PARAVIEW)
  find_package (ParaView REQUIRED)
endif ()

if (FTK_BUILD_PYFTK)
  set (FTK_HAVE_PYBIND11 TRUE)
endif()

set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON)

set (CMAKE_EXPORT_COMPILE_COMMANDS ON)
set (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

# dependencies
function (ftk_option name description default)
  set (FTK_USE_${name} ${default} CACHE STRING "${description}")
  set_property (CACHE FTK_USE_${name} PROPERTY STRINGS "TRUE;AUTO;FALSE")
endfunction ()

ftk_option (ADIOS2 "Use ADIOS2" FALSE)
ftk_option (Boost "Use Boost" FALSE) # not used
ftk_option (CUDA "Use CUDA" FALSE) # experimental
ftk_option (GSL "Use GSL (GNU Scientific Library)" FALSE)
ftk_option (GMP "Use GMP (GNU Multiple Precision Arithmetic Library)" FALSE)
ftk_option (HDF5 "Use HDF5" AUTO)
ftk_option (Kokkos "Use Kokkos" FALSE) # experimental
ftk_option (LevelDB "Use LevelDB" FALSE)
ftk_option (MPI "Use MPI" FALSE)
ftk_option (MPSolve "Use MPSolve" FALSE)
ftk_option (NETCDF "Use NetCDF" AUTO)
ftk_option (OpenMP "Use OpenMP" FALSE)
ftk_option (PNETCDF "Use parallel-netcdf" FALSE)
ftk_option (PNG "Use PNG" AUTO)
ftk_option (Qt5 "Use Qt5" FALSE)
ftk_option (RocksDB "Use RocksDB" FALSE)
ftk_option (TBB "Use TBB (Intel Thread Building Blocks)" FALSE) # this feature is experimental
ftk_option (VTK "Use VTK" AUTO)

find_package (Threads REQUIRED)

if (FTK_USE_ADIOS2 STREQUAL AUTO)
  find_package (ADIOS2 QUIET)
elseif (FTK_USE_ADIOS2)
  find_package (ADIOS2 REQUIRED)
endif ()
if (ADIOS2_FOUND)
  if (ADIOS2_HAVE_MPI)
    set (FTK_USE_MPI ON)
  endif ()
  set (FTK_HAVE_ADIOS2 ON)
  # set (ADIOS2_INCLUDE_DIRS ${ADIOS2_DIR}/include)
  include_directories (${ADIOS2_INCLUDE_DIRS})
endif ()

if (FTK_USE_VTK STREQUAL AUTO)
  find_package (VTK QUIET)
elseif (FTK_USE_VTK)
  find_package (VTK REQUIRED)
endif ()
if (VTK_FOUND)
  set (FTK_HAVE_VTK TRUE)
  if (VTK_MAJOR_VERSION STRLESS 9) # vtk8.9 does not require VTK_USE_FILE as well
    include (${VTK_USE_FILE})
  endif ()
endif ()

if (FTK_USE_Kokkos STREQUAL AUTO)
  find_package (Kokkos QUIET)
elseif (FTK_USE_Kokkos)
  find_package (Kokkos REQUIRED)
endif ()
if (Kokkos_FOUND)
  set (FTK_HAVE_KOKKOS TRUE)
  include_directories (${Kokkos_INCLUDE_DIRS})
endif ()

if (FTK_USE_Qt5 STREQUAL AUTO)
  find_package (OpenGL QUIET)
  find_package (Qt5Widgets QUIET)
  find_package (Qt5OpenGL QUIET)
  find_package (GLEW QUIET)
elseif (FTK_USE_Qt5)
  find_package (OpenGL REQUIRED)
  find_package (Qt5Widgets REQUIRED)
  find_package (Qt5OpenGL REQUIRED)
  find_package (GLEW REQUIRED)
endif ()
if (Qt5OpenGL_FOUND AND GLEW_FOUND)
  set (FTK_HAVE_QT5 TRUE)
  set (FTK_HAVE_QT TRUE)
  add_definitions("-Wno-deprecated-declarations")
  include_directories (
    ${Qt5Widgets_INCLUDE_DIRS}
    ${Qt5OpenGL_INCLUDE_DIRS}
    ${GLEW_INCLUDE_DIR})
endif ()

if (FTK_USE_MPI STREQUAL AUTO)
  find_package (MPI QUIET COMPONENTS MPICXX CXX)
elseif (FTK_USE_MPI)
  find_package (MPI REQUIRED COMPONENTS MPICXX CXX)
endif ()
if (MPI_FOUND)
  set (FTK_HAVE_MPI TRUE)
  set (CMAKE_CXX_COMPILER ${MPI_CXX_COMPILER})
  include_directories (${MPI_C_INCLUDE_DIRS})
else ()
  # add_definitions (-DDIY_NO_MPI)
endif ()

if (FTK_USE_HDF5 STREQUAL AUTO)
  find_package (HDF5 QUIET)
elseif (FTK_USE_HDF5)
  find_package (HDF5 REQUIRED)
endif()
if (HDF5_FOUND)
  set (FTK_HAVE_HDF5 TRUE)
  include_directories (${HDF5_INCLUDE_DIRS})
endif ()

if (FTK_USE_NETCDF STREQUAL AUTO)
  find_package (NetCDF QUIET)
elseif (FTK_USE_NETCDF)
  find_package (NetCDF REQUIRED)
endif ()
if (NETCDF_FOUND)
  set (FTK_HAVE_NETCDF TRUE)
  include_directories (${NETCDF_INCLUDE_DIR})
endif ()

if (FTK_USE_MPSolve STREQUAL AUTO)
  find_package (MPSolve QUIET)
elseif (FTK_USE_MPSolve)
  find_package (MPSolve REQUIRED)
endif ()
if (MPSolve_FOUND)
  set (FTK_USE_GMP TRUE)
  set (FTK_HAVE_MPSOLVE TRUE)
  add_definitions ("-Wno-register")
  include_directories (${MPSolve_INCLUDE_DIR})
endif ()

if (FTK_USE_GMP STREQUAL AUTO)
  find_package (GMP QUIET)
elseif (FTK_USE_GMP)
  find_package (GMP REQUIRED)
endif ()
if (GMP_FOUND)
  set (FTK_HAVE_GMP TRUE)
  include_directories (${GMP_INCLUDE_DIRS})
endif ()

if (FTK_USE_GSL STREQUAL AUTO)
  find_package (GSL QUIET)
elseif (FTK_USE_GSL)
  find_package (GSL REQUIRED)
endif ()
if (GSL_FOUND)
  set (FTK_HAVE_GSL TRUE)
  include_directories (${GSL_INCLUDE_DIRS})
endif ()

if (FTK_USE_TBB STREQUAL AUTO)
  find_package (TBB QUIET)
elseif (FTK_USE_TBB)
  find_package (TBB REQUIRED)
endif()
if (TBB_FOUND)
  set (FTK_HAVE_TBB TRUE)
  include_directories (${TBB_INCLUDE_DIRS})
endif ()

if (FTK_USE_PNETCDF STREQUAL AUTO)
  find_package (PNetCDF QUIET)
elseif (FTK_USE_PNETCDF)
  find_package (PNetCDF REQUIRED)
endif()
if (PNETCDF_FOUND)
  set (FTK_HAVE_PNETCDF TRUE)
  include_directories (${PNETCDF_INCLUDE_DIR})
endif ()

if (FTK_USE_PNG STREQUAL AUTO)
  find_package (PNG QUIET)
elseif (FTK_USE_PNG)
  find_package (PNG REQUIRED)
endif ()
if (PNG_FOUND)
  set (FTK_HAVE_PNG TRUE)
  include_directories (${PNG_INCLUDE_DIRS})
endif ()

if (FTK_USE_RocksDB STREQUAL AUTO)
  find_package (RocksDB QUIET)
elseif (FTK_USE_RocksDB)
  find_package (RocksDB REQUIRED)
endif ()
if (RocksDB_FOUND)
  set (FTK_HAVE_ROCKSDB TRUE)
  include_directories (${RocksDB_INCLUDE_DIR})
endif ()

if (FTK_USE_OpenMP STREQUAL AUTO)
  find_package (OpenMP QUIET)
elseif (FTK_USE_OpenMP)
  find_package (OpenMP REQUIRED)
endif ()
if (OpenMP_CXX_FOUND)
  set (FTK_HAVE_OPENMP TRUE)
endif ()

if (FTK_USE_CUDA STREQUAL AUTO)
  find_package (CUDA QUIET)
elseif (FTK_USE_CUDA)
  find_package (CUDA REQUIRED)
endif ()
if (CUDA_FOUND)
  set (FTK_HAVE_CUDA TRUE)
  set (CUDA_PROPAGATE_HOST_FLAGS OFF)
  set (CUDA_NVCC_FLAGS "--expt-relaxed-constexpr")
  include_directories (${CUDA_INCLUDE_DIRS})
endif ()
  
set (Boost_USE_STATIC_LIBS ON)
set (Boost_USE_MULTITHREADED ON)
set (Boost_USE_STATIC_RUNTIME OFF)
if (FTK_USE_Boost STREQUAL AUTO)
  find_package (Boost QUIET COMPONENTS system)
elseif (FTK_USE_Boost)
  find_package (Boost REQUIRED COMPONENTS system)
endif ()
if (Boost_SYSTEM_FOUND)
  set (FTK_HAVE_BOOST TRUE)
  # add_definitions (-DBOOST_NO_AUTO_PTR) # c++17 deprecated auto_ptr
  include_directories (${Boost_INCLUDE_DIRS})
endif ()

### 

set (FTK_INCLUDE_DIR 
  ${CMAKE_CURRENT_SOURCE_DIR}/include
  ${CMAKE_CURRENT_BINARY_DIR}/include
)
include_directories (${FTK_INCLUDE_DIR})

option (FTK_BUILD_EXECUTABLES "Build executables" ON)
option (FTK_BUILD_TESTS "Build tests" OFF)
option (FTK_BUILD_APPLICATIONS "Build applications" OFF)
option (FTK_BUILD_PARAVIEW "Build ParaView plugins" OFF)
option (FTK_BUILD_PYFTK "Build Python bindings" OFF)
set (FTK_FP_PRECISION "32768" CACHE STRING "Fixed point precision")
set (FTK_CP_MAX_NUM_VARS "2" CACHE STRING "Max number of scalar properties for critical points")

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/ftk/ftk_config.hh.in
  ${CMAKE_CURRENT_BINARY_DIR}/include/ftk/ftk_config.hh)

install (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ftk DESTINATION
  ${CMAKE_INSTALL_INCLUDEDIR})
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/include/ftk/ftk_config.hh
  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ftk)

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FTKConfig.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/FTKConfig.cmake)

include (CMakePackageConfigHelpers)
set (FTK_ROOT_DIR ${CMAKE_INSTALL_PREFIX})
set (FTK_INCLUDE_DIR ${CMAKE_INSTALL_PREFIX}/include)
configure_package_config_file(
  ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FTKConfig.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/install/FTKConfig.cmake
  PATH_VARS FTK_INCLUDE_DIR FTK_ROOT_DIR
  INSTALL_DESTINATION lib/cmake
  NO_CHECK_REQUIRED_COMPONENTS_MACRO)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/install/FTKConfig.cmake
  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/FTK/)

if (FTK_BUILD_PYFTK)
  include_directories (${CMAKE_CURRENT_SOURCE_DIR}/python/pybind11/include
    ${PYTHON_INCLUDE_DIRS})
  add_subdirectory (python)
endif ()

add_subdirectory (src)

if (FTK_BUILD_EXECUTABLES)
  add_subdirectory (cli)
endif ()

if (FTK_BUILD_APPLICATIONS)
  add_subdirectory (apps)
endif ()

if (FTK_BUILD_PARAVIEW)
  add_subdirectory (paraview)
endif ()

# If FTK_BUILD_TESTS is on, enable testing.
if (FTK_BUILD_TESTS)
  # find_package (GTest REQUIRED)
  enable_testing ()
  include (Catch)
  add_subdirectory (tests)
endif ()

# If FTK_BUILD_EXAMPLES is on, add the examples subdirectory to the build.
# if (FTK_BUILD_EXAMPLES)
#   add_subdirectory (examples)
# endif ()

## summary
message ("")
message ("FTK build configuration:")
message ("  FTK Version:  ${FTK_VERSION}")
message ("")
message ("  C++ compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} ${CMAKE_CXX_COMPILER_WRAPPER}")
message ("                ${CMAKE_CXX_COMPILER}")
message ("")
message ("  Installation prefix: ${CMAKE_INSTALL_PREFIX}")
message("        bin: ${CMAKE_INSTALL_BINDIR}")
message("        lib: ${CMAKE_INSTALL_LIBDIR}")
message("    include: ${CMAKE_INSTALL_INCLUDEDIR}")
message("      cmake: ${CMAKE_INSTALL_CMAKEDIR}")
message ("")
message("  Features:")
message("    Build Type:   ${CMAKE_BUILD_TYPE}")
message("    Executables:  ${FTK_BUILD_EXECUTABLES}")
message("    Applications: ${FTK_BUILD_APPLICATIONS}")
message("    Testing:      ${FTK_BUILD_TESTS}")
message("    ParaView:     ${FTK_BUILD_PARAVIEW}")
message("    PyFTK:        ${FTK_BUILD_PYFTK}")
if (${FTK_BUILD_PYFTK})
  message("        Python executable:          ${PYTHON_EXECUTABLE}")
  message("        Python include directories: ${PYTHON_INCLUDE_DIRS}")
  message("        Python library:             ${PYTHON_LIBRARY}")
endif ()
message ("")
message("  Dependencies:")
message("    ADIOS2       ${FTK_USE_ADIOS2} ${ADIOS2_FOUND}")
if (${FTK_HAVE_ADIOS2})
  message("      Include path:    ${ADIOS2_INCLUDES}")
  message("      Libraries:       ${ADIOS2_LIBRARIES}")
endif ()
message("    Boost:       ${FTK_USE_Boost} ${Boost_FOUND}")
if (${FTK_HAVE_BOOST})
  message("      Version:         ${Boost_VERSION_STRING}")
  message("      Include path:    ${Boost_INCLUDE_DIR}")
  message("      Libraries:       ${Boost_LIBRARIES}")
endif ()
message("    CUDA:        ${FTK_USE_CUDA} ${CUDA_FOUND}")
if (${FTK_HAVE_CUDA})
  message("      Version:         ${CUDA_VERSION}")
  message("      Toolkit path:    ${CUDA_TOOLKIT_ROOT_DIR}")
  message("      SDK path:        ${CUDA_SDK_ROOT_DIR}")
  message("      Host compiler:   ${CUDA_HOST_COMPILER}")
  message("      NVCC flags:      ${CUDA_NVCC_FLAGS}")
  message("      Include path:    ${CUDA_INCLUDE_DIRS}")
  message("      Libraries:       ${CUDA_LIBRARIES}")
endif()
message("    GSL:         ${FTK_USE_GSL} ${GSL_FOUND}")
message("    GMP:         ${FTK_USE_GMP} ${GMP_FOUND}")
if (${GMP_FOUND})
  message("      Include path:    ${GMP_INCLUDE_DIRS}")
  message("      Libraries:       ${GMP_LIBRARIES}")
endif()
message("    HDF5:        ${FTK_USE_HDF5} ${HDF5_FOUND}")
if (${FTK_HAVE_HDF5})
  message("      Version:           ${HDF5_VERSION}")
  message("      Include path:      ${HDF5_INCLUDE_DIRS}")
  message("      Definitions:       ${HDF5_DEFINITIONS}")
  message("      Libraries:         ${HDF5_LIBRARIES}")
endif ()
message("    Kokkos*      ${FTK_USE_Kokkos} ${Kokkos_FOUND}")
message("    MPI:         ${FTK_USE_MPI} ${MPI_FOUND}")
if (${FTK_HAVE_MPI})
  message("      MPI C++ compiler:  ${MPI_CXX_COMPILER}")
  message("      mpiexec:           ${MPIEXEC_EXECUTABLE}")
endif ()
message("    MPSolve:     ${FTK_USE_MPSolve} ${MPSolve_FOUND}")
message("    NetCDF:      ${FTK_USE_NETCDF} ${NETCDF_FOUND}")
if (${FTK_HAVE_NETCDF})
  message("      Include path:   ${NETCDF_INCLUDE_DIR}")
  message("      Library:        ${NETCDF_LIBRARIES}")
endif ()
message("    OpenMP*:     ${FTK_USE_OpenMP} ${OpenMP_CXX_FOUND}")
if (${FTK_HAVE_OPENMP})
  message("      Version:         ${OpenMP_CXX_VERSION}")
  message("      Flags:           ${OpenMP_CXX_FLAGS}")
  message("      Library:         ${OpenMP_CXX_LIBRARY}")
endif ()
message("    PNETCDF:     ${FTK_USE_PNETCDF} ${PNetCDF_FOUND}")
message("    PNG:         ${FTK_USE_PNG} ${PNG_FOUND}")
if (${FTK_HAVE_PNG})
  message("      Library:   ${PNG_LIBRARY}")
endif()
message("    Qt5:         ${FTK_USE_Qt5} ${FTK_HAVE_QT5}")
if (${FTK_HAVE_QT5})
  message("      Qt5OpenGL:  ${Qt5OpenGL_DIR}")
  message("      Qt5Widigets:${Qt5Widgets_DIR}")
  message("      Qt5Core:    ${Qt5Core_DIR}")
  message("      Qt5Gui:     ${Qt5Gui_DIR}")
endif ()
message("    TBB*:        ${FTK_USE_TBB} ${TBB_FOUND}")
if (${FTK_HAVE_TBB})
  message("      Include path:   ${TBB_INCLUDE_DIR}")
  message("      Library:        ${TBB_LIBRARY_DEBUG}")
  message("      Library:        ${TBB_LIBRARY_RELEASE}")
endif()
message("    VTK:         ${FTK_USE_VTK} ${FTK_HAVE_VTK}")
if (${FTK_HAVE_VTK})
  message("      VTK_DIR:     ${VTK_DIR}")
  message("      VTK_VERSION: ${VTK_MAJOR_VERSION}.${VTK_MINOR_VERSION}")
endif ()
message ("")
message ("  (*) Experimental and not recommend to use")
message ("")
