cmake_minimum_required(VERSION 3.20)
project(dfin
    VERSION 1.0.0
    DESCRIPTION "High-performance duplicate finder with I/O Ring/io_uring support"
    LANGUAGES C
)

# ---------------------------------------------------------------------------
# Force compiler search order
# ---------------------------------------------------------------------------

# On Windows, prefer clang-cl, then GCC, then Clang
if(WIN32)
    # Try to force compiler order if not already set
    if(NOT CMAKE_C_COMPILER)
        # Search in preferred order
        find_program(CLANG_CL_COMPILER NAMES clang-cl)
        find_program(GCC_COMPILER NAMES gcc)
        find_program(CLANG_COMPILER NAMES clang)
        
        if(CLANG_CL_COMPILER)
            message(STATUS "Found clang-cl as preferred compiler: ${CLANG_CL_COMPILER}")
            set(CMAKE_C_COMPILER "${CLANG_CL_COMPILER}" CACHE STRING "" FORCE)
        elseif(GCC_COMPILER)
            message(STATUS "Found GCC as fallback compiler: ${GCC_COMPILER}")
            set(CMAKE_C_COMPILER "${GCC_COMPILER}" CACHE STRING "" FORCE)
        elseif(CLANG_COMPILER)
            message(STATUS "Found Clang as last-resort compiler: ${CLANG_COMPILER}")
            set(CMAKE_C_COMPILER "${CLANG_COMPILER}" CACHE STRING "" FORCE)
        endif()
    endif()
else()
    # On Linux, prefer GCC, then Clang
    if(NOT CMAKE_C_COMPILER)
        find_program(GCC_COMPILER NAMES gcc)
        find_program(CLANG_COMPILER NAMES clang)
        
        if(GCC_COMPILER)
            message(STATUS "Found GCC as preferred compiler: ${GCC_COMPILER}")
            set(CMAKE_C_COMPILER "${GCC_COMPILER}" CACHE STRING "" FORCE)
        elseif(CLANG_COMPILER)
            message(STATUS "Found Clang as fallback compiler: ${CLANG_COMPILER}")
            set(CMAKE_C_COMPILER "${CLANG_COMPILER}" CACHE STRING "" FORCE)
        endif()
    endif()
endif()

# Now project() will use the compiler we found
# However, since we needed project() first to get C support,
# we check what we actually got
message(STATUS "Using compiler: ${CMAKE_C_COMPILER} (${CMAKE_C_COMPILER_ID})")

# ---------------------------------------------------------------------------
# Platform and Compiler Detection
# ---------------------------------------------------------------------------

if(WIN32)
    set(PLATFORM_WINDOWS TRUE)
    set(PLATFORM_NAME "Windows")
else()
    set(PLATFORM_LINUX TRUE)
    set(PLATFORM_NAME "Linux")
endif()

# Compiler type
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
    # Check if it's clang-cl
    if(CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
        set(COMPILER_CLANG_CL TRUE)
        message(STATUS "Detected clang-cl (MSVC-compatible frontend)")
    else()
        set(COMPILER_CLANG_GNU TRUE)
        message(STATUS "Detected Clang (GNU-compatible frontend)")
    endif()
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
    set(COMPILER_GCC TRUE)
    message(STATUS "Detected GCC")
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
    # We don't want MSVC, but if it's found, warn user
    message(FATAL_ERROR 
        "MSVC (cl.exe) detected!\n"
        "This project requires clang-cl, GCC, or Clang.\n"
        "Please install one of these compilers or specify manually:\n"
        "  cmake .. -DCMAKE_C_COMPILER=clang-cl\n"
        "  cmake .. -DCMAKE_C_COMPILER=gcc\n"
        "  cmake .. -DCMAKE_C_COMPILER=clang\n"
    )
endif()

# ---------------------------------------------------------------------------
# Build System Selection
# ---------------------------------------------------------------------------

if(NOT CMAKE_GENERATOR OR CMAKE_GENERATOR STREQUAL "")
    find_program(NINJA_EXECUTABLE NAMES ninja)
    if(NINJA_EXECUTABLE)
        message(STATUS "Using Ninja build system")
        set(CMAKE_GENERATOR "Ninja")
    else()
        message(STATUS "Ninja not found, using default generator: ${CMAKE_GENERATOR}")
    endif()
endif()

# ---------------------------------------------------------------------------
# Source Files
# ---------------------------------------------------------------------------

set(SOURCES
    duplicate_finder.c
    xxhash.c
    xxh_x86dispatch.c
)

# Headers for dependency tracking and IDE
set(HEADERS
    arena.h
    base.h
    xxhash.h
    mt_mpmc.h
)

# ---------------------------------------------------------------------------
# Create Executable
# ---------------------------------------------------------------------------

add_executable(${PROJECT_NAME}
    ${SOURCES}
    ${HEADERS}
)

# Include directories
target_include_directories(${PROJECT_NAME} PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}
)

# ---------------------------------------------------------------------------
# Compiler Flags - Exact match to your commands
# ---------------------------------------------------------------------------

if(PLATFORM_WINDOWS)
    if(COMPILER_CLANG_CL)
        # === clang-cl flags ===
        # Release: /O2
        target_compile_options(${PROJECT_NAME} PRIVATE
            $<$<CONFIG:Release>:/O2>
        )
        # Debug: /Zi /Od
        target_compile_options(${PROJECT_NAME} PRIVATE
            $<$<CONFIG:Debug>:/Zi /Od>
        )
        # Common warnings
        target_compile_options(${PROJECT_NAME} PRIVATE /W4)
        
    elseif(COMPILER_GCC)
        # === GCC flags (Windows/MinGW) ===
        # Release: -O3
        target_compile_options(${PROJECT_NAME} PRIVATE
            $<$<CONFIG:Release>:-O3>
        )
        # Debug: -g -O0
        target_compile_options(${PROJECT_NAME} PRIVATE
            $<$<CONFIG:Debug>:-g -O0>
        )
        # Common warnings
        target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra)
        
    elseif(COMPILER_CLANG_GNU)
        # === Clang flags (Windows, GNU frontend) ===
        # Release: -O3
        target_compile_options(${PROJECT_NAME} PRIVATE
            $<$<CONFIG:Release>:-O3>
        )
        # Debug: -g -O0
        target_compile_options(${PROJECT_NAME} PRIVATE
            $<$<CONFIG:Debug>:-g -O0>
        )
        # Common warnings
        target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra)
    endif()
    
    # Windows-specific libraries
    target_link_libraries(${PROJECT_NAME} PRIVATE
        kernel32
    )
    
    # Windows-specific defines
    target_compile_definitions(${PROJECT_NAME} PRIVATE
        WIN32_LEAN_AND_MEAN
        _WIN32_WINNT=0x0A00  # Windows 10+
    )
    
    # Set output name with .exe
    set_target_properties(${PROJECT_NAME} PROPERTIES
        SUFFIX ".exe"
    )
    
elseif(PLATFORM_LINUX)
    # === Linux GCC/Clang flags ===
    if(COMPILER_GCC OR COMPILER_CLANG_GNU)
        # Release: -O3
        target_compile_options(${PROJECT_NAME} PRIVATE
            $<$<CONFIG:Release>:-O3>
        )
        # Debug: -g -O0
        target_compile_options(${PROJECT_NAME} PRIVATE
            $<$<CONFIG:Debug>:-g -O0>
        )
        # Common warnings
        target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra)
    endif()
    
    # Linux-specific libraries
    find_package(Threads REQUIRED)
    find_library(LIBURING_LIBRARY NAMES uring)
    
    if(LIBURING_LIBRARY)
        message(STATUS "Found liburing: ${LIBURING_LIBRARY}")
        target_link_libraries(${PROJECT_NAME} PRIVATE
            Threads::Threads
            ${LIBURING_LIBRARY}
        )
    else()
        message(FATAL_ERROR "liburing not found! Install liburing-dev or equivalent")
    endif()
    
    # Linux-specific defines
    target_compile_definitions(${PROJECT_NAME} PRIVATE
        _GNU_SOURCE
    )
endif()

# ---------------------------------------------------------------------------
# C Standard
# ---------------------------------------------------------------------------

set_target_properties(${PROJECT_NAME} PROPERTIES
    C_STANDARD 11
    C_STANDARD_REQUIRED ON
    C_EXTENSIONS OFF
)

# ---------------------------------------------------------------------------
# Build Configurations
# ---------------------------------------------------------------------------

# Set default build type if not specified (matching your Release command)
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING 
        "Choose the type of build: Release or Debug" FORCE)
    message(STATUS "No build type specified, defaulting to Release")
endif()

# ---------------------------------------------------------------------------
# IDE Support
# ---------------------------------------------------------------------------

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# ---------------------------------------------------------------------------
# Info Target
# ---------------------------------------------------------------------------

add_custom_target(info
    COMMAND ${CMAKE_COMMAND} -E echo "=== Build Configuration ==="
    COMMAND ${CMAKE_COMMAND} -E echo "Project: ${PROJECT_NAME}"
    COMMAND ${CMAKE_COMMAND} -E echo "Compiler: ${CMAKE_C_COMPILER} (${CMAKE_C_COMPILER_ID})"
    COMMAND ${CMAKE_COMMAND} -E echo "Frontend: $<IF:$<BOOL:${COMPILER_CLANG_CL}>,clang-cl,GNU>"
    COMMAND ${CMAKE_COMMAND} -E echo "Generator: ${CMAKE_GENERATOR}"
    COMMAND ${CMAKE_COMMAND} -E echo "Build Type: ${CMAKE_BUILD_TYPE}"
    COMMAND ${CMAKE_COMMAND} -E echo "Platform: ${PLATFORM_NAME}"
    COMMAND ${CMAKE_COMMAND} -E echo "============================"
)

# ---------------------------------------------------------------------------
# Print final configuration
# ---------------------------------------------------------------------------

message(STATUS "----------------------------------------")
message(STATUS "Configuration Summary:")
message(STATUS "  Compiler: ${CMAKE_C_COMPILER}")
message(STATUS "  Build Type: ${CMAKE_BUILD_TYPE}")
message(STATUS "  Generator: ${CMAKE_GENERATOR}")
message(STATUS "  Platform: ${PLATFORM_NAME}")
message(STATUS "----------------------------------------")
