cmake_minimum_required(VERSION 3.28)
project(DT C CXX ASM)
set(CMAKE_PROJECT_VERSION_MAJOR 0)
set(CMAKE_PROJECT_VERSION_MINOR 6)
set(CMAKE_PROJECT_VERSION_PATCH 0)
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
    message(FATAL_ERROR "In-source builds are not allowed")
endif()
if(NOT CMAKE_BUILD_TYPE)
   set(CMAKE_BUILD_TYPE Release
       CACHE STRING "Choose the type of build: Release Debug Coverage Profile Valgrind."
       FORCE)
endif()
set(COVERAGE_ARGS "" CACHE STRING "select specific unit tests to run for test coverage evaluations")
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_EXTENSIONS NO)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wthread-safety")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Wno-changes-meaning")
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
if (MSVC)
    add_compile_options(/bigobj)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_RELEASE} /EHa")
    enable_language(ASM_MASM)
endif()
if (MINGW)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
    list(APPEND CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES /mingw64/include)
endif()
if(UNIX AND NOT APPLE)
    link_libraries("atomic")
endif()
add_subdirectory(3rdparty)
add_subdirectory(lib)
target_link_libraries(lib PUBLIC 3rdparty 3rdparty-cpp)

add_executable(dt src/dt.cpp)
target_link_libraries(dt PRIVATE lib 3rdparty 3rdparty-cpp)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries)
install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION bin)
install(TARGETS dt RUNTIME DESTINATION bin)
install(DIRECTORY etc/ DESTINATION etc)
if (WIN32)
    if (MINGW)
        add_custom_command(TARGET dt POST_BUILD COMMAND ldd ${CMAKE_BINARY_DIR}/dt.exe | awk '{ print $$3 }' | grep -vi system > ${CMAKE_BINARY_DIR}/dlls.txt)
        add_custom_command(TARGET dt POST_BUILD COMMAND cygpath -w -f ${CMAKE_BINARY_DIR}/dlls.txt > ${CMAKE_BINARY_DIR}/dlls-win-path.txt)
        install(CODE [[
            file(READ $<TARGET_FILE_DIR:dt>/dlls-win-path.txt DLLS)
            string(REPLACE "\n" ";" DLLS_LIST ${DLLS})
            file(COPY ${DLLS_LIST} DESTINATION $<INSTALL_PREFIX>/bin)
        ]])
    else()
        file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} BIN_DIR_WIN)
        add_custom_command(TARGET dt POST_BUILD COMMAND dir /b ${BIN_DIR_WIN}\\*.dll > ${BIN_DIR_WIN}\\dlls.txt COMMAND_EXPAND_LISTS)
        install(CODE [[
            file(READ $<TARGET_FILE_DIR:dt>/dlls.txt DLLS)
            string(REPLACE "\n" ";" DLLS_LIST ${DLLS})
            foreach(lib ${DLLS_LIST})
                LIST(APPEND DLLS_WITH_PATH "$<TARGET_FILE_DIR:dt>/${lib}")
            endforeach()
            file(COPY ${DLLS_WITH_PATH} DESTINATION $<INSTALL_PREFIX>/bin)
        ]])
    endif()
endif()

file(GLOB ITEST_SRCS test/*.cpp)
foreach(ITEST_SRC IN LISTS ITEST_SRCS)
    cmake_path(GET ITEST_SRC STEM ITEST)
    add_executable(${ITEST} ${ITEST_SRC})
    target_link_libraries(${ITEST} PRIVATE lib 3rdparty 3rdparty-cpp)
endforeach()

file(GLOB_RECURSE TEST_SRCS lib/dt/*.test.cpp)
add_executable(run-test src/run-test.cpp ${TEST_SRCS})
target_link_libraries(run-test PRIVATE lib 3rdparty 3rdparty-cpp)

file(GLOB_RECURSE BENCH_SRCS lib/dt/*.bench.cpp)
add_executable(run-bench src/run-test.cpp ${BENCH_SRCS})
target_link_libraries(run-bench PRIVATE lib 3rdparty 3rdparty-cpp)

message("copying configs")
file(COPY etc DESTINATION .)
if (EXISTS ${CMAKE_SOURCE_DIR}/publisher)
    file(COPY publisher DESTINATION .)
endif()
message("copying test data")
file(COPY data DESTINATION .)

if(CMAKE_BUILD_TYPE STREQUAL Valgrind)
    if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3")
        set(SANITIZER "address,fuzzer")
        if(SANITIZER)
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${SANITIZER}")
            set(MAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${SANITIZER}")
        endif()
    else()
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O1")
    endif()
    file(GLOB_RECURSE FUZZ_SRCS lib/dt/*.fuzz.cpp)
    foreach(FUZZ_SRC IN LISTS FUZZ_SRCS)
        cmake_path(GET FUZZ_SRC STEM FUZZ_NAME)
        add_executable(fuzz-${FUZZ_NAME} ${FUZZ_SRC})
        target_link_libraries(fuzz-${FUZZ_NAME} PRIVATE lib 3rdparty 3rdparty-cpp)
    endforeach()
elseif(CMAKE_BUILD_TYPE STREQUAL Profile)
    target_compile_options(lib PRIVATE -pg -g -O1)
    target_compile_options(dt PRIVATE -pg -g -O1)
    target_link_options(dt PRIVATE -pg -g)
elseif(CMAKE_BUILD_TYPE STREQUAL Coverage)
    if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
        target_compile_options(lib PRIVATE -fprofile-instr-generate -fcoverage-mapping -g -O3)
        target_compile_options(run-test PRIVATE -fprofile-instr-generate -fcoverage-mapping -g -O3)
        target_link_options(run-test PRIVATE -fprofile-instr-generate -fcoverage-mapping -g)
        add_custom_target(coverage
                COMMENT "Running test coverage ..."
                COMMAND $<TARGET_FILE:run-test> ${COVERAGE_ARGS}
                COMMAND echo "Indexing raw profdata"
                COMMAND llvm-profdata merge -sparse default.profraw -o run-test.profdata
                COMMAND echo "Generating coverage report"
                COMMAND llvm-cov report $<TARGET_FILE:run-test> -instr-profile=run-test.profdata
                DEPENDS run-test
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        )
    else()
        target_compile_options(lib PRIVATE -g --coverage)
        target_compile_options(run-test PRIVATE -g --coverage)
        target_link_options(run-test PRIVATE -g --coverage -lgcov)
        add_custom_command(TARGET run-test PRE_BUILD COMMAND
                find ${CMAKE_BINARY_DIR} -type f -name '*.gcda' -exec rm {} +)
        find_program(GCOVR_PATH gcovr)
        if (GCOVR_PATH)
            message("adding target coverage using ${GCOVR_PATH}")
            add_custom_target(coverage
                COMMENT "Running test coverage ..."
                COMMAND $<TARGET_FILE:run-test> ${COVERAGE_ARGS}
                COMMAND mkdir -p ${CMAKE_BINARY_DIR}/coverage
                COMMAND ${GCOVR_PATH} -j 8 -r ${CMAKE_SOURCE_DIR} --gcov-ignore-parse-errors=negative_hits.warn --merge-mode-functions=separate --html-details ${CMAKE_BINARY_DIR}/coverage/coverage.html .
                WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
            )
        endif()
    endif()
endif()

if (EXISTS ${CMAKE_SOURCE_DIR}/ui)
    add_subdirectory(ui)
    set(CPACK_THREADS 0)
    set(CPACK_PACKAGE_NAME "DT Explorer")
    set(CPACK_PACKAGE_VENDOR "R2 Rationality")
    set(CPACK_PACKAGE_FILE_NAME "dt-explorer-${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}")
    set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
    if (WIN32)
        set(APP_NAME "DT Explorer")
        set(CPACK_GENERATOR "NSIS")
        set(CPACK_NSIS_COMPRESSOR "/SOLID zlib")
        set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
        set(CPACK_NSIS_MUI_HEADERIMAGE "${CMAKE_SOURCE_DIR}\\\\ui\\\\etc\\\\nsis-header.bmp")
        set(CPACK_NSIS_MUI_UNHEADERIMAGE "${CMAKE_SOURCE_DIR}\\\\ui\\\\etc\\\\nsis-header.bmp")
        set(CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP "${CMAKE_SOURCE_DIR}\\\\ui\\\\etc\\\\nsis-welcome.bmp")
        set(CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP "${CMAKE_SOURCE_DIR}\\\\ui\\\\etc\\\\nsis-welcome.bmp")
        set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}\\\\ui\\\\etc\\\\logo.ico")
        set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}\\\\ui\\\\etc\\\\logo.ico")
        set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\dt-explorer.exe")
        set(CPACK_NSIS_MUI_FINISHPAGE_RUN "dt-explorer")
        set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/sierkov/daedalus-turbo")
        set(CPACK_NSIS_BRANDING_TEXT " ")
        set(CPACK_PACKAGE_EXECUTABLES "dt-explorer" ${APP_NAME})
        set(CPACK_CREATE_DESKTOP_LINKS "dt-explorer")
    endif()
    set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
    include(CPack)
endif()