cmake_minimum_required(VERSION 3.25)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
project(fire_emblem_recomp LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

get_filename_component(FEFW_WORK_DEFAULT "${CMAKE_CURRENT_SOURCE_DIR}/../../work" ABSOLUTE)
if(DEFINED FEFW_WORK)
    file(TO_CMAKE_PATH "${FEFW_WORK}" FEFW_WORK_DEFAULT)
endif()
set(FEFW_ENGINE "${FEFW_WORK_DEFAULT}/engine" CACHE PATH "Pinned pokopia-recomp checkout with the supplied patch")
set(FEFW_LIFT_DIR "${FEFW_WORK_DEFAULT}/lift" CACHE PATH "Private generated C++ (tools/translate.py lift)")
set(FEFW_EXEFS "${FEFW_WORK_DEFAULT}/prepared/exefs" CACHE PATH "Private normalized ExeFS")
set(FEFW_LIFT_OPT "/Od" CACHE STRING "Generated-code optimization: /Od for bring-up, /O1 for performance builds")
set(NXRT_DEPS_DIR "${FEFW_WORK_DEFAULT}/downloads/nxrt" CACHE PATH "Private third-party archive cache")
if(NOT WIN32 OR NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR NOT MSVC)
    message(FATAL_ERROR "Requires Windows x64, clang-cl, MSVC C++ libraries and Windows SDK")
endif()

set(NXRT_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(NXRT_BUILD_GAMES OFF CACHE BOOL "" FORCE)
set(NXRT_BUILD_GFX ON CACHE BOOL "" FORCE)
set(NXRT_RECOMP_INCLUDE "${FEFW_ENGINE}/recomp/include" CACHE PATH "" FORCE)
set(_fefw_vpx "${FEFW_WORK_DEFAULT}/toolchains/vcpkg/installed/x64-windows-static")
if(NOT NXRT_LIBVPX_ROOT AND EXISTS "${_fefw_vpx}/lib/vpx.lib")
    set(NXRT_LIBVPX_ROOT "${_fefw_vpx}" CACHE PATH "Private host video decoder" FORCE)
endif()

file(READ "${CMAKE_CURRENT_SOURCE_DIR}/supported-build.json" FEFW_SUPPORTED_JSON)
string(JSON FEFW_SUPPORTED_TITLE GET "${FEFW_SUPPORTED_JSON}" program_id)
set(FEFW_SUPPORTED_MODULES "")
foreach(module main sdk subsdk0 subsdk1 subsdk2 rtld)
    string(JSON module_sha GET "${FEFW_SUPPORTED_JSON}" modules ${module} sha256)
    string(JSON module_build GET "${FEFW_SUPPORTED_JSON}" modules ${module} build_id)
    string(APPEND FEFW_SUPPORTED_MODULES "    {\"${module}\", \"${module_sha}\", \"${module_build}\"},\n")
endforeach()
configure_file(src/fefw_supported.h.in "${CMAKE_CURRENT_BINARY_DIR}/fefw_supported.h" @ONLY)
add_library(fefw_config STATIC src/fefw_config.cpp)
target_include_directories(fefw_config PUBLIC src "${CMAKE_CURRENT_BINARY_DIR}")
target_compile_options(fefw_config PUBLIC /utf-8)
target_compile_definitions(fefw_config PUBLIC NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS UNICODE _UNICODE)
target_link_libraries(fefw_config PUBLIC bcrypt)
enable_language(RC)
add_executable(fefw_setup WIN32 src/fefw_setup.cpp src/fefw_setup.rc)
target_link_libraries(fefw_setup PRIVATE fefw_config comctl32 ole32 shell32 uxtheme dwmapi)
target_link_options(fefw_setup PRIVATE /MANIFEST:NO)
enable_testing()
add_executable(fefw_config_tests tests/test_config.cpp)
target_link_libraries(fefw_config_tests PRIVATE fefw_config)
add_test(NAME fefw_config COMMAND fefw_config_tests)
add_library(fefw_mods STATIC src/fefw_mods.cpp)
target_include_directories(fefw_mods PUBLIC src)
add_executable(fefw_mods_tests tests/test_mods.cpp)
target_link_libraries(fefw_mods_tests PRIVATE fefw_mods)
add_test(NAME fefw_mods COMMAND fefw_mods_tests)
add_test(NAME fefw_updates COMMAND powershell -NoProfile -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_SOURCE_DIR}/tests/check_updates.ps1" -Work "${CMAKE_CURRENT_BINARY_DIR}")
option(FEFW_BUILD_GAME "Build the private translated game; OFF builds only the distributable configurator" ON)
if(NOT FEFW_BUILD_GAME)
    return()
endif()
add_subdirectory("${FEFW_ENGINE}/runtime" "${CMAKE_CURRENT_BINARY_DIR}/runtime")

option(FEFW_BUILD_RUNTIME_TESTS "Build memory and thread regression tests" ON)
if(FEFW_BUILD_RUNTIME_TESTS)
    add_executable(fefw_runtime_tests
        tests/test_audio_revision.cpp
        tests/test_applet22.cpp
        "${FEFW_ENGINE}/runtime/tests/test_main.cpp"
        "${FEFW_ENGINE}/runtime/tests/test_svc_memory.cpp"
        "${FEFW_ENGINE}/runtime/tests/test_svc_audren_types.cpp"
        "${FEFW_ENGINE}/runtime/tests/test_kernel_threads.cpp")
    target_include_directories(fefw_runtime_tests PRIVATE "${FEFW_ENGINE}/runtime/tests" "${FEFW_ENGINE}/runtime/src")
    target_compile_definitions(fefw_runtime_tests PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN
        _CRT_SECURE_NO_WARNINGS _WIN32_WINNT=0x0A00
        NXRT_TEST_DATA_DIR="${FEFW_ENGINE}/runtime/tests/data")
    target_link_libraries(fefw_runtime_tests PRIVATE nxrt_gfx nxrt)
    target_link_options(fefw_runtime_tests PRIVATE /STACK:67108864 /HIGHENTROPYVA:NO)
    enable_testing()
    add_executable(fefw_video_tests tests/test_av1.cpp "${FEFW_ENGINE}/runtime/tests/test_main.cpp")
    target_include_directories(fefw_video_tests PRIVATE "${FEFW_ENGINE}/runtime/tests")
    target_compile_definitions(fefw_video_tests PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS)
    target_link_libraries(fefw_video_tests PRIVATE nxrt_gfx nxrt)
    target_link_options(fefw_video_tests PRIVATE /STACK:67108864 /HIGHENTROPYVA:NO)
    add_test(NAME fefw_memory COMMAND fefw_runtime_tests svc_memory)
    add_test(NAME fefw_threads COMMAND fefw_runtime_tests kernel_threads)
    add_test(NAME fefw_audio_revision COMMAND fefw_runtime_tests audio_revision)
    add_test(NAME fefw_applet22 COMMAND fefw_runtime_tests applet22)
    add_test(NAME fefw_audio_types COMMAND fefw_runtime_tests svc_audren_types)

    add_executable(fefw_input_tests tests/test_interactive_input.cpp src/fefw_game.cpp "${FEFW_ENGINE}/runtime/tests/test_main.cpp")
    target_include_directories(fefw_input_tests PRIVATE src "${FEFW_ENGINE}/runtime/tests" "${FEFW_ENGINE}/runtime/src/gfx")
    target_compile_definitions(fefw_input_tests PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS)
    target_link_libraries(fefw_input_tests PRIVATE nxrt_gfx nxrt)
    target_link_options(fefw_input_tests PRIVATE /STACK:67108864 /HIGHENTROPYVA:NO)
    add_test(NAME fefw_interactive_input COMMAND fefw_input_tests interactive_input)

    add_executable(fefw_numeric_tests tests/test_compute_numeric.cpp "${FEFW_ENGINE}/runtime/tests/test_main.cpp")
    target_include_directories(fefw_numeric_tests PRIVATE "${FEFW_ENGINE}/runtime/tests" "${FEFW_ENGINE}/runtime/src/gfx" "${FEFW_ENGINE}/runtime/src/gfx/nvn")
    target_compile_definitions(fefw_numeric_tests PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS)
    target_link_libraries(fefw_numeric_tests PRIVATE nxrt_gfx nxrt)
    target_link_options(fefw_numeric_tests PRIVATE /STACK:67108864 /HIGHENTROPYVA:NO)

    add_executable(fefw_compute_tests tests/test_compute_constants.cpp "${FEFW_ENGINE}/runtime/tests/test_main.cpp")
    target_include_directories(fefw_compute_tests PRIVATE "${FEFW_ENGINE}/runtime/tests" "${FEFW_ENGINE}/runtime/src/gfx" "${FEFW_ENGINE}/runtime/src/gfx/nvn")
    target_compile_definitions(fefw_compute_tests PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS
        FEFW_COMPUTE_SPV_DIR="${FEFW_WORK_DEFAULT}/tests/compute-c0")
    target_link_libraries(fefw_compute_tests PRIVATE nxrt_gfx nxrt)
    target_link_options(fefw_compute_tests PRIVATE /STACK:67108864 /HIGHENTROPYVA:NO)

    add_executable(fefw_array_tests tests/test_texture_arrays.cpp tests/test_root_constants.cpp tests/test_gpu_validation.cpp tests/test_temporal_upscaler.cpp tests/test_pc_runtime.cpp "${FEFW_ENGINE}/runtime/tests/test_main.cpp")
    target_include_directories(fefw_array_tests PRIVATE "${FEFW_ENGINE}/runtime/tests" "${FEFW_ENGINE}/runtime/tests/gfx"
        "${FEFW_ENGINE}/runtime/src/gfx" "${FEFW_ENGINE}/runtime/src/gfx/nvn")
    target_compile_definitions(fefw_array_tests PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS
        FEFW_ARRAY_CACHE_DIR="${FEFW_WORK_DEFAULT}/tests/texture-arrays")
    target_link_libraries(fefw_array_tests PRIVATE nxrt_gfx nxrt)
    target_link_options(fefw_array_tests PRIVATE /STACK:67108864 /HIGHENTROPYVA:NO)
    add_test(NAME fefw_temporal COMMAND fefw_array_tests gfx_nvn_dlss)
    add_test(NAME fefw_pc_runtime COMMAND fefw_array_tests fefw_pc)
endif()

set(FEFW_MODULE_TARGETS "")
foreach(module main sdk subsdk0 subsdk1 subsdk2 rtld)
    file(GLOB sources CONFIGURE_DEPENDS "${FEFW_LIFT_DIR}/${module}/c_*.cpp")
    set(descriptor "${FEFW_LIFT_DIR}/${module}/module_${module}.cpp")
    if(NOT sources OR NOT EXISTS "${descriptor}")
        message(FATAL_ERROR "Missing generated ${module}: run tools/translate.py lift first")
    endif()
    add_library(fefw_${module} STATIC ${sources} "${descriptor}")
    target_include_directories(fefw_${module} PUBLIC "${FEFW_ENGINE}/recomp/include" "${FEFW_LIFT_DIR}/${module}")
    target_compile_options(fefw_${module} PRIVATE ${FEFW_LIFT_OPT} /w /EHs-c- /GR- -fno-strict-aliasing -ffp-contract=off -mcx16)
    list(APPEND FEFW_MODULE_TARGETS fefw_${module})
endforeach()

add_executable(fefw src/fefw_main.cpp src/fefw_game.cpp src/fefw_pc.cpp src/fefw_resolution.cpp)
add_dependencies(fefw fefw_setup)
target_include_directories(fefw PRIVATE "${FEFW_ENGINE}/runtime/include" "${FEFW_ENGINE}/runtime/src/gfx" src)
target_compile_definitions(fefw PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS
    FEFW_EXEFS_DEFAULT="${FEFW_EXEFS}" FEFW_VERSION="experimental")
target_link_libraries(fefw PRIVATE ${FEFW_MODULE_TARGETS} nxrt_gfx nxrt fefw_config fefw_mods)
target_link_options(fefw PRIVATE /MAP /STACK:134217728 /HIGHENTROPYVA:NO /DEBUG)
option(FEFW_GUI_SUBSYSTEM "Launch the game without a console window" ON)
if(FEFW_GUI_SUBSYSTEM)
    target_compile_definitions(fefw PRIVATE FEFW_GUI_SUBSYSTEM)
    target_link_options(fefw PRIVATE /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup)
endif()
add_custom_command(TARGET fefw POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:fefw>/fonts"
    COMMAND ${CMAKE_COMMAND} -E copy_if_different "${FEFW_ENGINE}/runtime/assets/fonts/DejaVuSans.ttf" "$<TARGET_FILE_DIR:fefw>/fonts/DejaVuSans.ttf")
file(GLOB decoder_dlls "${NXRT_A2B_DIR}/astcdec-*-shared.dll")
foreach(dll IN LISTS decoder_dlls)
    add_custom_command(TARGET fefw POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${dll}" "$<TARGET_FILE_DIR:fefw>")
endforeach()
