cmake_minimum_required(VERSION 3.16)
project(foxranger C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
if (NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif()
add_compile_definitions(SDL_MAIN_HANDLED)
if (MSVC)
  add_compile_options(/W3 /wd4201)
  add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
  set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
else()
  add_compile_options(-Wall -Wextra)
endif()

# runtime core: memory map, VGA, ports, far-call table, loader, native replacements
find_package(SDL2 CONFIG REQUIRED)

# Nuked-OPL3 (LGPL-2.1-or-later, unmodified, see third_party/nuked-opl3/LICENSE).
# Kept as a separate library so it can be shipped as a replaceable shared object.
option(FOX_OPL_SHARED "build Nuked-OPL3 as a shared library (LGPL relinking)" OFF)
if (FOX_OPL_SHARED)
  add_library(nukedopl3 SHARED third_party/nuked-opl3/opl3.c)
else()
  add_library(nukedopl3 STATIC third_party/nuked-opl3/opl3.c)
endif()
target_include_directories(nukedopl3 PUBLIC third_party/nuked-opl3)
set_target_properties(nukedopl3 PROPERTIES POSITION_INDEPENDENT_CODE ON)

add_library(foxcore STATIC
  src/mem.c src/vga_modex.c src/io.c src/cpu.c src/mz_load.c src/heap.c
  src/blit_10a7.c src/assets.c src/audio_opl.c src/ctvoice.c src/dos.c
  src/crt_shims.c src/recomp_rt.c src/platform.c src/natives.c src/cheat.c
  src/retro_font.c src/launcher_config.c src/ingest.c src/native_dialog.c src/launcher.c
  src/enhanced_music.c)
target_include_directories(foxcore PUBLIC src third_party/stb_vorbis)
set_target_properties(foxcore PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_link_libraries(foxcore PUBLIC nukedopl3 SDL2::SDL2)
if (APPLE)
  set_source_files_properties(src/native_dialog.c PROPERTIES COMPILE_FLAGS "-x objective-c")
  target_link_libraries(foxcore PUBLIC "-framework Cocoa")
elseif (WIN32)
  target_link_libraries(foxcore PUBLIC comdlg32 shell32 ole32)
endif()
if (NOT MSVC)
  target_link_libraries(foxcore PUBLIC m)
endif()

# recompiled game code (generated by tools/recomp.py into gen/)
file(GLOB FOX_GEN_SOURCES CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/gen/recomp_*.c)
if (FOX_GEN_SOURCES)
  add_library(foxgame STATIC ${FOX_GEN_SOURCES})
  target_include_directories(foxgame PUBLIC gen)
  target_link_libraries(foxgame PUBLIC foxcore)
  set_target_properties(foxgame PROPERTIES POSITION_INDEPENDENT_CODE ON)
  if (NOT MSVC)
    target_compile_options(foxgame PRIVATE -Wno-unused-label)
  endif()
endif()
# the game: recompiled code + runtime
add_executable(foxrt src/main_sdl.c)
if (TARGET SDL2::SDL2main)
  target_link_libraries(foxrt PRIVATE SDL2::SDL2main)
endif()
target_link_libraries(foxrt PRIVATE foxgame foxcore SDL2::SDL2)

if (APPLE)
  set_target_properties(foxrt PROPERTIES
    MACOSX_BUNDLE TRUE
    MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/packaging/Info.plist.in"
    MACOSX_BUNDLE_BUNDLE_NAME "Fox Ranger"
    MACOSX_BUNDLE_BUNDLE_VERSION "1.0.0"
    MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0.0"
    MACOSX_BUNDLE_GUI_IDENTIFIER "com.softaction.foxranger"
    MACOSX_BUNDLE_ICON_FILE "FoxRanger.icns"
    OUTPUT_NAME "FoxRanger"
    BUILD_WITH_INSTALL_RPATH TRUE
    INSTALL_RPATH "@executable_path/../Frameworks"
  )
  set_source_files_properties(packaging/FoxRanger.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
  target_sources(foxrt PRIVATE packaging/FoxRanger.icns)

  add_custom_command(TARGET foxrt POST_BUILD
    COMMAND "${CMAKE_SOURCE_DIR}/packaging/bundle_macos.sh" "$<TARGET_BUNDLE_DIR:foxrt>"
    COMMENT "Bundling SDL2 dependencies and signing FoxRanger.app"
  )
endif()

# shared library for the Python differential tests (ctypes)
add_library(foxrt_test SHARED tests/testhooks.c)
target_link_libraries(foxrt_test PRIVATE foxcore)
if (APPLE)
  target_link_options(foxrt_test PRIVATE -Wl,-all_load)
elseif (UNIX)
  target_link_options(foxrt_test PRIVATE -Wl,--whole-archive $<TARGET_FILE:foxcore> -Wl,--no-whole-archive)
endif()

# shared library for tests/diff_fn.py: generated code built with loop budgets
if (FOX_GEN_SOURCES AND (NOT WIN32 OR FOX_PYTHON))
  add_library(foxrt_fn_test SHARED tests/testhooks.c tests/testhooks_fn.c ${FOX_GEN_SOURCES})
  target_include_directories(foxrt_fn_test PRIVATE gen)
  target_compile_definitions(foxrt_fn_test PRIVATE RT_BUDGET)
  target_link_libraries(foxrt_fn_test PRIVATE foxcore)
  if (NOT MSVC)
    target_compile_options(foxrt_fn_test PRIVATE -Wno-unused-label)
  endif()
  if (APPLE)
    target_link_options(foxrt_fn_test PRIVATE -Wl,-all_load)
  elseif (UNIX)
    target_link_options(foxrt_fn_test PRIVATE -Wl,--whole-archive $<TARGET_FILE:foxcore> -Wl,--no-whole-archive)
  endif()
endif()

# same, with per-instruction tracing for `diff_fn.py --trace`
if (FOX_GEN_SOURCES AND (NOT WIN32 OR FOX_PYTHON))
  add_library(foxrt_fn_trace SHARED tests/testhooks.c tests/testhooks_fn.c ${FOX_GEN_SOURCES})
  target_include_directories(foxrt_fn_trace PRIVATE gen)
  target_compile_definitions(foxrt_fn_trace PRIVATE RT_BUDGET RT_TRACE)
  target_link_libraries(foxrt_fn_trace PRIVATE foxcore)
  if (NOT MSVC)
    target_compile_options(foxrt_fn_trace PRIVATE -Wno-unused-label -O1)
  endif()
  if (APPLE)
    target_link_options(foxrt_fn_trace PRIVATE -Wl,-all_load)
  elseif (UNIX)
    target_link_options(foxrt_fn_trace PRIVATE -Wl,--whole-archive $<TARGET_FILE:foxcore> -Wl,--no-whole-archive)
  endif()
endif()

if (FOX_GEN_SOURCES)
  add_executable(test_audio tests/test_audio.c)
  target_link_libraries(test_audio PRIVATE foxgame foxcore)
endif()

enable_testing()
set(FOX_PYTHON "" CACHE STRING "python with unicorn>=2 for differential tests (venv path kept as-is)")
set(FOX_GAME_DIR "${CMAKE_SOURCE_DIR}/fox-ranger" CACHE PATH "original game files")
set(FOX_UNPACKED_EXE "${CMAKE_SOURCE_DIR}/work/FOX_UNP2.EXE" CACHE FILEPATH "unpacked FOX.EXE")
if (FOX_GEN_SOURCES)
  if (EXISTS "${FOX_GAME_DIR}" AND EXISTS "${FOX_UNPACKED_EXE}")
    add_test(NAME audio_sfx_opl COMMAND test_audio ${FOX_GAME_DIR} ${FOX_UNPACKED_EXE})
  endif()
  find_package(Python3 COMPONENTS Interpreter)
  if (Python3_FOUND AND EXISTS "${FOX_GAME_DIR}" AND EXISTS "${CMAKE_SOURCE_DIR}/work/FOX_EN_FINAL.EXE")
    add_test(NAME boot_title_stage1
             COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/boot_smoke.py $<TARGET_FILE:foxrt>
                     ${FOX_GAME_DIR} ${CMAKE_SOURCE_DIR}/work/FOX_EN_FINAL.EXE)
  endif()
endif()
if (FOX_PYTHON AND EXISTS "${FOX_GAME_DIR}" AND EXISTS "${FOX_UNPACKED_EXE}")
  add_test(NAME diff_blit_10a7
           COMMAND ${FOX_PYTHON} -I ${CMAKE_SOURCE_DIR}/tests/diff_blit.py
                   ${FOX_GAME_DIR} ${FOX_UNPACKED_EXE} $<TARGET_FILE:foxrt_test> --cases 500)
  if (FOX_GEN_SOURCES AND EXISTS "${CMAKE_SOURCE_DIR}/work/functions.csv" AND EXISTS "${CMAKE_SOURCE_DIR}/work/function_modules.csv")
    add_test(NAME diff_fn_game
             COMMAND ${FOX_PYTHON} -I ${CMAKE_SOURCE_DIR}/tests/diff_fn.py
                     ${FOX_GAME_DIR} ${FOX_UNPACKED_EXE} $<TARGET_FILE:foxrt_fn_test>
                     ${CMAKE_SOURCE_DIR}/work/functions.csv ${CMAKE_SOURCE_DIR}/work/function_modules.csv
                     --modules game_loop,game_logic,audio_sfx,audio_music,story_ui,system_video,file_io,platform_timer,platform_input
                     --trials 5)
    add_test(NAME diff_crt_shims
             COMMAND ${FOX_PYTHON} -I ${CMAKE_SOURCE_DIR}/tests/diff_crt.py
                     ${FOX_GAME_DIR} ${FOX_UNPACKED_EXE} $<TARGET_FILE:foxrt_fn_test>
                     ${CMAKE_SOURCE_DIR}/work/functions.csv ${CMAKE_SOURCE_DIR}/work/function_modules.csv
                     --cases 600)
  endif()
endif()

# -----------------------------------------------------------------------------
# Packaging / CPack (macOS .dmg/.zip, Windows .zip, Linux .tar.gz)
# -----------------------------------------------------------------------------
set(CPACK_PACKAGE_NAME "FoxRanger")
set(CPACK_PACKAGE_VENDOR "Soft Action / Fox Ranger Recomp Project")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Fox Ranger (1992) Enhanced Native Recompilation & Translation")
set(CPACK_PACKAGE_VERSION_MAJOR 1)
set(CPACK_PACKAGE_VERSION_MINOR 0)
set(CPACK_PACKAGE_VERSION_PATCH 0)
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/packaging/README_RELEASE.md")

if (APPLE)
  install(TARGETS foxrt BUNDLE DESTINATION . COMPONENT Runtime)
  install(FILES packaging/README_RELEASE.md DESTINATION . RENAME "README.txt" COMPONENT Runtime)
  install(DIRECTORY enhanced_music DESTINATION . COMPONENT Runtime)
  install(DIRECTORY translations DESTINATION . COMPONENT Runtime)
  install(FILES packaging/foxranger.ini.default DESTINATION . RENAME "foxranger.ini" COMPONENT Runtime)

  set(CPACK_GENERATOR "DragNDrop;ZIP")
  set(CPACK_DMG_VOLUME_NAME "Fox Ranger (1992)")
  set(CPACK_DMG_FORMAT "UDZO")
elseif (WIN32)
  install(TARGETS foxrt RUNTIME DESTINATION . COMPONENT Runtime)
  install(FILES "${CMAKE_BINARY_DIR}/SDL2.dll" DESTINATION . OPTIONAL COMPONENT Runtime)
  install(FILES packaging/README_RELEASE.md DESTINATION . RENAME "README.txt" COMPONENT Runtime)
  install(DIRECTORY enhanced_music DESTINATION . COMPONENT Runtime)
  install(DIRECTORY translations DESTINATION . COMPONENT Runtime)
  install(FILES packaging/foxranger.ini.default DESTINATION . RENAME "foxranger.ini" COMPONENT Runtime)

  set(CPACK_GENERATOR "ZIP")
else ()
  install(TARGETS foxrt RUNTIME DESTINATION bin COMPONENT Runtime)
  install(FILES packaging/foxranger.desktop DESTINATION share/applications COMPONENT Runtime)
  install(FILES packaging/foxranger.png DESTINATION share/icons/hicolor/512x512/apps COMPONENT Runtime)
  install(FILES packaging/foxranger_256.png DESTINATION share/icons/hicolor/256x256/apps RENAME "foxranger.png" COMPONENT Runtime)
  install(FILES packaging/README_RELEASE.md DESTINATION share/doc/foxranger RENAME "README.txt" COMPONENT Runtime)
  install(DIRECTORY enhanced_music DESTINATION share/foxranger COMPONENT Runtime)
  install(DIRECTORY translations DESTINATION share/foxranger COMPONENT Runtime)
  install(FILES packaging/foxranger.ini.default DESTINATION share/foxranger RENAME "foxranger.ini" COMPONENT Runtime)

  set(CPACK_GENERATOR "TGZ;ZIP")
endif()

include(CPack)
