cmake_minimum_required(VERSION 3.24)
project(DingoSDK LANGUAGES CXX C)
if(WIN32)
    if(NOT MSVC OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
        message(FATAL_ERROR "DingoSDK on Windows requires MSVC and the x64 platform.")
    endif()
else()
    if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
        message(FATAL_ERROR "DingoSDK on Linux requires a 64-bit platform.")
    endif()
    message(STATUS "Linux build: dedicated server only (launcher/runtime stay Windows-only).")
endif()

include(cmake/ProjectOptions.cmake)
include(cmake/Dependencies.cmake)
include(cmake/CustomScriptData.cmake)
include(cmake/Core.cmake)
if(WIN32)
    include(cmake/Features.cmake)
    include(cmake/UI.cmake)
    include(cmake/Runtime.cmake)
endif()
include(cmake/Apps.cmake)
if(WIN32)
    include(cmake/Backtrace.cmake)
endif()

option(DINGOSDK_BUILD_TESTS "Build logging regression tests" OFF)
option(DINGOSDK_BUILD_PARK_EDITOR_TESTS "Build park editor regression tests" OFF)
option(DINGOSDK_BUILD_TRAINER_TESTS "Build the trainer's tools and regression tests" OFF)
if(DINGOSDK_BUILD_PARK_EDITOR_TESTS AND WIN32)
    enable_testing()
    add_executable(dingosdk_park_editor_tests Extension/Objects/ParkEditor/Test/park_editor_tests.cpp)
    target_link_libraries(dingosdk_park_editor_tests PRIVATE dingosdk_local_profile)
    add_test(NAME park_editor COMMAND dingosdk_park_editor_tests "${CMAKE_CURRENT_BINARY_DIR}/editor-fixtures")
    add_executable(dingosdk_park_editor_ui_tests Extension/UI/Overlay/Test/park_editor_tests.cpp
        Extension/UI/Overlay/park_editor.cpp Extension/UI/Overlay/park_editor_actions.cpp
        Extension/UI/Overlay/park_editor_panels.cpp Extension/UI/Overlay/park_editor_viewport.cpp Extension/UI/Overlay/park_previews.cpp)
    target_link_libraries(dingosdk_park_editor_ui_tests PRIVATE dingosdk_imgui dingosdk_json dingosdk_game_archives dingosdk_content_cache dingosdk_logging)
    add_test(NAME park_editor_ui COMMAND dingosdk_park_editor_ui_tests "${CMAKE_CURRENT_BINARY_DIR}/editor-preview" "${DINGOSDK_TEST_GAME_ROOT}")
    add_executable(dingosdk_park_editor_transaction_tests Extension/Objects/ParkEditor/Test/park_editor_transaction_tests.cpp)
    target_link_libraries(dingosdk_park_editor_transaction_tests PRIVATE dingosdk_local_profile dingosdk_logging)
    add_test(NAME park_editor_transactions COMMAND dingosdk_park_editor_transaction_tests)
    add_executable(dingosdk_profile_migration_tests Extension/Profile/Test/profile_migration_tests.cpp)
    target_link_libraries(dingosdk_profile_migration_tests PRIVATE dingosdk_local_profile)
    add_test(NAME profile_migration COMMAND dingosdk_profile_migration_tests)
    add_executable(dingosdk_profile_change_tests Extension/Profile/Test/profile_change_tests.cpp)
    target_link_libraries(dingosdk_profile_change_tests PRIVATE dingosdk_local_profile)
    add_test(NAME profile_changes COMMAND dingosdk_profile_change_tests)
    add_executable(dingosdk_object_categories_tests Extension/Objects/Test/object_categories_tests.cpp)
    target_link_libraries(dingosdk_object_categories_tests PRIVATE dingosdk_local_profile)
    add_test(NAME object_categories COMMAND dingosdk_object_categories_tests)
endif()
option(DINGOSDK_BUILD_MULTIPLAYER_TESTS "Build multiplayer protocol regression tests" OFF)
if(DINGOSDK_BUILD_MULTIPLAYER_TESTS)
    enable_testing()
    if(WIN32)
        add_executable(dingosdk_voice_tests Extension/Multiplayer/Test/voice_chat_tests.cpp Extension/Multiplayer/Net/protocol.cpp
            Extension/Multiplayer/Net/delta_codec.cpp Extension/Multiplayer/Net/wire_codec.cpp)
        target_link_libraries(dingosdk_voice_tests PRIVATE dingosdk_lz4 dingosdk_zstd)
        add_test(NAME multiplayer_voice COMMAND dingosdk_voice_tests)
    endif()
    add_executable(dingosdk_party_tests Extension/Multiplayer/Test/party_tests.cpp Extension/Multiplayer/Session/party_book.cpp)
    target_include_directories(dingosdk_party_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME multiplayer_parties COMMAND dingosdk_party_tests)
    add_executable(dingosdk_server_activity_tests Server/Test/server_activity_tests.cpp Server/server_activity.cpp
        Extension/Throwdowns/throwdown_wire.cpp)
    target_include_directories(dingosdk_server_activity_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME server_activity COMMAND dingosdk_server_activity_tests)
    add_executable(dingosdk_speed_check_tests Server/Test/speed_check_tests.cpp)
    target_include_directories(dingosdk_speed_check_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME server_speed_check COMMAND dingosdk_speed_check_tests)
    add_executable(dingosdk_server_config_tests Server/Test/server_config_tests.cpp Server/server_config.cpp
        Extension/Multiplayer/Net/protocol.cpp)
    target_include_directories(dingosdk_server_config_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    if(WIN32)
        target_link_libraries(dingosdk_server_config_tests PRIVATE dingosdk_launcher_support dingosdk_world_layer_scan dingosdk_json
            dingosdk_lz4 dingosdk_zstd dingosdk_logging)
    else()
        target_link_libraries(dingosdk_server_config_tests PRIVATE dingosdk_launcher_support dingosdk_world_layer_scan dingosdk_json
            dingosdk_lz4 dingosdk_zstd)
    endif()
    add_test(NAME server_config COMMAND dingosdk_server_config_tests)
    add_executable(dingosdk_word_filter_tests Engine/Core/Text/Test/word_filter_tests.cpp)
    target_link_libraries(dingosdk_word_filter_tests PRIVATE dingosdk_word_filter)
    add_test(NAME word_filter COMMAND dingosdk_word_filter_tests)
    add_executable(dingosdk_multiplayer_bandwidth_tests
        Extension/Multiplayer/Test/bandwidth_tests.cpp Extension/Multiplayer/Net/protocol.cpp Extension/Multiplayer/Remote/playback_buffers.cpp
        Extension/Multiplayer/Net/wire_codec.cpp Extension/Multiplayer/Net/delta_codec.cpp)
    target_link_libraries(dingosdk_multiplayer_bandwidth_tests PRIVATE dingosdk_lz4 dingosdk_zstd)
    add_test(NAME multiplayer_bandwidth COMMAND dingosdk_multiplayer_bandwidth_tests)
    add_executable(dingosdk_multiplayer_lane_tests Extension/Multiplayer/Test/steam_lanes_tests.cpp)
    target_include_directories(dingosdk_multiplayer_lane_tests SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/External/steam_networking")
    add_test(NAME multiplayer_lanes COMMAND dingosdk_multiplayer_lane_tests)
    add_executable(dingosdk_multiplayer_prediction_tests
        Extension/Multiplayer/Test/prediction_tests.cpp Extension/Multiplayer/Net/protocol.cpp Extension/Multiplayer/Remote/playback_buffers.cpp)
    add_test(NAME multiplayer_prediction COMMAND dingosdk_multiplayer_prediction_tests)
endif()
if(DINGOSDK_BUILD_MULTIPLAYER_TESTS AND WIN32)
    if(EXISTS "${PROJECT_SOURCE_DIR}/tests/first_person_spring_tests.cpp")
        add_executable(dingosdk_first_person_spring_tests tests/first_person_spring_tests.cpp)
        target_include_directories(dingosdk_first_person_spring_tests PRIVATE "${PROJECT_SOURCE_DIR}")
        add_test(NAME first_person_spring COMMAND dingosdk_first_person_spring_tests)
    endif()
    add_executable(dingosdk_playstation_report_tests Engine/Game/Input/Test/playstation_report_tests.cpp)
    target_include_directories(dingosdk_playstation_report_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME playstation_report COMMAND dingosdk_playstation_report_tests)
    add_executable(dingosdk_physics_tuning_tests Extension/Skater/Test/physics_tuning_tests.cpp
        Extension/Skater/physics_tuning_model.cpp)
    target_include_directories(dingosdk_physics_tuning_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    target_link_libraries(dingosdk_physics_tuning_tests PRIVATE dingosdk_game_archives)
    add_test(NAME physics_tuning COMMAND dingosdk_physics_tuning_tests "${DINGOSDK_TEST_GAME_ROOT}")
    add_executable(dingosdk_camera_observer_tests Extension/Skater/Test/camera_observer_tests.cpp)
    add_test(NAME camera_observer_recovery COMMAND dingosdk_camera_observer_tests)
    add_executable(dingosdk_native_menu_view_tests Extension/UI/NativeMenu/Test/native_menu_view_tests.cpp)
    add_executable(dingosdk_loading_screen_tests Extension/World/Test/loading_screen_tests.cpp)
    target_include_directories(dingosdk_loading_screen_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME loading_screen_selection COMMAND dingosdk_loading_screen_tests)
    add_test(NAME native_menu_browser COMMAND dingosdk_native_menu_view_tests)
    add_executable(dingosdk_native_tools_view_tests Extension/UI/NativeMenu/Test/native_tools_view_tests.cpp)
    target_include_directories(dingosdk_native_tools_view_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME native_menu_tools COMMAND dingosdk_native_tools_view_tests)
    add_executable(dingosdk_native_menu_lifetime_tests Extension/UI/NativeMenu/Test/native_menu_lifetime_tests.cpp)
    add_test(NAME native_menu_lifetime COMMAND dingosdk_native_menu_lifetime_tests)
    add_executable(dingosdk_native_callback_tests Engine/Game/Abi/Test/native_callback_queue_tests.cpp)
    target_include_directories(dingosdk_native_callback_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME native_callback_lifetime COMMAND dingosdk_native_callback_tests)
    add_executable(dingosdk_steam_friend_join_tests Extension/Multiplayer/Test/steam_friend_join_tests.cpp)
    add_test(NAME steam_friend_join COMMAND dingosdk_steam_friend_join_tests)
    add_executable(dingosdk_multiplayer_tests
        Extension/Multiplayer/Test/protocol_tests.cpp Extension/Multiplayer/Net/protocol.cpp Extension/Multiplayer/Remote/playback_buffers.cpp
        Extension/Multiplayer/Net/wire_codec.cpp Extension/Multiplayer/Steam/steam_server_browser.cpp Server/steam_server.cpp
        Extension/Throwdowns/throwdown_wire.cpp)
    target_link_libraries(dingosdk_multiplayer_tests PRIVATE dingosdk_lz4 dingosdk_zstd dingosdk_logging dingosdk_word_filter)
    add_test(NAME multiplayer_protocol COMMAND dingosdk_multiplayer_tests)
    add_executable(dingosdk_throwdown_relay_tests Extension/Throwdowns/Test/throwdown_relay_tests.cpp
        Extension/Throwdowns/throwdown_relay.cpp Extension/Throwdowns/throwdown_wire.cpp)
    target_include_directories(dingosdk_throwdown_relay_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    target_link_libraries(dingosdk_throwdown_relay_tests PRIVATE dingosdk_logging)
    add_test(NAME throwdown_relay COMMAND dingosdk_throwdown_relay_tests)
    add_executable(dingosdk_multiplayer_native_tests
        Extension/Multiplayer/Test/native_layout_tests.cpp Extension/Multiplayer/Net/protocol.cpp)
    add_test(NAME multiplayer_native_layout COMMAND dingosdk_multiplayer_native_tests)
    add_executable(dingosdk_multiplayer_lobby_tests
        Extension/Multiplayer/Test/lobby_tests.cpp Extension/Multiplayer/Steam/steam_lobbies.cpp
        Extension/Multiplayer/Net/protocol.cpp)
    target_link_libraries(dingosdk_multiplayer_lobby_tests PRIVATE dingosdk_word_filter)
    add_test(NAME multiplayer_lobbies COMMAND dingosdk_multiplayer_lobby_tests)
    add_executable(dingosdk_multiplayer_mesh_tests
        Extension/Multiplayer/Test/mesh_tests.cpp Extension/Multiplayer/Net/protocol.cpp Extension/Multiplayer/Remote/playback_buffers.cpp
        Extension/Multiplayer/Net/wire_codec.cpp Extension/Multiplayer/Net/delta_codec.cpp
        Extension/Multiplayer/Session/password.cpp Extension/Multiplayer/Session/party_book.cpp
        Extension/Multiplayer/Steam/steam_lobbies.cpp Extension/Multiplayer/Steam/steam_server_browser.cpp)
    target_link_libraries(dingosdk_multiplayer_mesh_tests PRIVATE dingosdk_logging dingosdk_launcher_support dingosdk_lz4 dingosdk_zstd bcrypt
        dingosdk_word_filter)
    add_test(NAME multiplayer_mesh COMMAND dingosdk_multiplayer_mesh_tests)
    add_executable(dingosdk_settings_lock_tests Engine/Game/Settings/Test/multiplayer_settings_lock_tests.cpp)
    target_include_directories(dingosdk_settings_lock_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME multiplayer_settings_lock COMMAND dingosdk_settings_lock_tests)
    add_executable(dingosdk_held_input_tests Extension/UI/Overlay/Test/held_input_tests.cpp)
    add_test(NAME overlay_held_input COMMAND dingosdk_held_input_tests)
endif()
# The route and logging tests live in the local, untracked tests/ folder.
option(DINGOSDK_BUILD_ROUTE_TESTS "Build native route cycle regression tests" OFF)
if(DINGOSDK_BUILD_ROUTE_TESTS AND EXISTS "${PROJECT_SOURCE_DIR}/tests/route_cycle_tests.cpp")
    enable_testing()
    add_executable(dingosdk_route_cycle_tests tests/route_cycle_tests.cpp)
    target_include_directories(dingosdk_route_cycle_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME route_cycle_guard COMMAND dingosdk_route_cycle_tests)
    add_executable(dingosdk_route_endpoint_tests tests/route_endpoint_tests.cpp)
    target_link_libraries(dingosdk_route_endpoint_tests PRIVATE dingosdk_hooks)
    add_test(NAME route_endpoint_guard COMMAND dingosdk_route_endpoint_tests)
endif()
if(DINGOSDK_BUILD_TESTS AND EXISTS "${PROJECT_SOURCE_DIR}/tests/logging_tests.cpp")
    enable_testing()
    add_executable(dingosdk_logging_tests tests/logging_tests.cpp)
    target_link_libraries(dingosdk_logging_tests PRIVATE dingosdk_logging)
    add_test(NAME logging_regressions COMMAND dingosdk_logging_tests)
endif()

# Header-only game contracts belong to the runtime's subsystem tree.
if(WIN32)
    file(GLOB_RECURSE game_headers CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/Engine/Game/*.h")
    target_sources(dingosdk_runtime PRIVATE ${game_headers})
endif()
if(DINGOSDK_BUILD_TRAINER_TESTS)
    enable_testing()
    add_executable(dingosdk_trainer_tuning_dump Extension/Trainer/Test/tuning_dump.cpp Extension/Skater/physics_tuning_model.cpp)
    target_include_directories(dingosdk_trainer_tuning_dump PRIVATE "${PROJECT_SOURCE_DIR}")
    target_link_libraries(dingosdk_trainer_tuning_dump PRIVATE dingosdk_game_archives)
    # Fails when the game's data stops naming a tuning value (the trainer's table comes from those names).
    add_test(NAME trainer_tuning_names COMMAND dingosdk_trainer_tuning_dump "${DINGOSDK_TEST_GAME_ROOT}")
    # What a session's host shares beyond its tuning: the encoding, and what a guest refuses.
    add_executable(dingosdk_trainer_session_tests Extension/Trainer/Test/session_extras_tests.cpp Extension/Trainer/trainer_session.cpp)
    target_include_directories(dingosdk_trainer_session_tests PRIVATE "${PROJECT_SOURCE_DIR}")
    add_test(NAME trainer_session_extras COMMAND dingosdk_trainer_session_tests)
    add_executable(dingosdk_trainer_asset_list Extension/Trainer/Test/asset_list.cpp)
    target_include_directories(dingosdk_trainer_asset_list PRIVATE "${PROJECT_SOURCE_DIR}")
    target_link_libraries(dingosdk_trainer_asset_list PRIVATE dingosdk_game_archives)
endif()
get_property(project_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
foreach(target IN LISTS project_targets)
    if(NOT target MATCHES "^(dingosdk_imgui|dingosdk_detours|dingosdk_sqlite|dingosdk_lz4|dingosdk_zstd|dingosdk_miniz)$")
        dingosdk_configure_target(${target})
    endif()
endforeach()
