cmake_minimum_required(VERSION 3.15...3.27)
project(cisv_python LANGUAGES C CXX)

# C++ standard for nanobind
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Try to find Python first to determine if we're in pip install mode
find_package(Python 3.10 COMPONENTS Interpreter Development.Module REQUIRED)

# Check if we're building as a standalone package (pip install) or as part of cisv
if(SKBUILD)
    # Building via scikit-build-core (pip install)
    message(STATUS "Building via scikit-build-core")
    message(STATUS "CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")

    # Use FetchContent to get nanobind
    include(FetchContent)
    FetchContent_Declare(
        nanobind
        GIT_REPOSITORY https://github.com/wjakob/nanobind.git
        GIT_TAG v2.2.0
    )
    FetchContent_MakeAvailable(nanobind)

    # Build the core library from source
    # Check multiple locations for core sources
    set(CISV_CORE_DIR "")

    # Option 1: Local core directory (sdist package)
    if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/core/src/parser.c")
        set(CISV_CORE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/core")
        message(STATUS "Using local core sources (sdist)")
    # Option 2: Parent submodule layout (development/cibuildwheel from git checkout)
    elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../core/core/src/parser.c")
        get_filename_component(CISV_CORE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../core/core" ABSOLUTE)
        message(STATUS "Using parent submodule core sources: ${CISV_CORE_DIR}")
    # Option 3: Legacy parent layout
    elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../core/src/parser.c")
        get_filename_component(CISV_CORE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../core" ABSOLUTE)
        message(STATUS "Using parent core sources: ${CISV_CORE_DIR}")
    # Option 4: SKBUILD_PROJECT_DIR with submodule layout
    elseif(DEFINED SKBUILD_PROJECT_DIR AND EXISTS "${SKBUILD_PROJECT_DIR}/../core/core/src/parser.c")
        get_filename_component(CISV_CORE_DIR "${SKBUILD_PROJECT_DIR}/../core/core" ABSOLUTE)
        message(STATUS "Using SKBUILD_PROJECT_DIR submodule core sources: ${CISV_CORE_DIR}")
    # Option 5: SKBUILD_PROJECT_DIR legacy layout
    elseif(DEFINED SKBUILD_PROJECT_DIR AND EXISTS "${SKBUILD_PROJECT_DIR}/../core/src/parser.c")
        get_filename_component(CISV_CORE_DIR "${SKBUILD_PROJECT_DIR}/../core" ABSOLUTE)
        message(STATUS "Using SKBUILD_PROJECT_DIR core sources: ${CISV_CORE_DIR}")
    else()
        message(FATAL_ERROR "Cannot find core sources. Searched in:\n"
            "  ${CMAKE_CURRENT_SOURCE_DIR}/core/src/parser.c\n"
            "  ${CMAKE_CURRENT_SOURCE_DIR}/../core/core/src/parser.c\n"
            "  ${CMAKE_CURRENT_SOURCE_DIR}/../core/src/parser.c")
    endif()

    # Add core library sources directly
    add_library(cisv_core STATIC
        ${CISV_CORE_DIR}/src/parser.c
    )
    target_include_directories(cisv_core PUBLIC
        ${CISV_CORE_DIR}/include
    )
    # -fPIC is required for linking into shared library
    set_target_properties(cisv_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
    target_compile_options(cisv_core PRIVATE
        -O3 -ffast-math -funroll-loops
    )

    # For distributable packages, use portable SIMD settings
    # Do NOT use -march=native as it causes "Illegal instruction" on older CPUs
    include(CheckCCompilerFlag)

    # Determine actual target architecture (handle cross-compilation on macOS)
    if(APPLE AND CMAKE_OSX_ARCHITECTURES)
        set(TARGET_ARCH "${CMAKE_OSX_ARCHITECTURES}")
    else()
        set(TARGET_ARCH "${CMAKE_SYSTEM_PROCESSOR}")
    endif()
    message(STATUS "Target architecture: ${TARGET_ARCH}")

    # Set architecture-specific SIMD flags
    if(TARGET_ARCH MATCHES "(x86_64|AMD64|amd64)")
        # x86-64: Use SSE4.2 baseline (available on all modern x86-64 CPUs)
        check_c_compiler_flag("-msse4.2" COMPILER_SUPPORTS_SSE42)
        if(COMPILER_SUPPORTS_SSE42)
            target_compile_options(cisv_core PRIVATE -msse4.2)
        endif()
    elseif(TARGET_ARCH MATCHES "(aarch64|arm64|ARM64)")
        # ARM64: NEON is always available, use portable flag
        if(APPLE)
            # macOS ARM: no extra flags needed, NEON is implicit
        else()
            target_compile_options(cisv_core PRIVATE -march=armv8-a)
        endif()
    endif()

    # Link pthread for parallel parsing
    find_package(Threads REQUIRED)
    target_link_libraries(cisv_core PRIVATE Threads::Threads)

else()
    # Building as part of the main cisv project
    message(STATUS "Building as part of cisv project")

    # Try to find nanobind via find_package first
    find_package(nanobind CONFIG QUIET)
    if(NOT nanobind_FOUND)
        # Fall back to FetchContent
        include(FetchContent)
        FetchContent_Declare(
            nanobind
            GIT_REPOSITORY https://github.com/wjakob/nanobind.git
            GIT_TAG v2.2.0
        )
        FetchContent_MakeAvailable(nanobind)
    endif()

    # Use the existing cisv_static target from the parent project
    if(NOT TARGET cisv_static)
        message(FATAL_ERROR "cisv_static target not found. Build from the project root.")
    endif()

    # Create alias for consistency
    add_library(cisv_core ALIAS cisv_static)
endif()

# Create the Python extension module
nanobind_add_module(_core
    STABLE_ABI
    NB_STATIC
    src/cisv_nanobind.cpp
)

# Link against the core library
if(SKBUILD)
    target_link_libraries(_core PRIVATE cisv_core)
    target_include_directories(_core PRIVATE
        ${CISV_CORE_DIR}/include
    )
else()
    target_link_libraries(_core PRIVATE cisv_static)
    target_include_directories(_core PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/../core/core/include
    )
endif()

# Set output directory for the module
if(SKBUILD)
    # For scikit-build-core: install to the cisv package directory
    install(TARGETS _core LIBRARY DESTINATION cisv)
else()
    # For development: output to the cisv package directory
    set_target_properties(_core PROPERTIES
        LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cisv"
    )
endif()

# Compiler optimizations
target_compile_options(_core PRIVATE
    $<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-O3>
)
