CMake_Minimum_Required(VERSION 3.6)  # cmake v3.6 available since July 2016
CMake_Policy(VERSION 3.6)

# *** Set global stuff
Set(CMAKE_CXX_STANDARD 14)
Set(ENV{LC_ALL} C)   # C locale
Set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeScripts CACHE PATH
  "Path to cmake scripts" FORCE)
Set(CMAKE_INCLUDE_CURRENT_DIR ON)
Set(MODULE_TYPE SHARED)  # MorphoGraphX plugins are meant to link to each other and do so (despite of dependency problems)
Set(MGX_BINARY_DIR bin)
Set(MGX_PROCESS_DIR lib/MorphoGraphX/processes)
Set(MGX_PROCESS_INCLUDE_DIR include/MorphoGraphX)
Set(MGX_PROCESS_CUDA_INCLUDE_DIR ${MGX_PROCESS_INCLUDE_DIR})
Set(MGX_DOC_DIR share/doc/MorphoGraphX)
Set(MGX_RESOURCES_DIR share/MorphoGraphX)
Set(MGX_LIB_DIR lib/MorphoGraphX)

# Win32 stuff
If(WIN32)
  Add_Definitions(-D_USE_MATH_DEFINES) # needed to get M_PI and friends
  # Win32 doesn't use libdl?
  set(CMAKE_DL_LIBS "")
Else()
  set(CMAKE_DL_LIBS dl)
EndIf()

# Public headers make use of different data structures depending on whether TR1 or C++11 types are used, therefore the whole project and dependencies must default to the same variant
# Caution - setting "98" can still result in actually using "11"
If(NOT CMAKE_CXX_STANDARD)
  Set(CMAKE_CXX_STANDARD 11)
EndIf()
Set(CMAKE_CXX_STANDARD_REQUIRED ON)
Set(CMAKE_CXX_EXTENSIONS OFF)
If(CMAKE_CXX_STANDARD EQUAL 98)
  # Try to use TR1
  Set(MGX_USE_TR1 ON)
EndIf()

If(CMAKE_VERSION VERSION_LESS 3.1)
  Add_Compile_Options("-std=c++${CMAKE_CXX_STANDARD}")
EndIf()

# Change the default install prefix
If(WIN32)
  Set(CMAKE_INSTALL_PREFIX "\\Program Files/MorphoGraphX" CACHE PATH "Install prefix for `make install`")
  Set(CPACK_PACKAGING_INSTALL_PREFIX "")
ElseIf(UNIX)
  Set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Install prefix for `make install`")
  Set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
EndIf()
Set(MGXPREFIX ${CMAKE_INSTALL_PREFIX})

# Flags defining directories are best set here and passed as defines, since any change will be automatically synchronized.
Set(MGXPROCPATH "${MGXPREFIX}/${MGX_PROCESS_DIR}")
Set(MGXPROCINCLPATH "${MGXPREFIX}/${MGX_PROCESS_INCLUDE_DIR}")
Set(MGXDOCPATH "${MGXPREFIX}/${MGX_DOC_DIR}")
Set(MGXRESPATH "${MGXPREFIX}/${MGX_RESOURCES_DIR}")
Set(MGXLIBPATH "${MGXPREFIX}/${MGX_LIB_DIR}")

Set(CMAKE_BUILD_TYPE Release CACHE STRING
  "Options are: <empty> Debug Release RelWithDebInfo MinSizeRel")

# *** Set up MGX project
Set(MGX_VERSION 2.0)     # current version
# If user manually selects a revision, respect that. Otherwise, fill it from svn.
If(NOT MGX_REVISION)
  Set(MGX_REVISION "unknown")
  # Find revision number
  Option(USE_GIT_REVISION_NAME "Get the git revision number automatically.\
                                Set to false for exported copy."
         TRUE)
  If(USE_GIT_REVISION_NAME)
    Find_Package(Git)
    If(GIT_FOUND)
        Include(GetGitRevisionDescription)
        Git_Describe(MGX_REVISION --always)
        string(FIND ${MGX_REVISION} "-" HASH_POS REVERSE)
        String(SUBSTRING ${MGX_REVISION} "0" ${HASH_POS} MGX_REVISION)
    Else()
      Message(SEND_ERROR "Git required when using Git revision name")
    EndIf() 
  EndIf()
EndIf()

Project(MorphoGraphX
  VERSION ${MGX_VERSION}
  LANGUAGES C CXX)

Message(STATUS "Configuring MorphoGraphX ${MGX_VERSION}-${MGX_REVISION} for ${CMAKE_SYSTEM}")

# in the future, switch to https://github.com/ruslo/sugar/wiki/Cross-platform-warning-suppression
If(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
  Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
  Option(MGX_DEBUG_STRICT_LINKING "Check for missing libraries while linking" ON) # TODO: enable independently of compiler
  String(COMPARE EQUAL "${CMAKE_BUILD_TYPE}" "Debug" IS_DEBUG)
  If(IS_DEBUG AND MGX_DEBUG_STRICT_LINKING)
    Set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
    Set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-undefined")
  EndIf()
EndIf()

# *** Library loading and setup
# Qt
Find_Package(Qt5 REQUIRED Widgets OpenGL Xml PrintSupport)
Set(CMAKE_AUTOMOC TRUE)
Set(CMAKE_AUTOUIC TRUE)
Set(CMAKE_AUTORCC TRUE)

Find_Package(Boost)

# OpenMP
Find_Package(OpenMP REQUIRED)
Set(USE_OPENMP TRUE)
If(USE_OPENMP AND OPENMP_FOUND)
  Set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
  Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
EndIf()
# GSL
Find_Package(GSL REQUIRED)
If(GSL_FOUND)
  Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_GSL_CXX_FLAGS}")
  Include_Directories("${GSL_INCLUDE_DIR}")
EndIf()

# hdf5
# Find_Package(HDF5 REQUIRED COMPONENTS C CXX)
# Include_Directories(${HDF5_INCLUDE_DIR})

# CImg
Set(CImg_DIR "" CACHE STRING "Directory containing CImg.h")
Include_Directories(${CImg_DIR})
Add_Definitions(-Dcimg_display=0)  # do not include X11 with CImg
Try_Compile(CIMG_COMPILES
  ${CMAKE_CURRENT_BINARY_DIR}
  SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/test_cimg.cpp"
  OUTPUT_VARIABLE TEST_CIMG_RESULT
  CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CImg_DIR}"
  )
If(NOT CIMG_COMPILES)
  Message(SEND_ERROR "CImg not found or too old (required version: 1.4.6). Output:\n${TEST_CIMG_RESULT}")
EndIf()

# CUDA and Thrust
set(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
Find_Package(CUDA) # Thrust searches CUDA paths when present
Find_Package(Thrust REQUIRED)

Include(MgxThrust) # pulls in utility functions useful (indispensable) for addons

# Set the backend for thrust
Set(THRUST_DEVICE_SYSTEM "CUDA" CACHE STRING "Thrust backend: CUDA, TBB, OMP or CPP")
Set(THRUST_BACKEND_${THRUST_DEVICE_SYSTEM} ON)
Set(CUDA_OPTIONS ${CUDA_OPTIONS} "-DTHRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_${THRUST_DEVICE_SYSTEM}")
Add_Definitions(-DTHRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_${THRUST_DEVICE_SYSTEM}) # Not having to include Config.hpp has less potential to cause errors with mismatched Thrust versions

# Add a cache  variable to add flags for MGX that we can default
Set(CUDA_NVCC_MGX_FLAGS "-D_FORCE_INLINES --expt-relaxed-constexpr --generate-code arch=compute_35,code=[compute_35,sm_35]"
       CACHE STRING "Extra flags for Cuda, typically architectures to compile for")

If("${THRUST_DEVICE_SYSTEM}" STREQUAL "CUDA")
  Find_Package(CUDA REQUIRED)
  Set(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} ${CUDA_NVCC_MGX_FLAGS})
  # Separable compilation causes "Invalid device function" unless you compile with exact arch, why?
  # Set(CUDA_SEPARABLE_COMPILATION ON)
  # NVCC in CUDA 8.0 breaks when -fPIC is passed in CMAKE_*_FLAGS_* due to empty arguments
  Set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC ")
Else()
  If("${THRUST_DEVICE_SYSTEM}" STREQUAL "OMP")
    SET(CUDA_OPTIONS ${CUDA_OPTIONS} ${OpenMP_CXX_FLAGS})
  ENDIF()
EndIf()

# Others
Find_Package(OpenGL REQUIRED)
Find_Package(TIFF REQUIRED)
Find_Package(PythonInterp)
Find_Package(PythonLibs)
Find_Package(TBB REQUIRED)

# *** Components
# libMGXViewer
Set(MGXViewer_SOURCE_PATH
  "${MorphoGraphX_SOURCE_DIR}/libMGXViewer")
Set(MGXViewer_LIBRARY_PATH
  "${MorphoGraphX_BINARY_DIR}/libMGXViewer/MGXViewer/libMGXViewer.so")
Add_Subdirectory("libMGXViewer/MGXViewer")

# Main MGX source
Set(MGX_SOURCE_PATH "${MorphoGraphX_SOURCE_DIR}/src")
Set(MGX_BINARY_PATH "${MorphoGraphX_BINARY_DIR}/src")
Add_Subdirectory("src")

# Plugins
Add_Subdirectory("ITK")
#Add_Subdirectory("VTK")
Find_Package(AddOns)
Message(STATUS "Building add-ons ${MGX_ADDONS}")

# *** Testing

If(UNIX)
# Builds plugin API tests
Add_Custom_Target(plugin_test_build
  COMMAND mkdir -p PluginAPITestBuild
  COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/AddOns/tests -BPluginAPITestBuild -DMGX_INCLUDE_DIR=${MGXPREFIX}/include -DMGX_LIB_DIR=${MGXPREFIX}/lib -DMGX_CACHE=${CMAKE_BINARY_DIR} -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
  COMMAND ${CMAKE_COMMAND} --build PluginAPITestBuild --target all
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

# Just tests plugin API
Add_Custom_Target(plugin_test
  COMMAND ${CMAKE_COMMAND} --build PluginAPITestBuild --target test
)

# Builds and executes tests of plugin API
Add_Custom_Target(plugin_check_installed
  COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target plugin_test
  DEPENDS plugin_test_build
)

# Installs plugin API and then tests it
Add_Custom_Target(plugin_check
  COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target install
  COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target plugin_check_installed
)

# Unit tests
Enable_Testing()

# MorphoGraphX base unit tests
Add_Subdirectory("tests")

# Build all unit tests. Do not move before all modules have been registered.
Get_Property(UNIT_TEST_TARGETS GLOBAL PROPERTY UNIT_TEST_TARGETS)
Add_Custom_Target(build_tests
  DEPENDS ${UNIT_TEST_TARGETS}
)

# Execute all unit tests
Add_Custom_Target(check
  COMMAND ${CMAKE_CTEST_COMMAND}
  DEPENDS build_tests
)

# Full test - first run unit tests, then test plugin ones
Add_Custom_Target(check_all
  COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR} --target plugin_check
  DEPENDS check
)
EndIf(UNIX)

# *** Documentation
Install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/MGXUserManual.pdf
  DESTINATION ${MGX_DOC_DIR} COMPONENT MGXCore)

# Doxygen documentation
Find_Package(Doxygen)
if(DOXYGEN_FOUND)
  Set(BUILD_DOXYGEN TRUE CACHE BOOL "Check to build the Doxygen API documentation")
  If(BUILD_DOXYGEN)
    Configure_File(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile)
    Add_Custom_Target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
    Set(CPACK_DEBIAN_ARCHIVE_TYPE "gnutar")
    Install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc/html
            DESTINATION ${MGX_DOC_DIR} COMPONENT MGXCore)
  EndIf(BUILD_DOXYGEN)
endif()

# *** Packaging
If(UNIX AND NOT APPLE AND NOT CYGWIN)  # i.e. Linux (??)
# Linux install scripts
  Find_Program(XDG_ICON_RESOURCE xdg-icon-resource DOC "XDG icon resource program")
  Find_Program(XDG_DESKTOP_MENU xdg-desktop-menu DOC "XDG desktop menu program")
  Find_Program(XDG_MIME xdg-mime DOC "XDG MIME program")
  Set(DESKTOP_FILES
    morphographx-MorphoGraphX.desktop
    morphographx-MorphoGraphX.xml
    src/images/Icon64.png
    src/images/Icon22.png
    src/images/FileIcon.png
    src/images/MeshIcon.png
    src/images/StackIcon.png)
  Install(FILES ${DESKTOP_FILES}
          DESTINATION ${MGX_RESOURCES_DIR} COMPONENT MGXCore)
ElseIf(WIN32)
  # Windows icons
  Set(ICON_FILES
    src/images/FileIcon.ico
    src/images/MeshIcon.ico
    src/images/StackIcon.ico )
  Install(FILES ${ICON_FILES}
          DESTINATION ${MGX_BINARY_DIR} COMPONENT MGXCore)
EndIf()

# Get OS information
If(UNIX)
  Exec_Program(lsb_release ARGS -si OUTPUT_VARIABLE MGX_OS)
  Exec_Program(lsb_release ARGS -sr OUTPUT_VARIABLE MGX_OS_RELEASE)
ElseIf(WIN32)
  Set(MGX_OS "Windows")
  Set(MGX_OS_RELEASE "")    ## how do we get this?
EndIf()
If("${CMAKE_SIZEOF_VOID_P}" EQUAL "4")
  Set(ARCH32 "32")
EndIf()

If(NOT CMAKE_BUILD_TYPE STREQUAL "Release")
  Message(WARNING "Building package but not in Release mode. Program will be larger and slower.")
EndIf()
# Package information
Set(CPACK_PACKAGE_NAME "MGX")
Set(CPACK_PACKAGE_VENDOR "MorphoGraphX")
Set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
  "MorphoGraphX: A tool for 4D image processing (and more!)")
Set(CPACK_PACKAGE_CONTACT "Richard S. Smith <Richard.Smith@jic.ac.uk>" CACHE STRING
  "Installation package contact")
Set(CPACK_PACKAGE_HOMEPAGE_URL "https://MorphoGraphX.org")
Set(CPACK_PACKAGE_VERSION_PATCH ${MGX_REVISION})
Set(CPACK_PACKAGE_VERSION ${MGX_VERSION}.${MGX_REVISION})

# Set package name
If("${THRUST_DEVICE_SYSTEM}" STREQUAL "CUDA")
  Set(MGX_BACKEND "Cuda${CUDA_VERSION_MAJOR}")
ElseIF("${THRUST_DEVICE_SYSTEM}" STREQUAL "OMP")
  Set(MGX_BACKEND "OMP")
ElseIf("${THRUST_DEVICE_SYSTEM}" STREQUAL "TBB")
  Set(MGX_BACKEND "TBB")
EndIf()
Set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}${ARCH32}-${MGX_VERSION}.${MGX_REVISION}-${MGX_OS}${MGX_OS_RELEASE}-${MGX_BACKEND}${MGX_ADDONS}")
Message(STATUS "MorphoGraphX package file is ${CPACK_PACKAGE_FILE_NAME}")

If(UNIX)
  Add_Subdirectory("deb")
ElseIf(WIN32)
  # No package-based install for Windows
  Set(CPACK_NSIS_COMPONENT_INSTALL OFF)

  # Run script to copy Windows runtime dependencies to bin directory
  CMake_Minimum_Required(VERSION 3.19)  # needed for CPACK_PRE_BUILD_SCRIPTS
  Set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_CURRENT_SOURCE_DIR}/scripts/windows_dependencies.cmake)

  Set(CPACK_PACKAGE_INSTALL_DIRECTORY "MorphoGraphX")

  Set(CPACK_GENERATOR NSIS)
  Set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL On)
  Set(CPACK_NSIS_PACKAGE_NAME "MorphoGraphX")
  Set(CPACK_NSIS_DISPLAY_NAME "MorphoGraphX")
  Set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/InstallLicense.txt")
  Set(CPACK_NSIS_MUI_FINISHPAGE_RUN "mgx.bat")

  # Set up file associations
  # nb. all NSIS entries have to be mega-ultra-escaped
  Set(NSIS_FILE_ASSOC ${CMAKE_CURRENT_SOURCE_DIR}/scripts/FileAssoc.nsh)
  String(REPLACE "/" "\\\\" NSIS_FILE_ASSOC_ESC "${NSIS_FILE_ASSOC}")
  Set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "!include \\\"${NSIS_FILE_ASSOC_ESC}\\\"")

  Set(MGX_BIN_DIR "\\\$INSTDIR\\\\bin\\\\")
  List(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS
    "!insertmacro APP_ASSOCIATE \\\"mgxv\\\" \\\"mgx.viewfile\\\" \\\"MorphoGraphX session file\\\" \
       \\\"${MGX_BIN_DIR}FileIcon.ico\\\" \\\"Open with MorphoGraphX\\\" \
       \\\"${MGX_BIN_DIR}mgx \\\$\\\\\\\"%1\\\$\\\\\\\"\\\"")
  List(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS
    "!insertmacro APP_ASSOCIATE \\\"mgxm\\\" \\\"mgx.meshfile\\\" \\\"MorphoGraphX mesh\\\" \
       \\\"${MGX_BIN_DIR}MeshIcon.ico\\\" \\\"Open with MorphoGraphX\\\" \
       \\\"${MGX_BIN_DIR}mgx \\\$\\\\\\\"%1\\\$\\\\\\\"\\\"")
  List(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS
    "!insertmacro APP_ASSOCIATE \\\"mgxs\\\" \\\"mgx.stackfile\\\" \\\"MorphoGraphX stack\\\" \
       \\\"${MGX_BIN_DIR}StackIcon.ico\\\" \\\"Open with MorphoGraphX\\\" \
       \\\"${MGX_BIN_DIR}mgx \\\$\\\\\\\"%1\\\$\\\\\\\"\\\"")
  String(REPLACE ";" "\n" CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}")

  List(APPEND CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
    "!insertmacro APP_UNASSOCIATE \\\"mgxv\\\" \\\"mgx.viewfile\\\"")
  List(APPEND CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
    "!insertmacro APP_UNASSOCIATE \\\"mgxm\\\" \\\"mgx.meshfile\\\"")
  List(APPEND CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
    "!insertmacro APP_UNASSOCIATE \\\"mgxs\\\" \\\"mgx.stackfile\\\"")
  String(REPLACE ";" "\n" CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}")

  Include(CPack)
EndIf()
