cmake_minimum_required(VERSION 3.20)
project(cf_pipeline_engine LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)

include(CTest)

find_package(Python3 REQUIRED COMPONENTS Interpreter)

execute_process(
  COMMAND ${Python3_EXECUTABLE} -c "import cf_datahive as d; print(getattr(d, 'cf_datahive_cpp_include_path', lambda: '')())"
  OUTPUT_VARIABLE CF_DATAHIVE_CPP_INCLUDE_DIR
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

execute_process(
  COMMAND ${Python3_EXECUTABLE} -c "import cf_datahive as d; print(getattr(d, 'cf_datahive_cpp_library_path', lambda: '')())"
  OUTPUT_VARIABLE CF_DATAHIVE_CPP_LIBRARY_PATH
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

execute_process(
  COMMAND ${Python3_EXECUTABLE} -c "import cf_datahive as d; print(getattr(d, 'cf_datahive_cpp_import_library_path', lambda: '')())"
  OUTPUT_VARIABLE CF_DATAHIVE_CPP_IMPORT_LIBRARY_PATH
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

execute_process(
  COMMAND ${Python3_EXECUTABLE} -c "import cf_datahive as d; print(getattr(d, 'cf_datahive_cpp_runtime_dir', lambda: '')())"
  OUTPUT_VARIABLE CF_DATAHIVE_CPP_RUNTIME_DIR
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

execute_process(
  COMMAND ${Python3_EXECUTABLE} -c "import cf_datahive as d; print(getattr(d, 'cf_datahive_cpp_consumer_cmake_path', lambda: '')())"
  OUTPUT_VARIABLE CF_DATAHIVE_CPP_CONSUMER_CMAKE
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

if (CF_DATAHIVE_CPP_INCLUDE_DIR STREQUAL "" OR NOT EXISTS "${CF_DATAHIVE_CPP_INCLUDE_DIR}/cf_datahive_cpp/cf_datahive_cpp.h")
  message(FATAL_ERROR "cf_datahive packaged include surface not found in '${CF_DATAHIVE_CPP_INCLUDE_DIR}'. Reinstall cf-datahive>=0.1.6.")
endif()

if (CF_DATAHIVE_CPP_LIBRARY_PATH STREQUAL "" OR NOT EXISTS "${CF_DATAHIVE_CPP_LIBRARY_PATH}")
  message(FATAL_ERROR "cf_datahive packaged runtime library not found in '${CF_DATAHIVE_CPP_LIBRARY_PATH}'. Reinstall cf-datahive>=0.1.6.")
endif()

if (WIN32 AND (CF_DATAHIVE_CPP_IMPORT_LIBRARY_PATH STREQUAL "" OR NOT EXISTS "${CF_DATAHIVE_CPP_IMPORT_LIBRARY_PATH}"))
  message(FATAL_ERROR "cf_datahive packaged import library not found in '${CF_DATAHIVE_CPP_IMPORT_LIBRARY_PATH}'. Reinstall cf-datahive>=0.1.6.")
endif()

if (NOT EXISTS "${CF_DATAHIVE_CPP_CONSUMER_CMAKE}")
  message(FATAL_ERROR "cf_datahive consumer helper not found in '${CF_DATAHIVE_CPP_CONSUMER_CMAKE}'. Reinstall cf-datahive.")
endif()
include("${CF_DATAHIVE_CPP_CONSUMER_CMAKE}")

if (NOT COMMAND cf_datahive_import_cpp_target)
  message(FATAL_ERROR "cf_datahive import helper not found. Reinstall cf-datahive>=0.1.6.")
endif()

cf_datahive_import_cpp_target(
  TARGET cf_datahive_cpp
  INCLUDE_DIR "${CF_DATAHIVE_CPP_INCLUDE_DIR}"
  LIBRARY_PATH "${CF_DATAHIVE_CPP_LIBRARY_PATH}"
  IMPORT_LIBRARY_PATH "${CF_DATAHIVE_CPP_IMPORT_LIBRARY_PATH}"
)

set(TYPE_REGISTRY_V0_SOURCE_PATH
  "${CMAKE_CURRENT_SOURCE_DIR}/resources/type_registry.v0.json"
)

include(FetchContent)
FetchContent_Declare(
  nlohmann_json
  GIT_REPOSITORY https://github.com/nlohmann/json.git
  GIT_TAG v3.11.2
)
FetchContent_MakeAvailable(nlohmann_json)

add_library(cf_pipeline_v2_utils STATIC
  src/common/sha256.cpp
  src/common/type_registry.cc
  src/compiler/jsonld_loader.cpp
  src/compiler/signature.cpp
)

target_include_directories(cf_pipeline_v2_utils PUBLIC
  ${CMAKE_CURRENT_SOURCE_DIR}/include
  ${CMAKE_CURRENT_SOURCE_DIR}/src
)

set(TYPE_REGISTRY_V0_FILENAME "type_registry.v0.json")

if (NOT EXISTS "${TYPE_REGISTRY_V0_SOURCE_PATH}")
  message(FATAL_ERROR "Bundled type_registry.v0.json not found in '${TYPE_REGISTRY_V0_SOURCE_PATH}'.")
endif()

file(TO_CMAKE_PATH "${TYPE_REGISTRY_V0_SOURCE_PATH}" TYPE_REGISTRY_V0_SOURCE_PATH)

target_compile_definitions(cf_pipeline_v2_utils PRIVATE
  CF_TYPE_REGISTRY_V0_PATH="${TYPE_REGISTRY_V0_SOURCE_PATH}"
  CF_TYPE_REGISTRY_V0_FILENAME="${TYPE_REGISTRY_V0_FILENAME}"
)

target_link_libraries(cf_pipeline_v2_utils PUBLIC nlohmann_json::nlohmann_json)

add_executable(cf_siggen tools/cf_siggen_main.cpp)
target_include_directories(cf_siggen PRIVATE
  ${CMAKE_CURRENT_SOURCE_DIR}/include
  ${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_link_libraries(cf_siggen PRIVATE cf_pipeline_v2_utils)

set(GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated)
file(MAKE_DIRECTORY ${GENERATED_DIR})

add_custom_command(
  OUTPUT ${GENERATED_DIR}/cf_test_signature_hashes.h
  COMMAND ${Python3_EXECUTABLE} -m cogniflow_pipeline_sdk.siggen
          --steps ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_steps.jsonld
          --out ${GENERATED_DIR}/cf_test_signature_hashes.h
          --scratch
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_steps.jsonld
  COMMENT "Generating test signature hashes"
)

add_library(cf_pipeline_v2_core STATIC
  src/compiler/pipeline_parser.cpp
  src/compiler/plan_builder.cpp
  src/plugin_loader/plugin_loader.cpp
  src/runtime/concurrency_controller.cpp
  src/runtime/builtin_steps.cpp
  src/scheduler/thread_pool.cpp
  src/scheduler/scheduler.cpp
)

target_include_directories(cf_pipeline_v2_core PUBLIC
  ${CMAKE_CURRENT_SOURCE_DIR}/include
  ${CMAKE_CURRENT_SOURCE_DIR}/src
)

target_link_libraries(cf_pipeline_v2_core PUBLIC cf_pipeline_v2_utils cf_datahive_cpp)

if(UNIX AND NOT APPLE)
  target_link_libraries(cf_pipeline_v2_core PUBLIC dl)
endif()

add_executable(cf_pipeline_v2 src/main.cpp)

target_link_libraries(cf_pipeline_v2 PRIVATE cf_pipeline_v2_core)

add_custom_command(TARGET cf_pipeline_v2 POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy_if_different
          ${TYPE_REGISTRY_V0_SOURCE_PATH}
          $<TARGET_FILE_DIR:cf_pipeline_v2>/${TYPE_REGISTRY_V0_FILENAME}
)

if (NOT COMMAND cf_datahive_stage_consumer_runtime)
  message(FATAL_ERROR "cf_datahive consumer runtime helper not found. Reinstall cf-datahive.")
endif()

cf_datahive_stage_consumer_runtime(
  TARGET cf_pipeline_v2
  RUNTIME_DIR "${CF_DATAHIVE_CPP_RUNTIME_DIR}"
  DESTINATIONS $<TARGET_FILE_DIR:cf_pipeline_v2>
)

# Tests
add_library(cf_pipeline_v2_test_plugin SHARED
  ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_plugin.cpp
  ${GENERATED_DIR}/cf_test_signature_hashes.h
)

target_include_directories(cf_pipeline_v2_test_plugin PUBLIC
  ${CMAKE_CURRENT_SOURCE_DIR}/include
  ${CMAKE_CURRENT_SOURCE_DIR}/src
  ${GENERATED_DIR}
)

target_link_libraries(cf_pipeline_v2_test_plugin PUBLIC cf_pipeline_v2_utils)
target_compile_definitions(cf_pipeline_v2_test_plugin PRIVATE CF_STEP_ABI_EXPORTS)

set_target_properties(cf_pipeline_v2_test_plugin PROPERTIES
  RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests
  LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests
  ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests
)

add_executable(cf_pipeline_v2_tests tests/test_main.cpp)

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

target_link_libraries(cf_pipeline_v2_tests PRIVATE cf_pipeline_v2_core)

add_custom_command(TARGET cf_pipeline_v2_tests POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy_if_different
          ${TYPE_REGISTRY_V0_SOURCE_PATH}
          $<TARGET_FILE_DIR:cf_pipeline_v2_tests>/${TYPE_REGISTRY_V0_FILENAME}
)

cf_datahive_stage_consumer_runtime(
  TARGET cf_pipeline_v2_tests
  RUNTIME_DIR "${CF_DATAHIVE_CPP_RUNTIME_DIR}"
  DESTINATIONS $<TARGET_FILE_DIR:cf_pipeline_v2_tests>
)

if (BUILD_TESTING)
  add_test(
    NAME cf_pipeline_v2_tests
    COMMAND cf_pipeline_v2_tests
  )
  set_tests_properties(cf_pipeline_v2_tests PROPERTIES
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  )
endif()

install(TARGETS cf_siggen RUNTIME DESTINATION bin)
install(TARGETS cf_pipeline_v2 RUNTIME DESTINATION bin)
if (NOT CF_DATAHIVE_CPP_RUNTIME_DIR STREQUAL "" AND EXISTS "${CF_DATAHIVE_CPP_RUNTIME_DIR}")
  set(_cf_datahive_cpp_runtime_install_dir "${CF_DATAHIVE_CPP_RUNTIME_DIR}")
  file(TO_CMAKE_PATH "${_cf_datahive_cpp_runtime_install_dir}" _cf_datahive_cpp_runtime_install_dir)
  install(DIRECTORY "${_cf_datahive_cpp_runtime_install_dir}/" DESTINATION bin
    FILES_MATCHING
    PATTERN "*.dll"
    PATTERN "*.so"
    PATTERN "*.dylib"
  )
endif()
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION include)
install(FILES ${TYPE_REGISTRY_V0_SOURCE_PATH} DESTINATION bin)
