cmake_minimum_required(VERSION 3.20)
project(CPP_TEMPLATE VERSION 0.1.0 LANGUAGES C CXX)

# Set C++ standard
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Set C standard
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

# Export compile commands for IDE support (clangd, etc.)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Create symlink to compile_commands.json in project root for LSP
if(CMAKE_EXPORT_COMPILE_COMMANDS)
    add_custom_target(symlink_compile_commands ALL
        COMMAND ${CMAKE_COMMAND} -E create_symlink
            ${CMAKE_BINARY_DIR}/compile_commands.json
            ${CMAKE_SOURCE_DIR}/compile_commands.json
        COMMENT "Creating symlink to compile_commands.json in project root"
    )
endif()

# Set output directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

# Build type defaults
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type" FORCE)
endif()

# Options
option(ENABLE_SANITIZERS "Enable address and undefined behavior sanitizers" ON)
option(ENABLE_STATIC_ANALYSIS "Enable static analysis warnings" ON)
set(OUTPUT_NAME "" CACHE STRING "Name of the output executable (defaults to project name)")

# Gather source files (avoid GLOB_RECURSE - explicitly list files is better practice)
# But keeping it for template flexibility
file(GLOB_RECURSE PROJECT_SOURCES
    ${CMAKE_SOURCE_DIR}/source/*.cpp
    ${CMAKE_SOURCE_DIR}/source/*.c
)

file(GLOB_RECURSE LIBRARY_SOURCES
    ${CMAKE_SOURCE_DIR}/libraries/*.cpp
    ${CMAKE_SOURCE_DIR}/libraries/*.c
)

# Don't glob headers from include/ as sources (they should only be included)
file(GLOB_RECURSE PROJECT_HEADERS
    ${CMAKE_SOURCE_DIR}/include/*.hpp
    ${CMAKE_SOURCE_DIR}/include/*.h
    ${CMAKE_SOURCE_DIR}/libraries/*.hpp
    ${CMAKE_SOURCE_DIR}/libraries/*.h
    ${CMAKE_SOURCE_DIR}/source/*.hpp
    ${CMAKE_SOURCE_DIR}/source/*.h
)

# Combine all sources
set(ALL_SOURCES ${PROJECT_SOURCES} ${LIBRARY_SOURCES})

# Determine executable name
if(OUTPUT_NAME)
    set(EXECUTABLE_NAME ${OUTPUT_NAME})
else()
    set(EXECUTABLE_NAME ${PROJECT_NAME})
endif()

# Create executable
add_executable(${EXECUTABLE_NAME} ${ALL_SOURCES})

# Set target properties
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
    OUTPUT_NAME ${EXECUTABLE_NAME}
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)

# Include directories - use target-specific commands
target_include_directories(${EXECUTABLE_NAME} PRIVATE
    ${CMAKE_SOURCE_DIR}/include
    ${CMAKE_SOURCE_DIR}/libraries
)

# Compiler-specific flags
if(MSVC)
    target_compile_options(${EXECUTABLE_NAME} PRIVATE
        /W4                 # Warning level 4
        /permissive-        # Standards conformance
        /Zc:__cplusplus     # Correct __cplusplus macro
        /Zc:inline          # Remove unreferenced COMDAT
        /WX-                # Don't treat warnings as errors by default
    )
    
    if(ENABLE_STATIC_ANALYSIS)
        target_compile_options(${EXECUTABLE_NAME} PRIVATE /analyze)
    endif()
    
    # MSVC debug flags
    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
        target_compile_options(${EXECUTABLE_NAME} PRIVATE /Zi /Od)
    endif()
    
else() # GCC/Clang
    target_compile_options(${EXECUTABLE_NAME} PRIVATE
        -Wall
        -Wextra
        -Wpedantic
        -Wshadow
        -Wconversion
        -Wsign-conversion
        -Wuninitialized
        -Wunused
        -Werror=return-type
        -Wcast-align
        -Wformat=2
        -Wnull-dereference
    )
    
    # Additional warnings for static analysis
    if(ENABLE_STATIC_ANALYSIS)
        target_compile_options(${EXECUTABLE_NAME} PRIVATE
            -Wcast-qual
            -Wdouble-promotion
            -Wold-style-cast
        )
    endif()
    
    # Sanitizers (Debug builds)
    if(ENABLE_SANITIZERS AND CMAKE_BUILD_TYPE STREQUAL "Debug")
        # Check if sanitizers are available
        include(CheckCXXCompilerFlag)
        check_cxx_compiler_flag("-fsanitize=address" HAS_ASAN)
        check_cxx_compiler_flag("-fsanitize=undefined" HAS_UBSAN)
        
        if(HAS_ASAN AND HAS_UBSAN)
            target_compile_options(${EXECUTABLE_NAME} PRIVATE
                -fsanitize=address,undefined,leak
                -fno-omit-frame-pointer
                -g
            )
            target_link_options(${EXECUTABLE_NAME} PRIVATE
                -fsanitize=address,undefined,leak
            )
            message(STATUS "Sanitizers enabled: address, undefined, leak")
        else()
            message(WARNING "Sanitizers requested but not available - skipping")
        endif()
    endif()
    
    # Optimization flags for Release
    if(CMAKE_BUILD_TYPE STREQUAL "Release")
        target_compile_options(${EXECUTABLE_NAME} PRIVATE
            -O3
            -march=native
            -DNDEBUG
        )
    endif()
    
    # Debug flags
    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
        target_compile_options(${EXECUTABLE_NAME} PRIVATE
            -O0
            -g3
            -ggdb
        )
    endif()
endif()

# Print configuration summary
message(STATUS "=== Configuration Summary ===")
message(STATUS "Project: ${PROJECT_NAME} v${PROJECT_VERSION}")
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
message(STATUS "C++ Standard: ${CMAKE_CXX_STANDARD}")
message(STATUS "C Standard: ${CMAKE_C_STANDARD}")
message(STATUS "Executable Name: ${EXECUTABLE_NAME}")
message(STATUS "Sanitizers: ${ENABLE_SANITIZERS}")
message(STATUS "Static Analysis: ${ENABLE_STATIC_ANALYSIS}")
message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
message(STATUS "============================")

# Optionally, enable testing
# enable_testing()
# add_subdirectory(tests)
