cmake_minimum_required(VERSION 3.20)
project(cnc_sim_wasm_core LANGUAGES CXX)

option(CNC_SIM_ENABLE_LINUXCNC_BRIDGE "Build the experimental LinuxCNC canon bridge" OFF)
option(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND "Build the native LinuxCNC librs274 backend" OFF)
set(CNC_SIM_LINUXCNC_ROOT "" CACHE PATH "LinuxCNC source root for the experimental bridge")

set(cnc_sim_core_sources
    src/canon_event_sink.cpp
    src/cnc_sim_api.cpp
    src/gcode_backend.cpp
    src/rtcp_kinematics.cpp
    src/simulator_gcode_controls.cpp
    src/smoke_gcode_parser.cpp
)

if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
    if(EMSCRIPTEN)
        message(FATAL_ERROR "CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND is native-only; it links LinuxCNC librs274")
    endif()
    if(NOT CNC_SIM_LINUXCNC_ROOT)
        message(FATAL_ERROR "Set CNC_SIM_LINUXCNC_ROOT to the LinuxCNC source root")
    endif()
    list(APPEND cnc_sim_core_sources
        src/linuxcnc_canon_bridge.cpp
        src/linuxcnc_rs274_backend.cpp
    )
endif()

add_library(cnc_sim_objects OBJECT
    ${cnc_sim_core_sources}
)

target_include_directories(cnc_sim_objects
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
)

target_compile_features(cnc_sim_objects PUBLIC cxx_std_17)

if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
    find_package(Python3 REQUIRED COMPONENTS Development)
    target_compile_definitions(cnc_sim_objects
        PRIVATE
            CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND
    )
    target_include_directories(cnc_sim_objects
        PRIVATE
            ${Python3_INCLUDE_DIRS}
            ${CNC_SIM_LINUXCNC_ROOT}/src
            ${CNC_SIM_LINUXCNC_ROOT}/src/emc
            ${CNC_SIM_LINUXCNC_ROOT}/src/emc/nml_intf
            ${CNC_SIM_LINUXCNC_ROOT}/src/emc/rs274ngc
            ${CNC_SIM_LINUXCNC_ROOT}/src/emc/motion
            ${CNC_SIM_LINUXCNC_ROOT}/include
    )
endif()

add_library(cnc_sim_core STATIC
    $<TARGET_OBJECTS:cnc_sim_objects>
)

target_include_directories(cnc_sim_core
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
)

if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
    target_link_directories(cnc_sim_core
        PUBLIC
            ${CNC_SIM_LINUXCNC_ROOT}/lib
    )
    target_link_libraries(cnc_sim_core
        PUBLIC
            rs274
            tooldata
            Python3::Python
    )
    target_link_options(cnc_sim_core
        PUBLIC
            "-Wl,-rpath,${CNC_SIM_LINUXCNC_ROOT}/lib"
    )
endif()

if(NOT EMSCRIPTEN)
    add_executable(cnc_sim_api_smoke
        tests/cnc_sim_api_smoke.cpp
    )
    target_link_libraries(cnc_sim_api_smoke PRIVATE cnc_sim_core)
    target_include_directories(cnc_sim_api_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

    add_executable(canon_event_sink_smoke
        tests/canon_event_sink_smoke.cpp
    )
    target_link_libraries(canon_event_sink_smoke PRIVATE cnc_sim_core)
    target_include_directories(canon_event_sink_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

    add_executable(rtcp_kinematics_smoke
        tests/rtcp_kinematics_smoke.cpp
    )
    target_link_libraries(rtcp_kinematics_smoke PRIVATE cnc_sim_core)
    target_include_directories(rtcp_kinematics_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

    add_executable(simulator_gcode_controls_smoke
        tests/simulator_gcode_controls_smoke.cpp
    )
    target_link_libraries(simulator_gcode_controls_smoke PRIVATE cnc_sim_core)
    target_include_directories(simulator_gcode_controls_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

    add_executable(cnc_sim_dump
        tools/cnc_sim_dump.cpp
    )
    target_link_libraries(cnc_sim_dump PRIVATE cnc_sim_core)
    target_include_directories(cnc_sim_dump PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

    if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
        add_executable(cnc_sim_api_linuxcnc_rs274_smoke
            tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp
        )
        target_link_libraries(cnc_sim_api_linuxcnc_rs274_smoke PRIVATE cnc_sim_core)
        target_include_directories(cnc_sim_api_linuxcnc_rs274_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
    endif()
endif()

if(CNC_SIM_ENABLE_LINUXCNC_BRIDGE)
    if(NOT CNC_SIM_LINUXCNC_ROOT)
        message(FATAL_ERROR "Set CNC_SIM_LINUXCNC_ROOT to the LinuxCNC source root")
    endif()
    add_library(cnc_sim_linuxcnc_canon_bridge STATIC
        src/linuxcnc_canon_bridge.cpp
    )
    target_include_directories(cnc_sim_linuxcnc_canon_bridge
        PRIVATE
            ${CMAKE_CURRENT_SOURCE_DIR}/include
            ${CMAKE_CURRENT_SOURCE_DIR}/src
            ${CNC_SIM_LINUXCNC_ROOT}/src/emc
            ${CNC_SIM_LINUXCNC_ROOT}/src/emc/nml_intf
            ${CNC_SIM_LINUXCNC_ROOT}/src/emc/rs274ngc
            ${CNC_SIM_LINUXCNC_ROOT}/src
            ${CNC_SIM_LINUXCNC_ROOT}/include
    )
    target_link_libraries(cnc_sim_linuxcnc_canon_bridge PRIVATE cnc_sim_core)
endif()

if(EMSCRIPTEN)
    add_executable(cnc_sim_wasm
        $<TARGET_OBJECTS:cnc_sim_objects>
    )

    target_include_directories(cnc_sim_wasm
        PRIVATE
            ${CMAKE_CURRENT_SOURCE_DIR}/include
            ${CMAKE_CURRENT_SOURCE_DIR}/src
    )

    set_target_properties(cnc_sim_wasm PROPERTIES
        OUTPUT_NAME "cnc_sim"
        SUFFIX ".js"
    )

    target_link_options(cnc_sim_wasm PRIVATE
        "--no-entry"
        "-sMODULARIZE=1"
        "-sEXPORT_NAME=createCncSimModule"
        "-sALLOW_MEMORY_GROWTH=1"
        "-sALLOW_TABLE_GROWTH=1"
        "-sEXPORTED_FUNCTIONS=['_malloc','_free','_cnc_sim_create','_cnc_sim_destroy','_cnc_sim_reset','_cnc_sim_set_dialect','_cnc_sim_set_event_callback','_cnc_sim_load_config_json','_cnc_sim_parse_program','_cnc_sim_last_error']"
        "-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','addFunction','removeFunction']"
    )
endif()
