#[==============================================================================================[
#                         CMakeLists.txt for Metis 5.1 plus GKlib
# Author: A. Buccheri 2024.
#
# Notes for GKlib
# ------------------------------------------------------------------------
# GKlib's config.h is hard-coded. This is because its only purpose is to specify TLS_SPECIFIER,
# which can be done with macros in the header.
#
# NDEBUG2 is always enabled to avoid expensive assert calls that would other be present
# with debug builds.
#
# Ignored preprocessor options for windows support:
# * MSVC to set -DWIN32 -DMSC -D_CRT_SECURE_NO_DEPRECATE -DUSE_GKREGEX
# * MINGW to set -DUSE_GKREGEX
# * CYGWIN to set -DCYGWIN
#
# Superfluous preprocessor options:
# * HAVE_EXECINFO_H dropped in favour of using __has_include in source
# * HAVE_GETLINE is always true for POSIX, hence CheckFunctionExists is dropped
#
# Ignored architecture-specific preprocessor option:
# * NO_X86 to set -DNO_X86
#]==============================================================================================]

cmake_minimum_required(VERSION 3.20)
project(metis VERSION 5.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)

list(APPEND CMAKE_MESSAGE_CONTEXT Internal Metis)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type")
endif()

#[==============================================================================================[
#                                         Options                                                #
#]==============================================================================================]

# GKlib Options
option(METIS_ENABLE_GK_LINUX "GKlib LINUX architecture" ON)
option(METIS_ENABLE_GK_PCRE "Enable GKlib PCRE support" OFF)
option(METIS_ENABLE_GK_REGEX "Enable GKlib GKREGEX" OFF)
option(METIS_ENABLE_GK_RAND "Enable GKlib GKRAND random numbers" OFF)

# OpenMP-enabled Metis/GKlib has not been tested with Octopus
option(METIS_GKlib_OpenMP "Enable GKlib OpenMP support" OFF)
option(METIS_OpenMP "Enable Metis OpenMP support" OFF)

# Interface to control scope of compiler flags and options
add_library(gk_flags INTERFACE)
add_library(metis_flags INTERFACE)

# Options set by cmake configuration
if(METIS_ENABLE_GK_LINUX)
    target_compile_definitions(gk_flags INTERFACE LINUX)
    target_compile_definitions(gk_flags INTERFACE _FILE_OFFSET_BITS=64)
    # Required by POSIX, hence guaranteed to be present
    target_compile_definitions(gk_flags INTERFACE HAVE_GETLINE)
endif()
if(METIS_ENABLE_GK_PCRE)
    target_compile_definitions(gk_flags INTERFACE __WITHPCRE__)
endif()
if(METIS_ENABLE_GK_REGEX)
    target_compile_definitions(gk_flags INTERFACE USE_GKREGEX)
endif()
if(METIS_ENABLE_GK_RAND)
    target_compile_definitions(gk_flags INTERFACE USE_GKRAND)
endif()

# Compiler-specific flags
# Adapted from more generic code in src/CMakeLists.txt
if (METIS_NATIVE)
    if (CMAKE_C_COMPILER_ID MATCHES "Intel")
        target_compile_options(gk_flags INTERFACE $<$<COMPILE_LANGUAGE:${lang}>:-xHost>)
        target_compile_options(metis_flags INTERFACE $<$<COMPILE_LANGUAGE:${lang}>:-xHost>)
    else ()
        target_compile_options(gk_flags INTERFACE $<$<COMPILE_LANGUAGE:${lang}>:-march=native>)
        target_compile_options(metis_flags INTERFACE $<$<COMPILE_LANGUAGE:${lang}>:-march=native>)
    endif ()
endif ()

# Options determined by build type
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    target_compile_definitions(gk_flags INTERFACE DEBUG)
endif()

# Always disable very expensive asserts (assert2)
target_compile_definitions(gk_flags INTERFACE NDEBUG2)

# External dependencies
if (METIS_GKlib_OpenMP)
    target_compile_definitions(gk_flags INTERFACE __OPENMP__)
endif (METIS_GKlib_OpenMP)

if(METIS_OpenMP)
    target_compile_definitions(metis_flags INTERFACE ENABLE_OPENMP)
endif(METIS_OpenMP)

#[==============================================================================================[
#                                         GKlib                                                #
#]==============================================================================================]

# GKlib header file location
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/GKlib")

add_library(GKLib STATIC)

# Use same versioning as metis
set_target_properties(GKLib
        PROPERTIES
        VERSION "${PROJECT_VERSION}"
        LIBRARY_OUTPUT_NAME gklib
        ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
)

# Defines header location for targets that use `GKLib` i.e. metis
target_include_directories(GKLib PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/GKlib>
)

target_link_libraries(GKLib PRIVATE
        $<$<BOOL:${ENABLE_OMP}>:OpenMP::OpenMP_C>
        gk_flags
)

# Source code
add_subdirectory(GKlib)

#[==============================================================================================[
#                                         Metis                                                #
#]==============================================================================================]

# Metis header file locations
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/libmetis")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")

# Library
add_library(metis STATIC)

set_target_properties(metis
        PROPERTIES
        VERSION "${PROJECT_VERSION}"
        LIBRARY_OUTPUT_NAME metis
        ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
)

target_link_libraries(metis PRIVATE
        $<$<BOOL:${ENABLE_OMP}>:OpenMP::OpenMP_C>
        metis_flags
        GKLib)

# Defines header locations for targets that use `metis`, because Octopus's src/math/metis_f.c
# depends on metis.h
target_include_directories(metis PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/libmetis>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)

# Source code
add_subdirectory(libmetis)

# Alias target expected by Octopus
add_library(METIS::metis ALIAS metis)

#[==============================================================================================[
#                         Set IDXTYPEWIDTH to be consistent with metis.h                        #
#                                                                                               #
# From the original M4 macro, METIS must be compiled with REALTYPEWIDTH set to single precision #
# for Octopus. IDXTYPEWIDTH can be chosen to be either 32 or 64, as long as METIS_IDXTYPEWIDTH  #
# is set consistently.                                                                          #
#]==============================================================================================]
# NOTE this is set to be consistent with the hard-coded metis header
set(METIS_IDXTYPEWIDTH 64 CACHE INTERNAL "Width of IDXTYPE Octopus expects for METIS")

list(POP_BACK CMAKE_MESSAGE_CONTEXT)
