cmake_minimum_required(VERSION 3.20)
project(AIServer LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(GAMEPLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../Config/Gameplay/greenfield.json")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${GAMEPLAY_FILE}")
file(READ "${GAMEPLAY_FILE}" GAMEPLAY_JSON)
file(SHA256 "${GAMEPLAY_FILE}" GAMEPLAY_HASH)
string(SUBSTRING "${GAMEPLAY_HASH}" 0 12 GAMEPLAY_HASH_SHORT)
set(GAMEPLAY_VERSION "greenfield-1-${GAMEPLAY_HASH_SHORT}")
# MSVC caps a single string literal. Chunks are cut on a newline so a UTF-8 character is never
# split, and adjacent literals concatenate back to the exact file bytes.
string(LENGTH "${GAMEPLAY_JSON}" GAMEPLAY_JSON_LENGTH)
set(GAMEPLAY_JSON_CHUNKS "")
set(GAMEPLAY_JSON_OFFSET 0)
while(GAMEPLAY_JSON_OFFSET LESS GAMEPLAY_JSON_LENGTH)
  math(EXPR GAMEPLAY_JSON_REMAINING "${GAMEPLAY_JSON_LENGTH} - ${GAMEPLAY_JSON_OFFSET}")
  if(GAMEPLAY_JSON_REMAINING GREATER 4000)
    string(SUBSTRING "${GAMEPLAY_JSON}" ${GAMEPLAY_JSON_OFFSET} 4000 GAMEPLAY_JSON_PART)
    string(FIND "${GAMEPLAY_JSON_PART}" "\n" GAMEPLAY_JSON_BREAK REVERSE)
    if(GAMEPLAY_JSON_BREAK GREATER 0)
      math(EXPR GAMEPLAY_JSON_BREAK "${GAMEPLAY_JSON_BREAK} + 1")
      string(SUBSTRING "${GAMEPLAY_JSON_PART}" 0 ${GAMEPLAY_JSON_BREAK} GAMEPLAY_JSON_PART)
    endif()
  else()
    string(SUBSTRING "${GAMEPLAY_JSON}" ${GAMEPLAY_JSON_OFFSET} ${GAMEPLAY_JSON_REMAINING} GAMEPLAY_JSON_PART)
  endif()
  string(LENGTH "${GAMEPLAY_JSON_PART}" GAMEPLAY_JSON_PART_LENGTH)
  string(APPEND GAMEPLAY_JSON_CHUNKS "R\"GAMECONFIG(${GAMEPLAY_JSON_PART})GAMECONFIG\"\n")
  math(EXPR GAMEPLAY_JSON_OFFSET "${GAMEPLAY_JSON_OFFSET} + ${GAMEPLAY_JSON_PART_LENGTH}")
endwhile()
configure_file(src/game_config.hpp.in generated/game_config_data.hpp @ONLY)
set(SKILL_RUNTIME_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../Config/Combat/skill_runtime_v1.json")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${SKILL_RUNTIME_FILE}")
file(READ "${SKILL_RUNTIME_FILE}" SKILL_RUNTIME_JSON)
configure_file(src/skill_runtime.hpp.in generated/skill_runtime_data.hpp @ONLY)
include_directories("${CMAKE_CURRENT_BINARY_DIR}/generated")

add_executable(aiserver
  src/main.cpp
  src/db.cpp
  src/game.cpp
  src/game_config.cpp
  src/combat_slice.cpp
  src/combat_runtime.cpp
  src/auth.cpp
  src/http_ws_server.cpp
  src/maintenance.cpp
  src/crypto.cpp
  third_party/sqlite3.c
)

target_include_directories(aiserver PRIVATE
  ${CMAKE_CURRENT_SOURCE_DIR}/src
  ${CMAKE_CURRENT_SOURCE_DIR}/third_party
)

# SQLite: 关闭部分扩展，加快编译
target_compile_definitions(aiserver PRIVATE
  SQLITE_OMIT_LOAD_EXTENSION
  SQLITE_THREADSAFE=1
  _CRT_SECURE_NO_WARNINGS
)

if(WIN32)
  target_link_libraries(aiserver PRIVATE ws2_32 bcrypt)
  # sqlite3.c 是 C，MSVC 下关闭部分告警
  if(MSVC)
    target_compile_options(aiserver PRIVATE /W3 /utf-8)
    set_source_files_properties(third_party/sqlite3.c PROPERTIES COMPILE_FLAGS "/W0")
  endif()
else()
  target_link_libraries(aiserver PRIVATE pthread dl)
endif()

# 运行时默认数据目录：可执行文件旁的 data/
add_custom_command(TARGET aiserver POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:aiserver>/data
)

include(CTest)
if(BUILD_TESTING)
  add_executable(combat_runtime_tests tests/combat_runtime_tests.cpp src/db.cpp src/game.cpp src/game_config.cpp src/combat_slice.cpp src/combat_runtime.cpp src/crypto.cpp third_party/sqlite3.c)
  target_include_directories(combat_runtime_tests PRIVATE src third_party)
  target_compile_definitions(combat_runtime_tests PRIVATE SQLITE_OMIT_LOAD_EXTENSION SQLITE_THREADSAFE=1 _CRT_SECURE_NO_WARNINGS)
  if(MSVC)
    target_compile_options(combat_runtime_tests PRIVATE /W3 /utf-8)
  elseif(NOT WIN32)
    target_link_libraries(combat_runtime_tests PRIVATE pthread dl)
  endif()
  add_test(NAME combat_authoritative_runtime COMMAND combat_runtime_tests)
  add_executable(combat_slice_tests tests/combat_slice_tests.cpp src/combat_slice.cpp)
  target_include_directories(combat_slice_tests PRIVATE src third_party)
  if(MSVC)
    target_compile_options(combat_slice_tests PRIVATE /W3 /utf-8)
  endif()
  add_test(NAME combat_skill_slice COMMAND combat_slice_tests)
  add_executable(game_tests tests/game_tests.cpp src/db.cpp src/game.cpp src/game_config.cpp src/combat_slice.cpp src/combat_runtime.cpp src/crypto.cpp third_party/sqlite3.c)
  target_include_directories(game_tests PRIVATE src third_party)
  target_compile_definitions(game_tests PRIVATE SQLITE_OMIT_LOAD_EXTENSION SQLITE_THREADSAFE=1 _CRT_SECURE_NO_WARNINGS)
  target_compile_definitions(game_tests PRIVATE GAME_TEST_FIXTURE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures")
  if(MSVC)
    target_compile_options(game_tests PRIVATE /W3 /utf-8)
  elseif(NOT WIN32)
    target_link_libraries(game_tests PRIVATE pthread dl)
  endif()
  add_test(NAME game_transactions COMMAND game_tests)
  add_executable(first_day_tests tests/first_day_tests.cpp src/db.cpp src/game.cpp src/game_config.cpp src/combat_slice.cpp src/combat_runtime.cpp src/crypto.cpp third_party/sqlite3.c)
  target_include_directories(first_day_tests PRIVATE src third_party)
  target_compile_definitions(first_day_tests PRIVATE SQLITE_OMIT_LOAD_EXTENSION SQLITE_THREADSAFE=1 _CRT_SECURE_NO_WARNINGS)
  if(MSVC)
    target_compile_options(first_day_tests PRIVATE /W3 /utf-8)
  elseif(NOT WIN32)
    target_link_libraries(first_day_tests PRIVATE pthread dl)
  endif()
  add_test(NAME first_day_progression COMMAND first_day_tests)
endif()
