cmake_minimum_required(VERSION 3.20)
project(dayz-memory-cpp CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# -------------------------------------------------------------------------
# Platform guard
# -------------------------------------------------------------------------
if(NOT WIN32)
    message(FATAL_ERROR "dayz-memory-cpp is Windows-only (requires VolkDMA / WinAPI)")
endif()

# -------------------------------------------------------------------------
# Paths to VolkDMA (sibling checkout)
# -------------------------------------------------------------------------
set(VOLKDMA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../dayz-dma/dma-dayz-cpp-master/VolkDMA")

# -------------------------------------------------------------------------
# Dependencies — nlohmann/json, spdlog via FetchContent; ImGui from local copy
# -------------------------------------------------------------------------
include(FetchContent)

FetchContent_Declare(json
    URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
    URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d
)

FetchContent_Declare(spdlog
    GIT_REPOSITORY https://github.com/gabime/spdlog.git
    GIT_TAG        v1.14.1
    GIT_SHALLOW    TRUE
)

# cpp-httplib — header-only HTTP/1.1 server used by WebRadarServer
FetchContent_Declare(httplib
    GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
    GIT_TAG        v0.18.1
    GIT_SHALLOW    TRUE
)

# stb — single-file image load/write libraries
FetchContent_Declare(stb
    GIT_REPOSITORY https://github.com/nothings/stb.git
    GIT_TAG        master
    GIT_SHALLOW    TRUE
)

FetchContent_MakeAvailable(json spdlog httplib stb)

# Vendored Lumin ImGui (1.91.3 + FreeType) and its menu framework.
set(LUMIN_DIR     "${CMAKE_CURRENT_SOURCE_DIR}/external/lumin")
set(IMGUI_DIR     "${LUMIN_DIR}/thirdparty/imgui")
set(FREETYPE_DIR  "${LUMIN_DIR}/thirdparty/freetype")
set(FRAMEWORK_DIR "${LUMIN_DIR}/framework")

# -------------------------------------------------------------------------
# ImGui sources (Lumin's bundled ImGui + FreeType atlas builder)
# -------------------------------------------------------------------------
set(IMGUI_SOURCES
    "${IMGUI_DIR}/imgui.cpp"
    "${IMGUI_DIR}/imgui_draw.cpp"
    "${IMGUI_DIR}/imgui_tables.cpp"
    "${IMGUI_DIR}/imgui_widgets.cpp"
    "${IMGUI_DIR}/imgui_freetype.cpp"
    "${IMGUI_DIR}/backends/imgui_impl_win32.cpp"
    "${IMGUI_DIR}/backends/imgui_impl_dx11.cpp"
)

# -------------------------------------------------------------------------
# Lumin menu framework sources (the actual menu UI)
# -------------------------------------------------------------------------
set(FRAMEWORK_SOURCES
    "${FRAMEWORK_DIR}/gui.cpp"
    "${FRAMEWORK_DIR}/helpers/draw.cpp"
    "${FRAMEWORK_DIR}/helpers/fonts.cpp"
    "${FRAMEWORK_DIR}/widgets/helpers.cpp"
    "${FRAMEWORK_DIR}/widgets/notify.cpp"
    "${FRAMEWORK_DIR}/widgets/text_field.cpp"
    "${FRAMEWORK_DIR}/widgets/widgets.cpp"
    "${FRAMEWORK_DIR}/widgets/window.cpp"
)

# -------------------------------------------------------------------------
# Sources — project + VolkDMA implementation files
# -------------------------------------------------------------------------
file(GLOB_RECURSE PROJECT_SOURCES "src/*.cpp")
# Explicit list ensures new files are picked up without a CMake cache reset.
list(APPEND PROJECT_SOURCES
    "${CMAKE_CURRENT_SOURCE_DIR}/src/SigScanner/SigScanner.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/src/Overlay/OverlayWindow.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/src/Overlay/GameOverlay.cpp"
)
list(REMOVE_DUPLICATES PROJECT_SOURCES)

set(VOLKDMA_SOURCES
    "${VOLKDMA_ROOT}/src/dma.cpp"
    "${VOLKDMA_ROOT}/src/process.cpp"
    "${VOLKDMA_ROOT}/src/inputstate.cpp"
)

# -------------------------------------------------------------------------
# Embed map PNGs as Windows RCDATA resources
#
# Maps present in maps/ at configure time are baked into the binary so the
# server works out-of-the-box without copying anything next to the exe.
# A PNG placed on disk at runtime (maps/<id>.png next to the exe) still
# takes priority, so maps can be updated without rebuilding.
#
# Re-run CMake if you add new PNGs to maps/ after the initial configure.
# -------------------------------------------------------------------------
set(MAPS_SOURCE_DIR   "${CMAKE_CURRENT_SOURCE_DIR}/maps")
set(EMBEDDED_MAPS_RC  "${CMAKE_CURRENT_BINARY_DIR}/maps_embedded.rc")
set(EMBEDDED_MAPS_CPP "${CMAKE_CURRENT_BINARY_DIR}/EmbeddedMaps.cpp")

set(MAP_IDS  chernarusplus livonia namalsk banov deadfall deerisle esseker lux sakhal takistan alteria)
set(MAP_RIDS 1001          1002    1003    1004  1005     1006     1007    1008 1009   1010     1011)

set(RC_LINES "")
set(CPP_CASES "")
foreach(MAP_ID MAP_RID IN ZIP_LISTS MAP_IDS MAP_RIDS)
    if(EXISTS "${MAPS_SOURCE_DIR}/${MAP_ID}.png")
        string(APPEND RC_LINES "${MAP_RID} RCDATA \"${MAPS_SOURCE_DIR}/${MAP_ID}.png\"\n")
        string(APPEND CPP_CASES "    if (mapId == \"${MAP_ID}\") return LoadRcData(${MAP_RID});\n")
        message(STATUS "Embedding map: ${MAP_ID}")
    else()
        message(STATUS "Skipping map (not found): ${MAP_ID}")
    endif()
endforeach()

file(WRITE "${EMBEDDED_MAPS_RC}" "// Auto-generated by CMake — do not edit.\n${RC_LINES}")
file(WRITE "${EMBEDDED_MAPS_CPP}"
"// Auto-generated by CMake — do not edit.
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <cstdint>
#include <utility>
#include \"EmbeddedMaps.h\"

static std::pair<const uint8_t*, size_t> LoadRcData(int resourceId) {
    HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(resourceId), RT_RCDATA);
    if (!hRes) return {nullptr, 0};
    HGLOBAL hData = LoadResource(nullptr, hRes);
    if (!hData) return {nullptr, 0};
    const void* ptr = LockResource(hData);
    DWORD sz = SizeofResource(nullptr, hRes);
    if (!ptr || sz == 0) return {nullptr, 0};
    return {static_cast<const uint8_t*>(ptr), sz};
}

std::pair<const uint8_t*, size_t> GetEmbeddedMap(const std::string& mapId) {
${CPP_CASES}    return {nullptr, 0};
}
")

add_executable(dayz-memory-cpp
    ${PROJECT_SOURCES}
    ${VOLKDMA_SOURCES}
    ${IMGUI_SOURCES}
    ${FRAMEWORK_SOURCES}
)

# Wire in the generated RC + CPP files.
target_sources(dayz-memory-cpp PRIVATE
    "${EMBEDDED_MAPS_RC}"
    "${EMBEDDED_MAPS_CPP}"
)

# -------------------------------------------------------------------------
# Include directories
# -------------------------------------------------------------------------
target_include_directories(dayz-memory-cpp PRIVATE
    # Own source tree
    "${CMAKE_CURRENT_SOURCE_DIR}/src"

    # ImGui core + Win32/DX11 backends
    "${IMGUI_DIR}"
    "${IMGUI_DIR}/backends"

    # FreeType headers (atlas builder)
    "${FREETYPE_DIR}/include"

    # Lumin framework root — lets sources include "framework/headers/includes.h"
    "${LUMIN_DIR}"
    "${FRAMEWORK_DIR}"

    # VolkDMA repo root — VolkDMA's own source files use paths like
    # "include/VolkDMA/dma.hh" and "external/vmm/vmmdll.h" that are
    # relative to this root, so this entry must come first.
    "${VOLKDMA_ROOT}"

    # VolkDMA public headers  (provides <VolkDMA/dma.hh> etc.)
    "${VOLKDMA_ROOT}/include"

    # VolkLog  (required by VolkDMA's dma.cpp / process.cpp)
    "${VOLKDMA_ROOT}/external/VolkLog/include"

    # Raw vmmdll / leechcore headers (used in VmmAccessor.cpp + VolkDMA sources)
    "${VOLKDMA_ROOT}/external/vmm"
    "${VOLKDMA_ROOT}/external/leechcore"

    # cpp-httplib (header-only; FetchContent populates httplib_SOURCE_DIR)
    "${httplib_SOURCE_DIR}"

    # stb (header-only image library; stb_impl.cpp provides the implementation)
    "${stb_SOURCE_DIR}"
)

# -------------------------------------------------------------------------
# Link directories
# -------------------------------------------------------------------------
target_link_directories(dayz-memory-cpp PRIVATE
    "${VOLKDMA_ROOT}/external/vmm"
    "${VOLKDMA_ROOT}/external/leechcore"
)

# -------------------------------------------------------------------------
# Link libraries
# -------------------------------------------------------------------------
target_link_libraries(dayz-memory-cpp PRIVATE
    nlohmann_json::nlohmann_json
    spdlog::spdlog
    vmm
    leechcore
    # FreeType (prebuilt x64) — Lumin font atlas builder
    "${FREETYPE_DIR}/win64/freetype.lib"
    # DirectX 11 + DXGI (for ImGui DX11 window)
    d3d11
    dxgi
    # WinSock2 — required by cpp-httplib on Windows
    Ws2_32
)

# -------------------------------------------------------------------------
# Post-build: copy VolkDMA runtime DLLs next to the executable.
# Without these (vmm.dll, leechcore.dll, FTD3XX.dll) the process exits with
# 0xC0000135 (STATUS_DLL_NOT_FOUND) immediately on launch.
# -------------------------------------------------------------------------
set(VOLKDMA_DLLS_DIR "${VOLKDMA_ROOT}/dlls")
foreach(DLL vmm.dll leechcore.dll FTD3XX.dll)
    add_custom_command(TARGET dayz-memory-cpp POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
            "${VOLKDMA_DLLS_DIR}/${DLL}"
            "$<TARGET_FILE_DIR:dayz-memory-cpp>/${DLL}"
        COMMENT "Copying ${DLL} to output directory"
    )
endforeach()

# Copy webroot/ directory next to the executable so the HTTP server can serve it.
add_custom_command(TARGET dayz-memory-cpp POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
        "${CMAKE_CURRENT_SOURCE_DIR}/webroot"
        "$<TARGET_FILE_DIR:dayz-memory-cpp>/webroot"
    COMMENT "Copying webroot to output directory"
)

# Copy the Lumin icon font next to the executable so the menu's tab icons load
# (flaticon_uicons_regular_rounded_path is resolved relative to the work dir).
add_custom_command(TARGET dayz-memory-cpp POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
        "${FRAMEWORK_DIR}/data/uicons"
        "$<TARGET_FILE_DIR:dayz-memory-cpp>/data/uicons"
    COMMENT "Copying Lumin uicons font to output directory"
)

# Copy config/ directory next to the executable (config.cfg, item_filters.json).
add_custom_command(TARGET dayz-memory-cpp POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
        "${CMAKE_CURRENT_SOURCE_DIR}/config"
        "$<TARGET_FILE_DIR:dayz-memory-cpp>/config"
    COMMENT "Copying config to output directory"
)

# Copy maps/ directory next to the executable so MapTileService can find PNGs.
# maps/ is git-ignored (user-supplied), so only copy when it exists.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/maps")
    add_custom_command(TARGET dayz-memory-cpp POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_directory
            "${CMAKE_CURRENT_SOURCE_DIR}/maps"
            "$<TARGET_FILE_DIR:dayz-memory-cpp>/maps"
        COMMENT "Copying maps to output directory"
    )
endif()

# -------------------------------------------------------------------------
# Compiler definitions
# -------------------------------------------------------------------------
target_compile_definitions(dayz-memory-cpp PRIVATE
    _WIN32_WINNT=0x0601
    # Build the ImGui font atlas with FreeType (Lumin requirement; also set in
    # the vendored imconfig.h, kept here so every TU agrees).
    IMGUI_ENABLE_FREETYPE
    IMGUI_DEFINE_MATH_OPERATORS
    # WIN32_LEAN_AND_MEAN is intentionally NOT set globally: vmmdll.h requires
    # NTSTATUS from <winternl.h>, which windows.h omits when LEAN_AND_MEAN is
    # defined.  Our own headers define it locally before including <Windows.h>.
    NOMINMAX
)

# -------------------------------------------------------------------------
# MSVC-specific flags
# -------------------------------------------------------------------------
if(MSVC)
    target_compile_options(dayz-memory-cpp PRIVATE
        /W4
        /WX-        # vmmdll / VolkDMA headers generate some W4 noise
        /permissive-
        /utf-8
    )
endif()
