# 3.10 is the oldest version CMake 4.x still accepts; ...4.0 opts in to all
# policies up to 4.0 (including CMP0167: use Boost's own config files instead
# of the removed FindBoost module), which keeps modern CMake quiet.
cmake_minimum_required(VERSION 3.10...4.0)
project(tegmen LANGUAGES C CXX Fortran)

# Set WSJT-X source directory (cherry-picked sources in repo)
# For Tegmen Developers: Run sync-wsjtx.sh to update from ../wsjtx/lib
# For Tegmen Users: Sources are already in wsjtx-src-cherry-picked/lib
set(WSJTX_LIB_DIR "${CMAKE_SOURCE_DIR}/wsjtx-src-cherry-picked/lib")

# Check that WSJT-X directory exists
if(NOT EXISTS "${WSJTX_LIB_DIR}")
    message(FATAL_ERROR "WSJT-X lib directory not found: ${WSJTX_LIB_DIR}")
    message(FATAL_ERROR "If you're a Tegmen Developer, run: ./sync-wsjtx.sh")
    message(FATAL_ERROR "If you're a Tegmen User, the wsjtx-src-cherry-picked/ directory should be in the repo")
endif()

# Find required dependencies
# FFTW3 is required by WSJT-X - we keep it as a dependency
find_package(PkgConfig REQUIRED)
pkg_check_modules(FFTW3 REQUIRED fftw3f)

# Boost is required for CRC functions (like WSJT-X uses)
find_package(Boost REQUIRED COMPONENTS)

# Default to an optimized build. Without this, a bare `cmake ..` leaves
# CMAKE_BUILD_TYPE empty, no -O flags are applied, and the whole WSJT-X DSP
# (FFTs, sync search, LDPC/OSD, subtraction) compiles at -O0 — several times
# slower than it needs to be. A Debug build still gets -O0 -g as usual.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()

# Fortran compiler setup
set(CMAKE_Fortran_STANDARD 2003)
set(CMAKE_Fortran_STANDARD_REQUIRED ON)

# Fortran flags
if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
    set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Wall -Wno-conversion -Wno-surprising -Wno-unused-dummy-argument -fno-second-underscore")
    set(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE} -O3")
    # -finit-local-zero: zero-initialize local variables (the decode path relies
    #   on this). -fPIC: position-independent code. Optimization comes from the
    #   per-config flags above (RELEASE => -O3), not hardcoded here.
    set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -finit-local-zero -fPIC")
endif()

# C/C++ flags
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
    set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
endif()

# Include directories
include_directories(${FFTW3_INCLUDE_DIRS})
include_directories(${WSJTX_LIB_DIR})
include_directories(${WSJTX_LIB_DIR}/ft8)
include_directories(${WSJTX_LIB_DIR}/ft4)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)

# ============================================================================
# Source files organized by dependency level
# ============================================================================

# Level 1: Base modules (no dependencies)
# Note: constants.f90 is an include file, not a module - don't compile it
set(BASE_SOURCES
    ${WSJTX_LIB_DIR}/types.f90
    ${WSJTX_LIB_DIR}/timer_module.f90
    ${WSJTX_LIB_DIR}/shmem.f90
)

# Level 2: Utilities
set(UTIL_SOURCES
    ${WSJTX_LIB_DIR}/indexx.f90
    ${WSJTX_LIB_DIR}/crc.f90
    ${WSJTX_LIB_DIR}/chkcall.f90
    ${WSJTX_LIB_DIR}/db.f90
    ${WSJTX_LIB_DIR}/peakup.f90
    ${WSJTX_LIB_DIR}/pctile.f90
    ${WSJTX_LIB_DIR}/polyfit.f90
    ${WSJTX_LIB_DIR}/platanh.f90
    ${WSJTX_LIB_DIR}/nuttal_window.f90
    ${WSJTX_LIB_DIR}/shell.f90
    ${WSJTX_LIB_DIR}/determ.f90
    ${WSJTX_LIB_DIR}/smo121.f90
    # WSJT-X's CRC implementation (Boost-based, like the WSJT-X GUI); FT8/FT4 use CRC-14 only
    ${WSJTX_LIB_DIR}/crc14.cpp
    ${CMAKE_SOURCE_DIR}/src/stdcall_standalone.f90
    ${CMAKE_SOURCE_DIR}/src/shmem_stub.c
)

# Level 3: FFTW3 interface
set(FFTW_SOURCES
    ${WSJTX_LIB_DIR}/fftw3mod.f90
)

# Level 4: Message packing
set(PACK_SOURCES
    ${WSJTX_LIB_DIR}/packjt.f90
    ${WSJTX_LIB_DIR}/77bit/packjt77.f90
)

# Level 5: LDPC encoding/decoding
set(LDPC_SOURCES
    ${WSJTX_LIB_DIR}/ft8/encode174_91.f90
    ${WSJTX_LIB_DIR}/ft8/encode174_91_nocrc.f90
    ${WSJTX_LIB_DIR}/ft8/get_crc14.f90
    ${WSJTX_LIB_DIR}/ft8/osd174_91.f90
    ${WSJTX_LIB_DIR}/ft8/decode174_91.f90
)

# Level 6: Signal processing
set(SIGNAL_SOURCES
    ${WSJTX_LIB_DIR}/four2a.f90
    ${WSJTX_LIB_DIR}/ft2/gfsk_pulse.f90
)

# Level 7: FT8 encoding
set(FT8_ENCODE_SOURCES
    ${WSJTX_LIB_DIR}/ft8/genft8.f90
    ${WSJTX_LIB_DIR}/ft8/gen_ft8wave.f90
)

# Level 8: FT8 decoding support
set(FT8_DECODE_SUPPORT_SOURCES
    ${WSJTX_LIB_DIR}/ft8/baseline.f90
    ${WSJTX_LIB_DIR}/ft8/get_spectrum_baseline.f90
    ${WSJTX_LIB_DIR}/ft8/ft8_downsample.f90
    ${WSJTX_LIB_DIR}/ft8/sync8.f90
    ${WSJTX_LIB_DIR}/ft8/sync8d.f90
    ${WSJTX_LIB_DIR}/ft8/ft8_a7.f90
    ${WSJTX_LIB_DIR}/ft8/ft8_a8d.f90
    ${WSJTX_LIB_DIR}/ft8/ft8apset.f90
    ${WSJTX_LIB_DIR}/ft8/twkfreq1.f90
    ${WSJTX_LIB_DIR}/ft8/subtractft8.f90
    ${WSJTX_LIB_DIR}/ft8/ft8b.f90
)

# Level 9: FT8 decoding
set(FT8_DECODE_SOURCES
    ${WSJTX_LIB_DIR}/ft8_decode.f90
)

# Level 10: FT4 encoding
set(FT4_ENCODE_SOURCES
    ${WSJTX_LIB_DIR}/ft4/genft4.f90
    ${WSJTX_LIB_DIR}/ft4/gen_ft4wave.f90
)

# Level 11: FT4 decoding support
set(FT4_DECODE_SUPPORT_SOURCES
    ${WSJTX_LIB_DIR}/ft4/ft4_downsample.f90
    ${WSJTX_LIB_DIR}/ft4/ft4_baseline.f90
    ${WSJTX_LIB_DIR}/ft4/get_ft4_bitmetrics.f90
    ${WSJTX_LIB_DIR}/ft4/getcandidates4.f90
    ${WSJTX_LIB_DIR}/ft4/sync4d.f90
    ${WSJTX_LIB_DIR}/ft4/subtractft4.f90
)

# Level 12: FT4 decoding
set(FT4_DECODE_SOURCES
    ${WSJTX_LIB_DIR}/ft4_decode.f90
)

# C API sources
set(C_API_SOURCES
    ${CMAKE_SOURCE_DIR}/src/tegmen.c
)

# Fortran initialization
set(FORTRAN_INIT
    ${CMAKE_SOURCE_DIR}/src/tegmen_init.f90
)

# Fortran helper module (module, no bind(C))
set(FORTRAN_HELPER
    ${CMAKE_SOURCE_DIR}/src/tegmen_ft8_decode_helper.f90
    ${CMAKE_SOURCE_DIR}/src/tegmen_ft4_decode_helper.f90
)

# Fortran standalone procedures (no module, no bind(C))
set(FORTRAN_STANDALONE
    ${CMAKE_SOURCE_DIR}/src/tegmen_ft8_decode_standalone.f90
    ${CMAKE_SOURCE_DIR}/src/tegmen_ft4_decode_standalone.f90
)

# Fortran C bindings (decode goes through the *_standalone path, so only encode bindings are needed)
set(FORTRAN_C_BINDINGS
    ${CMAKE_SOURCE_DIR}/src/tegmen_ft8_encode_c.f90
    ${CMAKE_SOURCE_DIR}/src/tegmen_ft4_encode_c.f90
)

# Combine all sources in dependency order
set(TEGMEN_SOURCES
    ${BASE_SOURCES}
    ${UTIL_SOURCES}
    ${FFTW_SOURCES}
    ${PACK_SOURCES}
    ${LDPC_SOURCES}
    ${SIGNAL_SOURCES}
    ${FT8_ENCODE_SOURCES}
    ${FT8_DECODE_SUPPORT_SOURCES}
    ${FT8_DECODE_SOURCES}
    ${FT4_ENCODE_SOURCES}
    ${FT4_DECODE_SUPPORT_SOURCES}
    ${FT4_DECODE_SOURCES}
    ${C_API_SOURCES}
    ${FORTRAN_INIT}
    ${FORTRAN_HELPER}
    ${FORTRAN_STANDALONE}
    ${FORTRAN_C_BINDINGS}
)

# ============================================================================
# Build static library
# ============================================================================

add_library(tegmen STATIC ${TEGMEN_SOURCES})

# Link libraries
target_link_libraries(tegmen
    ${FFTW3_LIBRARIES}
    Boost::boost
    m
)

# Fortran module output directory
set_target_properties(tegmen PROPERTIES
    Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules
)

# Include directories for modules
target_include_directories(tegmen PUBLIC
    ${CMAKE_BINARY_DIR}/modules
    ${FFTW3_INCLUDE_DIRS}
)

# ============================================================================
# Test executables
# ============================================================================


# C API roundtrip test (encode then decode via C library)
add_executable(test_roundtrip tests/test_roundtrip.c tests/test_roundtrip_common.c)
target_link_libraries(test_roundtrip tegmen fftw3f m Boost::boost)
target_include_directories(test_roundtrip PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/tests)

# C API FT4 roundtrip test
add_executable(test_roundtrip_ft4 tests/test_roundtrip_ft4.c tests/test_roundtrip_common.c)
target_link_libraries(test_roundtrip_ft4 tegmen fftw3f m Boost::boost)
target_include_directories(test_roundtrip_ft4 PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/tests)


# ============================================================================
# Example programs
# ============================================================================

# Example: Complete roundtrip (encode -> WAV -> decode)
add_executable(example_roundtrip examples/example_roundtrip.c)
target_link_libraries(example_roundtrip tegmen fftw3f m Boost::boost)
target_include_directories(example_roundtrip PRIVATE ${CMAKE_SOURCE_DIR}/src)




# ============================================================================
# Enable CTest for test suite
# ============================================================================

enable_testing()

# Add tests to CTest
add_test(NAME test_roundtrip_ft8 COMMAND test_roundtrip)
add_test(NAME test_roundtrip_ft4 COMMAND test_roundtrip_ft4)

# Set test working directory to build directory (where test executables are)
set_tests_properties(test_roundtrip_ft8 test_roundtrip_ft4
    PROPERTIES WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
