PROJECT(libcscutils C )
CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
LIST(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")

INCLUDE(SetPolicy)
SET_POLICY(CMP0075 NEW)
SET_POLICY(CMP0063 NEW)
SET_POLICY(CMP0069 NEW)

INCLUDE(EnablePedanticC)

IF (WIN32)
    INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src)
ENDIF()

IF(NOT ${CMAKE_SOURCE_DIR} STREQUAL ${PROJECT_SOURCE_DIR})
    MESSAGE(STATUS "libcscutils in project build")
    SET(BUILD_SHARED_LIBS OFF)
    SET(DOCS OFF)
    SET(INPROJECT TRUE)
    SET(STANDALONE FALSE)
ELSE()
    SET(STANDALONE TRUE)
    SET(INPROJECT FALSE)
ENDIF()

IF (STANDALONE)
    MESSAGE(STATUS "Standalone build.")
    OPTION(DEBUG "Debug Mode" OFF)
    OPTION(BUILD_SHARED_LIBS "Build shared libs" OFF)
    OPTION(DOCS   "Build API documentation" ON)
    OPTION(FORTRAN "Build with Fortran support" ON)
    OPTION(TESTS "Build tests" ON)

    IF (FORTRAN)
        INCLUDE(CheckLanguage)
        CHECK_LANGUAGE(Fortran)
        IF(CMAKE_Fortran_COMPILER)
            ENABLE_LANGUAGE(Fortran)
        ELSE()
            MESSAGE(FATAL_ERROR "Fortran compiler not found. Please turn off the Fortran support using -DFORTRAN=OFF")
        ENDIF()
    ENDIF()
ENDIF()


set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_C_STANDARD_REQUIRED 11)




IF( NOT CMAKE_BUILD_TYPE )
    SET( CMAKE_BUILD_TYPE "Release" )
ENDIF()

IF(DEBUG STREQUAL ON)
    SET( CMAKE_BUILD_TYPE "Debug" )
ENDIF()

#Options for the IO
OPTION(CSC_ZLIB  "Build ZLIB IO Support" ON)
OPTION(CSC_BZIP2 "Build BZIP2 IO Support" ON)
OPTION(CSC_LIBLZMA "Build LZMA IO Support" ON)
OPTION(CSC_ZSTD    "Build ZSTD HDF5 IO Support" ON)

OPTION(CSC_MMAP "Build with MMAP Support" OFF)

# Setup destination
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
# FILE(COPY ${PROJECT_SOURCE_DIR}/include DESTINATION ${PROJECT_BINARY_DIR})
SET(CMAKE_Fortran_MODULE_DIRECTORY "${PROJECT_BINARY_DIR}/include/cscutils")

# Required Addons
INCLUDE(CheckIncludeFiles)
INCLUDE(CCompilerSettings)
INCLUDE(CXXCompilerSettings)
INCLUDE(FortranCompilerSettings)

# Compile Options
INCLUDE(${PROJECT_SOURCE_DIR}/CMakeModules/libm.cmake)
SET(LIBS ${LIBRARIES} ${LIBM_NAME})
SET(INCLUDE_DIR ${INCLUDE_DIR} ${PROJECT_BINARY_DIR}/include)

# Include Helpers
INCLUDE(CheckTypeSize)
INCLUDE(CheckFunctionExists)
INCLUDE(CheckIncludeFiles)
INCLUDE(CheckAttributeExists)
INCLUDE(CheckFeatureNeeded)
INCLUDE(ExternalProject)
INCLUDE(CheckSymbolExists)
INCLUDE(CheckCCompilerFlag)
INCLUDE(CheckBlasZdotcMKL)
INCLUDE(CMakeHelpers)

if(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
    file(STRINGS /proc/self/status _cmake_process_status)

    # Grab the PID of the parent process
    string(REGEX MATCH "PPid:[ \t]*([0-9]*)" _ ${_cmake_process_status})

    # Grab the absolute path of the parent process
    if ( CMAKE_VERSION VERSION_GREATER 3.13.99)
        file(READ_SYMLINK /proc/${CMAKE_MATCH_1}/exe _cmake_parent_process_path)
    else()
        execute_process(COMMAND readlink /proc/${CMAKE_MATCH_1}/exe OUTPUT_VARIABLE _cmake_parent_process_path)
    endif()

    # Compute CMake arguments only if CMake was not invoked by the native build
    # system, to avoid dropping user specified options on re-triggers.
    if(NOT ${_cmake_parent_process_path} STREQUAL ${CMAKE_MAKE_PROGRAM})
        execute_process(COMMAND bash -c "tr '\\0' ' ' < /proc/$PPID/cmdline"
                        OUTPUT_VARIABLE _cmake_args)

        string(STRIP "${_cmake_args}" _cmake_args)

        set(CMAKE_ARGS "${_cmake_args}"
            CACHE STRING "CMake command line args (set by end user)" FORCE)
    endif()
    message(STATUS "User Specified CMake Arguments: ${CMAKE_ARGS}")
else()
    SET(CMAKE_ARGS "")
endif()


#
# DECOMPOSE_FEATURES
#
IF(NOT CSCUTILS_FEATURES)
    SET(CSCUTILS_FEATURES "inifile;io;threading;hdf5;image;trace;lua")
ENDIF()

MESSAGE_COLOR("")
MESSAGE_COLOR(STATUS COLOR_BOLD_GREEN "LIBCSCUTILS Features")
FOREACH(FEAT ${CSCUTILS_FEATURES})
    MESSAGE(STATUS "Enable module: ${FEAT}")
    STRING(TOUPPER ${FEAT} _FEAT)
    SET(${_FEAT}_ENABLE TRUE)
ENDFOREACH()



#
# Check for Libraries
#
MACRO(CHECK_LIB PREFIX PACKAGE_NAME NO_DEFINE CONFIG_DEFINE)
    STRING(TOUPPER ${PACKAGE_NAME} _PACKAGE_NAME)
    IF(NOT ${PREFIX}_${CONFIG_DEFINE} STREQUAL OFF)
        MESSAGE_COLOR(STATUS COLOR_GREEN "Search for ${PACKAGE_NAME}")
        FIND_PACKAGE(${PACKAGE_NAME})
        IF(${_PACKAGE_NAME}_FOUND)
            SET(INCLUDE_DIR ${INCLUDE_DIR} ${${_PACKAGE_NAME}_INCLUDE_DIR})
            SET(LIBS ${${_PACKAGE_NAME}_LIBRARIES} ${LIBS})
            MESSAGE(STATUS "${PACKAGE_NAME} found (LIB = ${${_PACKAGE_NAME}_LIBRARIES}, INCLUDE = ${${_PACKAGE_NAME}_INCLUDE_DIR})")
            SET(CSC_HAVE_${CONFIG_DEFINE} TRUE)
        ELSE()
            MESSAGE(STATUS "${PACKAGE_NAME} not found. Deactivating.")
            ADD_DEFINITIONS(-D${NO_DEFINE})
            SET(CSC_HAVE_${CONFIG_DEFINE} FALSE)
        ENDIF()
    ELSE()
        MESSAGE_COLOR(STATUS COLOR_RED "${PACKAGE_NAME} deactivated")
        ADD_DEFINITIONS(-D${NO_DEFINE})
        SET(CSC_HAVE_${CONFIG_DEFINE} FALSE)
    ENDIF()
    UNSET(${_PACKAGE_NAME})
ENDMACRO()

MACRO(CHECK_FUNC FUNC)
    STRING(TOUPPER ${FUNC} _FUNC)
    CHECK_FUNCTION_EXISTS(${FUNC} CSC_HAVE_${_FUNC})
    UNSET(_FUNC)
ENDMACRO()

# Generic Stuff
MESSAGE_COLOR("")
MESSAGE_COLOR(STATUS COLOR_BOLD_BLUE "Check Generic Functionality")

#
#  Check for compiler features
#
CHECK_ATTRIBUTE_EXISTS(constructor CSC_HAVE_ATTR_CONSTRUCTOR)
CHECK_ATTRIBUTE_EXISTS(deprecated  CSC_HAVE_ATTR_DEPRECATED)


FIND_PACKAGE(Threads REQUIRED)
IF (NOT CMAKE_USE_WIN32_THREADS_INIT)
    SET(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT})
    SET(_X ${CMAKE_REQUIRED_LIBRARIES})
    SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_REQUIRED_LIBRARIES})
    IF(CMAKE_USE_PTHREADS_INIT)
        SET(CSC_HAVE_PTHREAD TRUE)
        CHECK_FEATURE_NEEDED(pthread_yield pthread.h CSC_HAVE_PTHREAD_YIELD PTHREAD_YIELD_FEATURE)
    ENDIF()
    SET(CMAKE_REQUIRED_LIBRARIES ${_X})
ENDIF()

FIND_PACKAGE(OpenMP)
    IF(OPENMP_FOUND)
        SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
        SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
        SET(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}" )
    ENDIF()

#
# Common FEATURES
#
CHECK_INCLUDE_FILES(execinfo.h HAVE_EXECINFO)
CHECK_FUNCTION_EXISTS(backtrace CSC_HAVE_BACKTRACE)
CHECK_FUNCTION_EXISTS(backtrace_symbols HAVE_BACKTRACE_SYMBOLS)
IF(HAVE_EXECINFO AND CSC_HAVE_BACKTRACE AND HAVE_BACKTRACE_SYMBOLS)
    MESSAGE_COLOR(STATUS COLOR_MAGENTA "Enable csc_show_backtrace.")
ENDIF()

CHECK_FEATURE_NEEDED(strnlen string.h CSC_HAVE_STRNLEN STRNLEN_FEATURE "_WITH_STRNLEN")
IF( CSC_HAVE_STRNLEN )
    ADD_DEFINITIONS("${STRNLEN_FEATURE}")
ENDIF()

IF(NOT WIN32)
    CHECK_FEATURE_NEEDED(strndup string.h CSC_HAVE_STRNDUP STRNDUP_FEATURE "_WITH_STRNDUP")
    IF( CSC_HAVE_STRNDUP )
        ADD_DEFINITIONS("${STRNDUP_FEATURE}")
    ENDIF()
ENDIF()


# check needed include files for csc_get_term_width
CHECK_INCLUDE_FILES(sys/ioctl.h CSC_HAVE_IOCTL)
CHECK_INCLUDE_FILES(unistd.h    CSC_HAVE_UNISTD)

IF(NOT WIN32)
    CHECK_FEATURE_NEEDED(getline stdio.h CSC_HAVE_GETLINE GETLINE_FEATURE "_WITH_GETLINE")
    IF( CSC_HAVE_GETLINE )
        ADD_DEFINITIONS("${GETLINE_FEATURE}")
    ENDIF()

    CHECK_FEATURE_NEEDED(getdelim stdio.h CSC_HAVE_GETDELIM GETDELIM_FEATURE "_WITH_GETDELIM")
    IF( CSC_HAVE_GETDELIM )
        ADD_DEFINITIONS("${GETDELIM_FEATURE}")
    ENDIF()
ENDIF()

IF(INIFILE_ENABLE)
    MESSAGE_COLOR("")
    MESSAGE_COLOR(STATUS COLOR_BOLD_BLUE "Features for CSC-INIFILE")

    IF (NOT WIN32)
        CHECK_FEATURE_NEEDED(realpath stdlib.h CSC_HAVE_REALPATH REALPATH_FEATURE)
        IF(CSC_HAVE_REALPATH)
            ADD_DEFINITIONS("${REALPATH_FEATURE}")
        ELSE()
            MESSAGE(FATAL_ERROR "realpath not found")
        ENDIF()
    ENDIF()
ENDIF()

IF(IO_ENABLE)
    MESSAGE_COLOR("")
    MESSAGE_COLOR(STATUS COLOR_BOLD_BLUE "Features for CSC-IO")

    # IO_ZLIB
    CHECK_FUNC(vsscanf)
    CHECK_FUNC(vsnprintf)

    CHECK_INCLUDE_FILES(sys/types.h CSC_HAVE_SYS_TYPES)
    IF(CSC_HAVE_SYS_TYPES)
        SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} sys/types.h)
    ENDIF()
    CHECK_INCLUDE_FILES(sys/stat.h  CSC_HAVE_SYS_STAT)
    IF(CSC_HAVE_SYS_STAT)
        SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} sys/stat.h)
    ENDIF()
    CHECK_INCLUDE_FILES(fcntl.h     CSC_HAVE_FCNTL)
    IF(CSC_HAVE_FCNTL)
        SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} fcntl.h)
    ENDIF()
    CHECK_INCLUDE_FILES(sys/mman.h     CSC_HAVE_SYS_MMAN)
    IF(CSC_HAVE_SYS_MMAN)
        SET(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} sys/mman.h)
    ENDIF()

    CHECK_FUNCTION_EXISTS(open CSC_HAVE_OPEN)
    CHECK_FUNCTION_EXISTS(mmap CSC_HAVE_MMAP)
    CHECK_FUNCTION_EXISTS(lseek CSC_HAVE_LSEEK)
    SET(CSC_IO_MMAP_IO FALSE)
    IF( CSC_IO_MMAP AND CSC_HAVE_LSEEK AND CSC_HAVE_MMAP AND CSC_HAVE_OPEN ) #CSC_IO_MMAP is an option which can be turned off
        SET(CSC_HAVE_MMAP_IO TRUE)
        MESSAGE_COLOR(STATUS COLOR_MAGENTA "Enable memory mapped IO")
    ELSE()
        SET(CSC_IO_MMAP_IO FALSE)
    ENDIF()
ENDIF()

IF(TRACE_ENABLE)
    MESSAGE_COLOR("")
    MESSAGE_COLOR(STATUS COLOR_BOLD_BLUE "Features for CSC-Tracewriter")

ENDIF()


# Common External Libraries
MESSAGE_COLOR("")
MESSAGE_COLOR(STATUS COLOR_BOLD_BLUE "External Libraries")

IF( IO_ENABLE OR HDF5_ENABLE )
    CHECK_LIB(CSC ZLIB NO_GZ ZLIB)
    CHECK_LIB(CSC BZip2 NO_BZ2 BZIP2)
    CHECK_LIB(CSC LibLZMA NO_XZ LZMA)
    CHECK_LIB(CSC ZSTD  NO_ZSTD ZSTD)
ENDIF()

IF (CMAKE_C_COMPILER_ID STREQUAL "TinyCC")
    MESSAGE(STATUS "TinyCC does not support complex numbers. Deactivating HDF5 support")
    SET( HDF5_ENABLE FALSE)
ENDIF()

IF(HDF5_ENABLE)
    MESSAGE_COLOR("")
    MESSAGE_COLOR(STATUS COLOR_BOLD_BLUE "Search HDF5")

    FIND_PACKAGE(HDF5 COMPONENTS C HL REQUIRED)
    SET(LIBS ${LIBS} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES})
    SET(INCLUDE_DIR ${INCLUDE_DIR} ${HDF5_INCLUDE_DIRS})
    ADD_DEFINITIONS(${HDF5_DEFINITIONS})
    SET(CSC_HAVE_HDF5 TRUE)

    SET(_CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS})  #list of macros to define (-DFOO=bar)
    SET(_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES})  #list of include directories
    SET(_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) #list of libraries to link
    SET(_CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES}) # list of extra headers to include

    SET (CMAKE_REQUIRED_DEFINITIONS ${HDF5_DEFINITIONS})
    SET (CMAKE_REQUIRED_INCLUDES ${HDF5_INCLUDE_DIRS})
    SET (CMAKE_REQUIRED_LIBRARIES ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES})
    SET (CMAKE_EXTRA_INCLUDE_FILES hdf5.h)

    CHECK_TYPE_SIZE(hid_t HDF5_HID_SIZE)
    MESSAGE(STATUS "Size of HDF5 hid_t: ${HDF5_HID_SIZE}")
    ADD_DEFINITIONS(-DHDF5_HID_SIZE=${HDF5_HID_SIZE})
    SET(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS})  #list of macros to define (-DFOO=bar)
    SET(CMAKE_REQUIRED_INCLUDES ${_CMAKE_REQUIRED_INCLUDES})  #list of include directories
    SET(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES}) #list of libraries to link
    SET(CMAKE_EXTRA_INCLUDE_FILES ${_CMAKE_EXTRA_INCLUDE_FILES}) # list of extra headers to include

ENDIF()

FIND_PACKAGE(CPUFREQ)
IF(CPUFREQ_FOUND)
    SET(LIBS ${CPUFREQ_LIBRARIES} ${LIBS})
    INCLUDE_DIRECTORIES(${CPUFREQ_INCLUDE_DIR})
    SET(CSC_HAVE_CPUFREQ TRUE)
ELSE()
    SET(CSC_HAVE_CPUFREQ FALSE)
    MESSAGE("CPUFREQ not found. Either cpufreq.h or libcpupower.so is missing.")
ENDIF()

#
# Add LUA
#
IF( LUA_ENABLE)
    ADD_SUBDIRECTORY(contrib/lua)
    SET(LIBS lua ${LIBS})
    SET(INCLUDE_DIR ${INCLUDE_DIR} ${PROJECT_BINARY_DIR}/contrib/lua/include)
    IF (NOT CMAKE_C_COMPILER_ID STREQUAL "TinyCC")
        SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs=liblua.a")
    ENDIF()
ENDIF()

CONFIGURE_FILE(config.h.in ${PROJECT_BINARY_DIR}/include/cscutils/cscutils_config.h @ONLY)
IF (NOT INPROJECT)
    INSTALL(FILES ${PROJECT_BINARY_DIR}/include/cscutils/cscutils_config.h DESTINATION include/cscutils)
ENDIF()

# Find additional Libraries
ADD_SUBDIRECTORY(src)
IF(NOT INPROJECT)
    ADD_SUBDIRECTORY(examples)
ENDIF()

IF(TESTS STREQUAL ON)
    ENABLE_TESTING()
    IF(HDF5_ENABLE)
        ADD_SUBDIRECTORY(tests/hdf5)
    ENDIF()


ENDIF()

IF(DOCS STREQUAL ON)
    MESSAGE_COLOR("")
    MESSAGE_COLOR(STATUS COLOR_BOLD_BLUE "Documentation")

    SET(DOXYFILE_OUTPUT_DIR "${PROJECT_BINARY_DIR}/doc")
    SET(DOXYFILE_LATEX_DIR  "${PROJECT_BINARY_DIR}/doc/latex")
    SET(LATEX_COMPILER "pdflatex")
    # set(DOXYFILE_EXTRA_SOURCES "${CMAKE_SOURCE_DIR}/documents/category.dox")
    SET(DOXYFILE_LATEX OFF)
    SET(DOXYFILE_GENERATE_LATEX YES)
    SET(USE_PDFLATEX YES)

    #set(PROJECT_VERSION ${MESS_VERSION})

    INCLUDE(UseDoxygen OPTIONAL)
    #  IF ( DOXYGEN_FOUND )
    #  file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/doc/html/misc)
    #  file(COPY ${CMAKE_SOURCE_DIR}/documents/doc_style.css DESTINATION  ${CMAKE_BINARY_DIR}/doc/html/misc/)
    #ENDIF (DOXYGEN_FOUND)
ENDIF()

IF (NOT INPROJECT)
    MESSAGE_COLOR("")
    MESSAGE_COLOR(STATUS COLOR_BOLD_BLUE "LIBCSCUTILS -- Final Configuration")
    SHOW_PROJECT_INFO()
ENDIF()
