cmake_minimum_required(VERSION 3.30)
project(Hoshi C CXX ASM_MASM)

set(CMAKE_CXX_STANDARD 20)

set(FETCHCONTENT_UPDATES_DISCONNECTED ON CACHE BOOL "Disable updates for FetchContent")
set(HOSHI_NDEBUG OFF CACHE BOOL "Disable Hoshi debug functionalities")

include(FetchContent)

set(BUILD_MOD ON CACHE BOOL "Build the client mod")
set(BUILD_TESTER ON CACHE BOOL "Build the tester")
set(SDKPATH "C:/Dumper-7/4.27.2-0+++UE4+Release-4.27-BLUEPROTOCOL/CppSDK" CACHE PATH "Path to the SDK")
set(VERSION "V5" CACHE STRING "Game version")
set(NTPDPATH "" CACHE FILEPATH "Path to ntpd library")

set(HOSHI_ROOT "${CMAKE_SOURCE_DIR}/..")

function (add_kproto_sources target)
    set(multiValueArgs HEADER_ONLY)
    cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "${multiValueArgs}")

    set(KPROTO_COMPILER_BIN "${HOSHI_ROOT}/Libraries/KiraProto.Compiler/bin/Release/net9.0/KiraProto.Compiler${CMAKE_EXECUTABLE_SUFFIX}")

    set(output_dir_include_root "${CMAKE_CURRENT_BINARY_DIR}/gen")
    set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/gen/Proto")
    set(input_files)
    set(output_files)

    foreach (arg_FILE IN LISTS arg_UNPARSED_ARGUMENTS)
        get_filename_component(file_name "${arg_FILE}" NAME)
        string(REGEX REPLACE "\\.[^.]*$" "" file_name "${file_name}")
        set(output_file "${output_dir}/${file_name}.cpp")
        list(APPEND input_files "${arg_FILE}")
        list(APPEND output_files "${output_file}")
    endforeach ()
    foreach (arg_FILE IN LISTS arg_HEADER_ONLY)
        list(APPEND input_files "${arg_FILE}")
    endforeach ()

    add_custom_command(
            OUTPUT ${output_files}
            COMMAND ${KPROTO_COMPILER_BIN} --cxx-out=${output_dir} ${input_files}
            DEPENDS ${KPROTO_COMPILER_BIN} ${input_files}
            WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
            VERBATIM
    )
    target_sources(${target} PUBLIC ${output_files})
    target_include_directories(${target} PUBLIC "${output_dir_include_root}")
endfunction()

FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz)
FetchContent_MakeAvailable(json)

if (BUILD_MOD)
    FetchContent_Declare(minhook GIT_REPOSITORY https://github.com/TsudaKageyu/minhook.git GIT_TAG master GIT_SHALLOW TRUE)
    FetchContent_MakeAvailable(minhook)
endif()

if (WIN32)
    add_compile_options(/GR-)  # disable rtti
    add_compile_definitions(-D_HAS_STATIC_RTTI=0)
    
    add_compile_options($<$<NOT:$<CONFIG:Debug>>:/Gy>)  # enable function-level linking
    add_link_options(/OPT:REF)  # strip unneedd functions
    add_link_options(/OPT:ICF)  # merge identical functions
    
    add_compile_options(/bigobj)
    add_compile_definitions(WIN32_LEAN_AND_MEAN NOMINMAX)
else()
    add_compile_options(-fno-rtti)
endif()

if (HOSHI_NDEBUG)
    add_compile_definitions(HOSHI_NDEBUG)
endif()

set(HOSHI_SOURCES
        main.cpp
        Util/Log.cpp
        Util/Hook.cpp
        Util/UnrealUtils.cpp
        Util/LMDebugAlloc.cpp
        Unreal/FWeakObjectPtr.cpp
        Unreal/FString.cpp
        Net/ProtoDump.cpp
        Game/BugFixes.cpp
)
set(KIRANET_SOURCES
        Net/ikcp.c
        Net/KiraNet.cpp
        Net/KiraRawSocket.cpp
)
set(HOSHI_PROTO_KPROTO_SOURCES
        "${HOSHI_ROOT}/Hoshi.GameServer/Net/Rpc/${VERSION}/Rpc.kproto"
        "${HOSHI_ROOT}/Hoshi.GameServer/Net/Rpc/${VERSION}/RpcTypes.kproto"
        "${HOSHI_ROOT}/Hoshi.GameServer/Net/Rpc/CustomRpc.kproto"
        "${HOSHI_ROOT}/Hoshi.Common/Data/Custom/CustomData_Stable.kproto"
        "${HOSHI_ROOT}/Hoshi.Common/Data/Custom/CustomData_Version.kproto"

        HEADER_ONLY
        "${HOSHI_ROOT}/Hoshi.GameServer/Net/Rpc/${VERSION}/RpcEnums.kproto"
        "${HOSHI_ROOT}/Hoshi.GameServer/Net/Rpc/RpcForeignTypes.kproto"
)
set(HOSHI_PROTO_SOURCES
        Unreal/FString.cpp
        Proto/CustomRpc_Manual.cpp
        Proto/SdkExtension.cpp
)
set(HOSHI_DYN_SOURCES
        ${KIRANET_SOURCES}
        DynReload.cpp
        Game/Replication.cpp
        Game/DataLoader.cpp
        Game/ApiextLoader.cpp
        Game/PlayerData.cpp
        Net/NetworkManager.cpp
        Net/NetObjectRef.cpp
        Util/Log.cpp
        Util/Hook.cpp
        Util/UnrealUtils.cpp
        Unreal/FWeakObjectPtr.cpp
        Game/MapLoader.cpp
        Game/MapLoader.h
        Game/Components/MountComponent.cpp
        Game/Components/MountComponent.h
        Game/Components/CharaCreateComponent.cpp
        Game/Components/CharaCreateComponent.h
        Game/Components/HoshiComponentManager.cpp
        Game/DebugUtil/StateMachineDumper.cpp
        Game/DebugUtil/StateMachineDumper.h
        Game/Replication_Property.cpp
        Game/Replication_Costume.cpp
        Game/Replication_Ai.cpp
        Game/HoshiPkg.cpp
        Game/HoshiPkg.h
        Util/PlatformUtils.cpp
        Util/PlatformUtils.h
        Game/Components/EquipComponent.cpp
        Game/Components/EquipComponent.h
        Game/Components/ImageUploadComponent.cpp
        Game/Components/ImageUploadComponent.h
        Game/Components/ChatComponent.cpp
        Game/Components/ChatComponent.h
        Game/Components/BasicComponent.cpp
        Game/Components/BasicComponent.h
        Game/DebugUtil/Blueprint.cpp
        Game/HoshiUi.cpp
        Game/ModConfig.cpp
        Game/ModConfig.h
        Game/Components/CharacterLogComponent.cpp
        Game/Components/CharacterLogComponent.h
        Game/Components/AdminComponent.cpp
        Game/Components/WorldInteractionComponent.cpp
        Net/JwtTokenHelper.cpp
        Game/MapDataDumper.cpp
        Game/MapDataDumper.h
        Game/Components/GachaComponent.cpp
        Game/Components/GachaComponent.h
)

include_directories(.)
include_directories(../Libraries/KiraProto.Runtime.Cpp)
include_directories(${SDKPATH})
include_directories(Version/${VERSION})
include_directories(../Hoshi.GameServer/Net/Rpc/${VERSION})

if (BUILD_MOD OR BUILD_TESTER)
    add_library(HoshiProto STATIC ${HOSHI_PROTO_SOURCES})
    add_kproto_sources(HoshiProto ${HOSHI_PROTO_KPROTO_SOURCES})
    target_sources(HoshiProto PRIVATE ${HOSHI_PROTO_SOURCES})
    target_link_libraries(HoshiProto PUBLIC SDK)
endif()

if (BUILD_MOD)
    add_library(SDK STATIC
            ${SDKPATH}/SDK/Basic.cpp
            ${SDKPATH}/SDK/CoreUObject_functions.cpp
            ${SDKPATH}/SDK/Engine_functions.cpp
            ${SDKPATH}/SDK/ActionSystem_functions.cpp
            ${SDKPATH}/SDK/UMG_functions.cpp
            ${SDKPATH}/SDK/SkyBlue_functions.cpp)

    add_library(Hoshi SHARED ${HOSHI_SOURCES})
    target_link_libraries(Hoshi PUBLIC minhook SDK)
    
    if (HOSHI_NDEBUG)
        target_sources(Hoshi PRIVATE ${HOSHI_DYN_SOURCES})
        target_link_libraries(Hoshi PUBLIC HoshiProto)
    else()
        add_library(HoshiDyn SHARED ${HOSHI_DYN_SOURCES})
        target_link_libraries(HoshiDyn PUBLIC minhook SDK HoshiProto)
        add_dependencies(Hoshi HoshiDyn)
    endif()
    
    add_library(xinput1_3 SHARED XinputLoader.cpp XinputLoader.asm XinputLoader.def)
endif()

if (BUILD_TESTER)
    add_executable(NetTesterAgent
            ${KIRANET_SOURCES}
            Net/Test/NetTesterAgent.cpp
            Util/Log.cpp
            Util/StandaloneSupport.cpp
            Net/Test/001_MovementData.cpp
            Net/Test/Scenario.cpp
            Net/Test/TimestampSource.cpp
            Net/Test/TimestampSource.h
    )
    target_compile_definitions(NetTesterAgent PRIVATE STANDALONE)
    target_link_libraries(NetTesterAgent PRIVATE nlohmann_json::nlohmann_json HoshiProto)

    if (NOT NTPDPATH STREQUAL "")
        add_library(ntpd STATIC IMPORTED)
        set_target_properties(ntpd PROPERTIES IMPORTED_LOCATION ${NTPDPATH})
        target_link_libraries(NetTesterAgent PRIVATE ntpd)
        target_compile_definitions(NetTesterAgent PRIVATE WITH_NTPD)
    endif()

    if (NOT WIN32)
#        target_link_libraries(NetTesterAgent PRIVATE -static)
    endif()
endif()

add_library(HoshiServerNative SHARED
        ${KIRANET_SOURCES}
        Net/KiraServer.cpp
        Server/KiraNet.cpp
        Server/Log.cpp
)

add_executable(KiraServerTest
        ${KIRANET_SOURCES}
        Net/KiraServer.cpp
        Net/Test/KiraServerTest.cpp
        Util/Log.cpp
)
add_executable(KiraClientTest
        ${KIRANET_SOURCES}
        Net/Test/KiraClientTest.cpp
        Util/Log.cpp
)

include_directories(E:/testEnv/)