cmake_minimum_required(VERSION 3.19)

option(INCLUDE_CUDA "GPU build in mode"    OFF)
option(USE_CXX  "CXX build in mode"    OFF)
option(BUILD_DOC    "Build documentation"    OFF)
option(SFX_CHECK_NAN    "Build documentation"    OFF)
option(USE_CONFIG_PARSER "Build config parser"    ON)

include(FetchContent)

project(INMCM_sfx)
enable_language(Fortran)

if(BUILD_DOC)
    find_package(Doxygen)
    if (DOXYGEN_FOUND)
        set(DOXYGEN_IN ../doxygen/config)    # doxygen config file

        # option ALL allows to build the docs together with the code
        add_custom_target( doc_doxygen ALL
            COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_IN}
            WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) # WORKING_DIRECTORY: Execute the command with the given current working directory. If it is a relative path it will be interpreted relative to the build tree directory corresponding to the current source directory.
    else()
    message("Doxygen need to be installed to generate the doxygen documentation")
    endif (DOXYGEN_FOUND)
endif(BUILD_DOC)

if (SFX_CHECK_NAN)
    add_definitions(-DSFX_CHECK_NAN)
endif ()

set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules)

if(USE_CONFIG_PARSER)
    add_subdirectory(config-parser/)
    add_definitions(-DUSE_CONFIG_PARSER)
endif(USE_CONFIG_PARSER)

if(INCLUDE_CUDA)
    if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
    message(FATAL_ERROR "
                    CMake will not pass any architecture flags to the compiler 
                    because the CUDA architecture is not set. You should specify 
                    an architecture: set -DCMAKE_CUDA_ARCHITECTURES=<N>.")
    endif(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)

    enable_language(CUDA)
    include_directories(${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
    add_definitions(-DINCLUDE_CUDA)
    set(USE_CXX ON)
endif(INCLUDE_CUDA)

if(USE_CXX)
    enable_language(C)
    enable_language(CXX)
    set(CMAKE_CXX_STANDARD 11)
    add_definitions(-DINCLUDE_CXX)
endif(USE_CXX)

set(SOURCES_F 
    srcF/sfx_data.f90
    srcF/sfx_common.f90
    srcF/sfx_io.f90
    srcF/sfx_config.f90
    srcF/sfx_esm.f90 
    srcF/sfx_esm_param.f90
    srcF/sfx_log.f90
    srcF/sfx_log_param.f90
    srcF/sfx_run.f90
    srcF/sfx_phys_const.f90
    srcF/sfx_surface.f90
    srcF/sfx_thermal_roughness.f90
    srcF/sfx_most.f90
    srcF/sfx_most_param.f90
    srcF/sfx_sheba.f90
    srcF/sfx_sheba_param.f90
    srcF/sfx_fc_wrapper.F90
    srcF/sfx_api_inmcm.f90
    srcF/sfx_api_term.f90
)
set(MAIN_F
        srcF/sfx_main.f90)

set(HEADERS_F
    includeF/sfx_def.fi
)

if(USE_CXX)
    set(SOURCES_C 
        srcC/c_sfx_model_compute_flux.c
    )

    set(HEADERS_C
        includeC/sfx_data.h
    )
    list(APPEND HEADERS_DIRS includeC/)

    set(SOURCES_CXX 
            srcCXX/model_base.cpp
            srcCXX/sfx_esm.cpp
            srcCXX/sfx_sheba.cpp
            srcCXX/cxx_sfx_model_compute_flux.cpp
    )
    set(HEADERS_CXX 
            includeCU/sfx_surface.cuh
            includeCU/sfx_math.cuh
            includeCU/sfx_model_compute_subfunc.cuh

            includeCXX/model_base.h
            includeCXX/sfx_esm.h
            includeCXX/sfx_sheba.h
            includeCXX/cxx_sfx_model_compute_flux.h
        )
    list(APPEND HEADERS_DIRS includeCU/)
    list(APPEND HEADERS_DIRS includeCXX/)
endif(USE_CXX)

if(INCLUDE_CUDA)
    set(SOURCES_CU 
        srcCU/sfx_esm.cu
        srcCU/sfx_sheba.cu
    )
    set(HEADERS_CU
    
    )
endif(INCLUDE_CUDA)

if(USE_CXX OR INCLUDE_CUDA)
    set(MEMPROC_SOURCES_CXX
        srcCXX/sfx_memory_processing.cpp
    )
    set(MEMPROC_HEADERS_CXX
        includeCXX/sfx_memory_processing.h
        includeCXX/sfx_template_parameters.h
    )

    if(INCLUDE_CUDA)
        set(MEMPROC_SOURCES_CU
            srcCU/sfx_memory_processing.cu
        )
        set(MEMPROC_HEADERS_CU
            includeCU/sfx_memory_processing.cuh
        )
    endif(INCLUDE_CUDA)
endif(USE_CXX OR INCLUDE_CUDA)

set(SOURCES 
    ${MEMPROC_HEADERS_CU} ${MEMPROC_SOURCES_CU} ${MEMPROC_HEADERS_CXX} ${MEMPROC_SOURCES_CXX} 
    ${HEADERS_CU} ${SOURCES_CU} ${HEADERS_C} ${HEADERS_CXX} ${SOURCES_CXX} ${SOURCES_C} 
    ${HEADERS_F} ${SOURCES_F} ${MAIN_F})
set(SOURCES_LIB 
    ${MEMPROC_HEADERS_CU} ${MEMPROC_SOURCES_CU} ${MEMPROC_HEADERS_CXX} ${MEMPROC_SOURCES_CXX} 
    ${HEADERS_CU} ${SOURCES_CU} ${HEADERS_CXX} 
    ${SOURCES_CXX} ${SOURCES_C} ${HEADERS_F} ${SOURCES_F})

set(CMAKE_Fortran_FLAGS " -cpp ")

add_executable(sfx ${SOURCES})
add_library(sfx_lib ${SOURCES_LIB})
set_property(TARGET sfx PROPERTY LINKER_LANGUAGE Fortran)
target_include_directories(sfx PUBLIC ${CMAKE_BINARY_DIR}/modules/)
target_include_directories(sfx PUBLIC ${HEADERS_DIRS})
target_include_directories(sfx_lib PUBLIC ${HEADERS_DIRS})

if(USE_CONFIG_PARSER)
    target_link_libraries(sfx config_parser_F config_parser_CXX)
    target_link_libraries(sfx_lib config_parser_F config_parser_CXX)
endif(USE_CONFIG_PARSER)

set_property(TARGET sfx_lib PROPERTY LINKER_LANGUAGE Fortran)
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
    SET(CMAKE_C_ARCHIVE_CREATE   "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
    SET(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
    SET(CMAKE_C_ARCHIVE_FINISH   "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
    SET(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
endif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
# set(CMAKE_Fortran_ARCHIVE_FINISH  "$<CMAKE_RANLIB> -no_warning_for_no_symbols -c $<TARGET>")
target_include_directories(sfx_lib PUBLIC ${CMAKE_BINARY_DIR}/modules/)

if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
    target_compile_options(sfx
        PUBLIC $<$<COMPILE_LANGUAGE:C>: -g >)
	target_compile_options(sfx
        PUBLIC $<$<COMPILE_LANGUAGE:CXX>: -g >)
    target_compile_options(sfx
        PUBLIC $<$<COMPILE_LANGUAGE:CUDA>: -g >)
    target_compile_options(sfx
        PUBLIC $<$<COMPILE_LANGUAGE:Fortran>: -g -fbacktrace -ffpe-trap=zero,overflow,underflow >)
endif()


# copy data & configs on post build
add_custom_command(
        TARGET sfx POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_directory
        ${CMAKE_SOURCE_DIR}/data
        ${CMAKE_CURRENT_BINARY_DIR}/data)
add_custom_command(
        TARGET sfx POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_directory
        ${CMAKE_SOURCE_DIR}/config-ex
        ${CMAKE_CURRENT_BINARY_DIR}/config-ex)