cmake_minimum_required(VERSION 3.28)
project(HyperMatrix VERSION 0.0.0 LANGUAGES CXX)

include(CheckIPOSupported)
include(FetchContent)

set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
set(EXPORT_COMPILE_COMMANDS TRUE)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(VCPKG_CRT_LINKAGE static)
set(VCPKG_LIBRARY_LINKAGE static)

find_package(GTest REQUIRED)
find_package(Boost REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(Protobuf REQUIRED CONFIG)
find_package(OpenSSL REQUIRED)
find_package(libpqxx REQUIRED)

add_definitions(-D_WIN32_WINNT=0x0601)

check_ipo_supported(RESULT IPO_SUPPORTED OUTPUT ERR)

string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)

# TODO: Enable UBSan and fix all errors
if ("${CMAKE_BUILD_TYPE}" STREQUAL "debug")
    message(STATUS "Enabled debug flags")
    if (MSVC)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /Od /Zi /DEBUG")
    else()
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -g -O0")
        #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -g -O0 -fsanitize=address -fsanitize-recover=address")
        #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address -fsanitize-recover=address")
    endif()
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "release")
    message(STATUS "Enabled release flags")
    add_compile_definitions(PRODUCTION=1)

    if (MSVC)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2")
    else()
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
    endif()

    if (IPO_SUPPORTED)
        set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
    endif()
endif()

message(STATUS "C++ flags: ${CMAKE_CXX_FLAGS}")

if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    add_compile_options(
        -Wno-unreachable-code
        -Wno-macro-redefined
        -Wno-unused-parameter
    )
elseif (MSVC)
    add_compile_options(
        /wd4702
        /wd4005
        /wd4100
    )
endif()

# Clang-format target
find_program(CLANG_FORMAT_PROGRAM clang-format)
if(CLANG_FORMAT_PROGRAM)
    file(GLOB_RECURSE ALL_SOURCE_FILES
        src/*.cpp src/*.hpp
        tests/*.cpp tests/*.hpp
    )
    add_custom_target(format
        COMMAND ${CLANG_FORMAT_PROGRAM} -i -style=file ${ALL_SOURCE_FILES}
        COMMENT "Formatting source files with clang-format"
        VERBATIM
    )
    add_custom_target(format-check
        COMMAND ${CLANG_FORMAT_PROGRAM} --dry-run --Werror -style=file ${ALL_SOURCE_FILES}
        COMMENT "Checking formatting with clang-format"
        VERBATIM
    )
else()
    message(WARNING "clang-format not found, format targets disabled")
endif()

# Clang-tidy targets
find_program(CLANG_TIDY_PROGRAM clang-tidy)
if(CLANG_TIDY_PROGRAM)
    add_custom_target(tidy
        COMMAND ${CMAKE_COMMAND} -E env
            ${CLANG_TIDY_PROGRAM}
            -p ${CMAKE_BINARY_DIR}
            -j 0
            --extra-arg=-Wno-unknown-warning-option
            --extra-arg=-Wno-unknown-pragmas
            ${ALL_SOURCE_FILES}
        COMMENT "Running clang-tidy on all source files"
        VERBATIM
    )
    add_custom_target(tidy-fix
        COMMAND ${CMAKE_COMMAND} -E env
            ${CLANG_TIDY_PROGRAM}
            -p ${CMAKE_BINARY_DIR}
            -j 0
            --fix
            --extra-arg=-Wno-unknown-warning-option
            --extra-arg=-Wno-unknown-pragmas
            ${ALL_SOURCE_FILES}
        COMMENT "Running clang-tidy with auto-fix on all source files"
        VERBATIM
    )
    add_custom_target(tidy-check
        COMMAND ${CMAKE_COMMAND} -E env
            ${CLANG_TIDY_PROGRAM}
            -p ${CMAKE_BINARY_DIR}
            -j 0
            --warnings-as-errors='*'
            --extra-arg=-Wno-unknown-warning-option
            --extra-arg=-Wno-unknown-pragmas
            ${ALL_SOURCE_FILES}
        COMMENT "Running clang-tidy check (fails on warnings)"
        VERBATIM
    )
else()
    message(WARNING "clang-tidy not found, tidy targets disabled")
endif()

add_subdirectory(src)
add_subdirectory(tests)
