cmake_minimum_required(VERSION 3.20)

if(PLAT_MACOS_X86_64)
    set(CMAKE_OSX_ARCHITECTURES "x86_64")

    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

    set(GLEW_PATH "/usr/local/opt/glew")
    set(CMAKE_IGNORE_PATH "/opt/homebrew")
    set(CMAKE_IGNORE_PATH "/opt/homebrew/include")
    set(CMAKE_IGNORE_PATH "/opt/homebrew/lib")

    list(APPEND CMAKE_PREFIX_PATH /usr/local)
elseif(PLAT_MACOS)
    set(CMAKE_OSX_ARCHITECTURES "arm64")
endif()

# Generated by scripts/increment_version.py to keep Flatpak
# and binary versions all in sync.
include(cmake_modules/version.cmake)
project(OpenJKDF2 VERSION ${OPENJKDF2_PROJECT_VERSION})

# All of our supported platforms
set(PLAT_LINUX_32 FALSE CACHE BOOL "Linux 32-bit target")
set(PLAT_LINUX_64 FALSE CACHE BOOL "Linux 64-bit target")
set(PLAT_MACOS FALSE CACHE BOOL "MacOS target")
set(PLAT_MACOS_X86_64 FALSE CACHE BOOL "MacOS x86_64")
set(PLAT_MINGW_X86_64 FALSE CACHE BOOL "MinGW Win64 target")
set(PLAT_MINGW_X86_32 FALSE CACHE BOOL "MinGW Win32 (Windows XP/OpenGL 1.1) target")
set(PLAT_WASM FALSE CACHE BOOL "Emscripten WASM target")
set(PLAT_HOOKS FALSE CACHE BOOL "Hook DLL target")
set(PLAT_MSVC FALSE CACHE BOOL "Windows 64-bit MSVC")
set(PLAT_ANDROID_ARM64 FALSE CACHE BOOL "Android AArch64")
set(PLAT_TWL FALSE CACHE BOOL "Nintendo DSi")
set(PLAT_DREAMCAST FALSE CACHE BOOL "Sega Dreamcast (KallistiOS)")
set(PLAT_AUTO FALSE CACHE BOOL "Target the host OS")

# Set PLAT_AUTO if a platform isn't specifically selected
if(NOT PLAT_LINUX_32 AND NOT PLAT_LINUX_64 AND NOT PLAT_MACOS AND NOT PLAT_MACOS_X86_64 AND NOT PLAT_MINGW_X86_64 AND NOT PLAT_MINGW_X86_32 AND NOT PLAT_WASM AND NOT PLAT_HOOKS AND NOT PLAT_TWL AND NOT PLAT_DREAMCAST)
    set(PLAT_AUTO TRUE)
endif()

# Internal targets: Stuff like OpenGL vs D3D, OpenAL vs null sound, etc
set(TARGET_COMPILE_FREEGLUT FALSE)
set(TARGET_USE_CURL FALSE)
set(TARGET_USE_PHYSFS FALSE)
set(TARGET_USE_BASICSOCKETS FALSE)
set(TARGET_USE_GAMENETWORKINGSOCKETS FALSE)
set(TARGET_USE_LIBSMACKER FALSE)
set(TARGET_USE_LIBSMUSHER FALSE)
set(TARGET_USE_SDL2 FALSE)
set(TARGET_USE_OPENGL FALSE)
set(TARGET_USE_OPENGL11 FALSE)
set(TARGET_USE_OPENAL FALSE)
set(TARGET_USE_D3D FALSE)
set(TARGET_POSIX FALSE)
set(TARGET_LINUX FALSE)
set(TARGET_MACOS FALSE)
set(TARGET_WIN32 FALSE)
set(TARGET_WASM FALSE)
set(TARGET_ANDROID FALSE)
set(TARGET_ANDROID_ARM64 FALSE)
set(TARGET_TWL FALSE)
set(TARGET_DREAMCAST FALSE)
set(TARGET_HOOKS FALSE)
set(TARGET_NO_BLOBS FALSE)
set(TARGET_CAN_JKGM FALSE)
set(TARGET_NO_MULTIPLAYER_MENUS FALSE)
set(OPENJKDF2_USE_BLOBS FALSE CACHE BOOL "Use blobs")

# Options for compiling
if(DEFINED ENV{TARGET_BUILD_TESTS})
    set(TARGET_BUILD_TESTS $ENV{TARGET_BUILD_TESTS})
else()
    set(TARGET_BUILD_TESTS FALSE)
endif()

set(DEBUG_QOL_CHEATS $ENV{DEBUG_QOL_CHEATS})
set(EXPERIMENTAL_FIXED_POINT $ENV{EXPERIMENTAL_FIXED_POINT})

# Options for compiling
if(DEFINED ENV{OPENJKDF2_NO_ASAN})
    set(OPENJKDF2_NO_ASAN $ENV{OPENJKDF2_NO_ASAN})
else()
    set(OPENJKDF2_NO_ASAN TRUE)
endif()

if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND PLAT_AUTO)
    set(PLAT_LINUX_64 TRUE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND PLAT_AUTO)
    set(PLAT_MACOS TRUE)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows" AND PLAT_AUTO)
    set(PLAT_MSVC TRUE)
endif()

# General defs
add_compile_definitions(
    OPENJKDF2_PROJECT_NAME=${PROJECT_NAME}
    OPENJKDF2_PROJECT_VERSION=${PROJECT_VERSION}
    OPENJKDF2_MAJOR_VERSION=${PROJECT_VERSION_MAJOR}
    OPENJKDF2_MINOR_VERSION=${PROJECT_VERSION_MINOR}
    OPENJKDF2_PATCH_VERSION=${PROJECT_VERSION_PATCH}
    OPENJKDF2_TWEAK_VERSION=${PROJECT_VERSION_TWEAK}
    OPENJKDF2_RELEASE_VERSION_STRING=\"v${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"
    # _W (wchar_t/L"...") is for packaging/win32/openjkdf2.rc only -- the Windows
    # resource-script format has its own wide-string syntax, unrelated to the
    # engine's char16_t. _U (char16_t/u"...") is for the engine (src/version.c).
    OPENJKDF2_RELEASE_VERSION_STRING_W=L\"v${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"
    OPENJKDF2_RELEASE_VERSION_STRING_U=u\"v${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"
    OPENJKDF2_RELEASE_COMMIT=\"${OPENJKDF2_RELEASE_COMMIT}\"
    OPENJKDF2_RELEASE_COMMIT_SHORT=\"${OPENJKDF2_RELEASE_COMMIT_SHORT}\"
    OPENJKDF2_RELEASE_COMMIT_U=u\"${OPENJKDF2_RELEASE_COMMIT}\"
    OPENJKDF2_RELEASE_COMMIT_SHORT_U=u\"${OPENJKDF2_RELEASE_COMMIT_SHORT}\"
    QOL_IMPROVEMENTS
)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)

# Default macros
macro(plat_link_and_package)
    message(FATAL_ERROR "Unknown target! Cannot continue.")
endmacro()
macro(plat_extra_deps)
endmacro()

if(PLAT_LINUX_32)
    include(cmake_modules/plat_linux_32.cmake)
elseif(PLAT_LINUX_64)
    include(cmake_modules/plat_linux_64.cmake)
elseif(PLAT_MACOS)
    include(cmake_modules/plat_macos.cmake)
elseif(PLAT_MACOS_X86_64)
    include(cmake_modules/plat_macos_x86_64.cmake)
elseif(PLAT_MINGW_X86_64)
    include(cmake_modules/plat_mingw_x86_64.cmake)
elseif(PLAT_MINGW_X86_32)
    include(cmake_modules/plat_mingw_x86_32.cmake)
elseif(PLAT_WASM)
    include(cmake_modules/plat_wasm.cmake)
elseif(PLAT_HOOKS)
    include(cmake_modules/plat_hooks.cmake)
elseif(PLAT_ANDROID_ARM64)
    include(cmake_modules/plat_android_arm64.cmake)
elseif(PLAT_MSVC)
    include(cmake_modules/plat_msvc.cmake)
elseif(PLAT_TWL)
    include(cmake_modules/plat_twl.cmake)
elseif(PLAT_DREAMCAST)
    include(cmake_modules/plat_dreamcast.cmake)
else()
    message(FATAL_ERROR "Unknown platform! Cannot continue.")
endif()

# Establishes compile options, binary/library names, defines, etc
plat_initialize()

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

# CMake module paths
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules")
if(TARGET_LINUX OR TARGET_MACOS OR PLAT_MSVC OR TARGET_ANDROID)
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules_linux")
endif()

if(TARGET_ANDROID)
    set(GLEW_INCLUDE_DIRS "")
endif()

if(TARGET_USE_OPENAL AND TARGET_FIND_OPENAL AND NOT TARGET_MACOS)
    find_package(OpenAL REQUIRED)
endif()

# idk
set(JSON_Install OFF CACHE INTERNAL "")
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/json)

# All of the includes
include_directories(
    ${PROJECT_SOURCE_DIR}/src
    ${PROJECT_SOURCE_DIR}/src/external/libsmacker
    ${PROJECT_SOURCE_DIR}/src/external/libsmusher/src
    ${PROJECT_SOURCE_DIR}/src/external/nativefiledialog-extended
)

if(TARGET_USE_SDL2)
    include_directories(${SDL_INCLUDE_DIRS} ${SDL_MIXER_INCLUDE_DIRS})
endif()

if(TARGET_USE_OPENAL AND NOT OPENAL_COMPILING_FROM_SRC)
    include_directories(${OPENAL_INCLUDE_DIR})
endif()

# All of the base sources
file(GLOB ENGINE_SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/*.c
                       ${PROJECT_SOURCE_DIR}/src/*/*.c
                       ${PROJECT_SOURCE_DIR}/src/General/*.cpp
                       ${PROJECT_SOURCE_DIR}/src/Platform/Common/*.c
                       ${PROJECT_SOURCE_DIR}/src/Platform/Common/*.cpp)

# Game main
file(GLOB GAME_MAIN_SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/main.c)

# Remove test sources from glob
file(GLOB TEST_SRCS ${PROJECT_SOURCE_DIR}/src/Tests/*.c)
list(REMOVE_ITEM ENGINE_SOURCE_FILES ${TEST_SRCS})
list(REMOVE_ITEM ENGINE_SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/main.c)

# The software (CPU) renderer is desktop-only (RDRASTER_SOFTWARE_RENDERER, off on
# TARGET_RETRO_HOMEBREW — TWL/DC can't afford CPU rasterization). rdAFRaster.c/rdNRaster.c/
# rdZRaster.c are wholly gated on that define, so drop them from the TWL/DC build entirely
# (rdActive.c stays — its _Startup is always compiled). engine_config.h keeps the define off there
# regardless.
if(TARGET_TWL OR TARGET_DREAMCAST)
    list(REMOVE_ITEM ENGINE_SOURCE_FILES
        ${PROJECT_SOURCE_DIR}/src/Raster/rdAFRaster.c
        ${PROJECT_SOURCE_DIR}/src/Raster/rdNRaster.c
        ${PROJECT_SOURCE_DIR}/src/Raster/rdZRaster.c)
endif()

# Remove multiplayer menus from compilation
file(GLOB MULTIPLAYER_MENU_SRCS 
    ${PROJECT_SOURCE_DIR}/src/Gui/jkGUIMultiplayer.c
    ${PROJECT_SOURCE_DIR}/src/Gui/jkGUIMultTally.c
#   ${PROJECT_SOURCE_DIR}/src/Gui/jkGUINetHost.c
    ${PROJECT_SOURCE_DIR}/src/Gui/jkGUIBuildMulti.c
)
if (TARGET_NO_MULTIPLAYER_MENUS)
    list(REMOVE_ITEM ENGINE_SOURCE_FILES ${MULTIPLAYER_MENU_SRCS})
endif()

# Fcaseopen
file(GLOB FCASEOPEN_SRCS ${PROJECT_SOURCE_DIR}/src/external/fcaseopen/*.c)
list(APPEND ENGINE_SOURCE_FILES ${FCASEOPEN_SRCS})
include_directories(${PROJECT_SOURCE_DIR}/src/external/fcaseopen)

# The rest of the conditional/TARGET_* dependencies
include(cmake_modules/config_platform_deps.cmake)

# globals.c/globals.c/precompiled resources preprocessing.
# This includes selection for most of the files in src/platform/*
include(cmake_modules/precompile_globals.cmake)

#set( CMAKE_VERBOSE_MAKEFILE on )

# Force CXX compilation for all files
file(GLOB ENGINE_SOURCE_FILES_REGLOB ${PROJECT_SOURCE_DIR}/src/*.c
                       ${PROJECT_SOURCE_DIR}/src/*/*.c
                       ${PROJECT_SOURCE_DIR}/src/General/*.cpp
                       ${PROJECT_SOURCE_DIR}/src/Platform/*/*.c
                       ${PROJECT_SOURCE_DIR}/src/Platform/*/*/*.c
                       ${PROJECT_SOURCE_DIR}/src/Platform/*/*.cpp
                       ${PROJECT_SOURCE_DIR}/src/Platform/*/*/*.cpp)
list(REMOVE_ITEM ENGINE_SOURCE_FILES_REGLOB ${TEST_SRCS})
list(APPEND ENGINE_SOURCE_FILES_REGLOB ${GLOBALS_C})
if (EXPERIMENTAL_FIXED_POINT)
    set_source_files_properties(${ENGINE_SOURCE_FILES_REGLOB} PROPERTIES LANGUAGE CXX )
endif()

add_library(sith_engine OBJECT ${ENGINE_SOURCE_FILES})
# Generate cog headers (generated/globals.h) fully before compiling any engine
# source -- many include it transitively, and a parallel build otherwise races the
# generation and reads a half-written header. See precompile_globals.cmake.
add_dependencies(sith_engine generate_globals)
plat_specific_deps()

if(TARGET_ANDROID)
    add_library(${BIN_NAME} SHARED ${GAME_MAIN_SOURCE_FILES})
    target_link_libraries(${BIN_NAME} PRIVATE ${SDL2_COMMON_LIBS} sith_engine)
elseif(TARGET_HOOKS)
    add_library(${BIN_NAME} SHARED ${GAME_MAIN_SOURCE_FILES})
    add_library("${BIN_NAME}_kvm" SHARED ${GAME_MAIN_SOURCE_FILES})

    SET_TARGET_PROPERTIES(${BIN_NAME} PROPERTIES PREFIX "")
    SET_TARGET_PROPERTIES("${BIN_NAME}_kvm" PROPERTIES PREFIX "")

    target_link_libraries(${BIN_NAME} PRIVATE ${SDL2_COMMON_LIBS} sith_engine)
    target_link_libraries(${BIN_NAME}_kvm PRIVATE  ${SDL2_COMMON_LIBS} sith_engine)
else()
    add_executable(${BIN_NAME} ${GAME_MAIN_SOURCE_FILES})
    target_link_libraries(${BIN_NAME} PRIVATE ${SDL2_COMMON_LIBS} sith_engine)

    if(TARGET_BUILD_TESTS AND TARGET_MACOS)
        if(NOT EXPERIMENTAL_FIXED_POINT)
            add_executable(rle_test src/Tests/rle_test.c)
            target_link_libraries(rle_test PRIVATE ${SDL2_COMMON_LIBS} sith_engine)
            macos_target_add_standard_deps(rle_test)

            add_executable(rdActive_test src/Tests/rdActive_test.c)
            target_link_libraries(rdActive_test PRIVATE ${SDL2_COMMON_LIBS} sith_engine)
            macos_target_add_standard_deps(rdActive_test)

            add_executable(rdNRaster_test src/Tests/rdNRaster_test.c)
            target_link_libraries(rdNRaster_test PRIVATE ${SDL2_COMMON_LIBS} sith_engine)
            macos_target_add_standard_deps(rdNRaster_test)
        endif()
    endif()
endif()

# TODO byacc and flex
# maxamillion@Maxs-MacBook-Pro Cog % ../../flex/flex -i cog.l
# maxamillion@Maxs-MacBook-Pro Cog % ../../byacc/yacc -d cog.y

# Allow platforms to add extra dependencies, ie Frameworks on macOS or PkgConfig on Linux
plat_extra_deps()

if(NOT OPENJKDF2_NO_ASAN)
    #add_compile_options(-fsanitize=address -fsanitize=float-divide-by-zero)
    #add_link_options(-fsanitize=address -fsanitize=float-divide-by-zero)
    
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize=float-divide-by-zero")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize=float-divide-by-zero")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address -fsanitize=float-divide-by-zero")
    set(SANITIZE_ADDRESS TRUE)
    #target_link_libraries(${BIN_NAME} PRIVATE "-static-libsan")

    add_compile_options(-fsanitize=leak)
    add_link_options(-fsanitize=leak)
endif()

include(cmake_modules/misc_stdcpp_fs_fix.cmake)

# Runs all the target_link_libraries, platform-specific post-build packaging, etc
plat_link_and_package()
