# DragonX Wallet - ImGui Edition
# Copyright 2024-2026 The Hush Developers
# Released under the GPLv3

cmake_minimum_required(VERSION 3.20)

# macOS: set deployment target and universal architectures BEFORE project()
# so they propagate to all targets, including FetchContent dependencies (SDL3, etc.)
if(APPLE)
    set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum macOS version" FORCE)
    # Build universal binary (Apple Silicon + Intel) unless the user explicitly set architectures
    if(NOT DEFINED CMAKE_OSX_ARCHITECTURES OR CMAKE_OSX_ARCHITECTURES STREQUAL "")
        set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "macOS architectures" FORCE)
    endif()
endif()

project(ObsidianDragon
    VERSION 2.0.0
    LANGUAGES C CXX
    DESCRIPTION "DragonX Cryptocurrency Wallet"
)

# Pre-release suffix (e.g. "-rc1", "-beta2"). Leave empty for stable releases.
set(DRAGONX_VERSION_SUFFIX "")

# ObsidianDragonLite is versioned INDEPENDENTLY of the full-node app above. The active variant's
# version flows to the generated header, the Windows .rc/manifest, and build.sh's release names via
# DRAGONX_APP_VERSION* (resolved in the lite/full block below).
set(DRAGONX_LITE_VERSION "1.0.0")
set(DRAGONX_LITE_VERSION_SUFFIX "")

# C++17 standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()

# Options
option(DRAGONX_USE_SYSTEM_SDL3 "Use system SDL3 instead of fetching" ON)
option(DRAGONX_ENABLE_EMBEDDED_DAEMON "Enable embedded dragonxd support" ON)
option(DRAGONX_BUILD_LITE "Build ObsidianDragonLite variant without full-node features" OFF)
option(DRAGONX_ENABLE_LITE_BACKEND "Enable real lite wallet backend integration" OFF)
option(DRAGONX_ENABLE_CHAT "Enable experimental HushChat protocol/UI integration" OFF)
set(DRAGONX_LITE_BACKEND_LIBRARY "" CACHE FILEPATH "Path to a prebuilt SDXL-compatible lite backend library")
set(DRAGONX_LITE_BACKEND_INCLUDE_DIR "" CACHE PATH "Optional include directory for SDXL-compatible lite backend headers")
set(DRAGONX_LITE_BACKEND_EXTRA_LIBS "" CACHE STRING "Additional libraries needed by the SDXL-compatible lite backend")
set(DRAGONX_LITE_BACKEND_LINK_MODE "imported" CACHE STRING "Lite backend link mode; Phase 1 supports imported only")
set_property(CACHE DRAGONX_LITE_BACKEND_LINK_MODE PROPERTY STRINGS imported)
set(DRAGONX_LITE_BACKEND_ABI "sdxl-c-v1" CACHE STRING "Expected lite backend C ABI version")
set(DRAGONX_LITE_BACKEND_SYMBOLS_FILE "" CACHE FILEPATH "Path to generated lite backend exported-symbol inventory")
set(DRAGONX_LITE_BACKEND_MANIFEST "" CACHE FILEPATH "Optional path to generated lite backend artifact manifest")
option(DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE "Require verified signature metadata in the lite backend artifact manifest" OFF)
set(DRAGONX_LITE_BACKEND_REQUIRED_SYMBOLS
    litelib_wallet_exists
    litelib_initialize_new
    litelib_initialize_new_from_phrase
    litelib_initialize_existing
    litelib_execute
    litelib_rust_free_string
    litelib_check_server_online
    litelib_shutdown
)

if(DRAGONX_BUILD_LITE)
    set(DRAGONX_APP_NAME "ObsidianDragonLite")
    set(DRAGONX_BINARY_NAME "ObsidianDragonLite")
    # NOTE: do NOT FORCE-write DRAGONX_ENABLE_EMBEDDED_DAEMON=OFF into the cache here. A forced
    # cache write persists into a later full-node reconfigure of the same build dir and silently
    # disables the embedded daemon — the binary still embeds/extracts, but isUsingEmbeddedDaemon()
    # returns false, so it "unpacks dragonxd but never starts" (the 1.3.0 regression). It is also
    # redundant: makeWalletCapabilities() already forces the embedded-daemon capability off for any
    # lite build via `fullNodeBuild && embeddedDaemonCompiled`, so lite never launches a daemon
    # regardless of this flag. build.sh sets the flag explicitly per variant to defeat stale caches.
    set(DRAGONX_APP_VERSION "${DRAGONX_LITE_VERSION}")
    set(DRAGONX_APP_VERSION_SUFFIX "${DRAGONX_LITE_VERSION_SUFFIX}")
else()
    set(DRAGONX_APP_NAME "ObsidianDragon")
    set(DRAGONX_BINARY_NAME "ObsidianDragon")
    set(DRAGONX_APP_VERSION "${PROJECT_VERSION}")
    set(DRAGONX_APP_VERSION_SUFFIX "${DRAGONX_VERSION_SUFFIX}")
endif()

# Split the active version into numeric components for the generated header + Windows VERSIONINFO.
string(REPLACE "." ";" _dragonx_ver_parts "${DRAGONX_APP_VERSION}")
list(GET _dragonx_ver_parts 0 DRAGONX_APP_VERSION_MAJOR)
list(GET _dragonx_ver_parts 1 DRAGONX_APP_VERSION_MINOR)
list(GET _dragonx_ver_parts 2 DRAGONX_APP_VERSION_PATCH)

set(DRAGONX_LITE_BACKEND_READY OFF)
if(DRAGONX_ENABLE_LITE_BACKEND)
    if(NOT DRAGONX_BUILD_LITE)
        message(FATAL_ERROR "DRAGONX_ENABLE_LITE_BACKEND is only supported with DRAGONX_BUILD_LITE=ON")
    endif()
    if(NOT DRAGONX_LITE_BACKEND_LINK_MODE STREQUAL "imported")
        message(FATAL_ERROR "DRAGONX_LITE_BACKEND_LINK_MODE currently supports only 'imported'; runtime dynamic loading is a later bridge-runtime phase")
    endif()
    if(NOT DRAGONX_LITE_BACKEND_ABI STREQUAL "sdxl-c-v1")
        message(FATAL_ERROR "DRAGONX_LITE_BACKEND_ABI must be sdxl-c-v1")
    endif()
    if(NOT DRAGONX_LITE_BACKEND_LIBRARY)
        message(FATAL_ERROR "DRAGONX_ENABLE_LITE_BACKEND requires DRAGONX_LITE_BACKEND_LIBRARY to point at an SDXL-compatible artifact")
    endif()
    if(NOT EXISTS "${DRAGONX_LITE_BACKEND_LIBRARY}")
        message(FATAL_ERROR "DRAGONX_LITE_BACKEND_LIBRARY does not exist: ${DRAGONX_LITE_BACKEND_LIBRARY}")
    endif()
    if(NOT DRAGONX_LITE_BACKEND_SYMBOLS_FILE)
        message(FATAL_ERROR "DRAGONX_ENABLE_LITE_BACKEND requires DRAGONX_LITE_BACKEND_SYMBOLS_FILE generated by scripts/build-lite-backend-artifact.sh")
    endif()
    if(NOT EXISTS "${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
        message(FATAL_ERROR "DRAGONX_LITE_BACKEND_SYMBOLS_FILE does not exist: ${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
    endif()
    file(STRINGS "${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}" DRAGONX_LITE_BACKEND_SYMBOL_LINES)
    if(NOT DRAGONX_LITE_BACKEND_SYMBOL_LINES)
        message(FATAL_ERROR "DRAGONX_LITE_BACKEND_SYMBOLS_FILE is empty: ${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
    endif()
    foreach(DRAGONX_LITE_REQUIRED_SYMBOL IN LISTS DRAGONX_LITE_BACKEND_REQUIRED_SYMBOLS)
        list(FIND DRAGONX_LITE_BACKEND_SYMBOL_LINES "${DRAGONX_LITE_REQUIRED_SYMBOL}" DRAGONX_LITE_SYMBOL_INDEX)
        if(DRAGONX_LITE_SYMBOL_INDEX EQUAL -1)
            message(FATAL_ERROR "DRAGONX_LITE_BACKEND_SYMBOLS_FILE is missing required symbol: ${DRAGONX_LITE_REQUIRED_SYMBOL}")
        endif()
    endforeach()
    if(DRAGONX_LITE_BACKEND_MANIFEST AND NOT EXISTS "${DRAGONX_LITE_BACKEND_MANIFEST}")
        message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST does not exist: ${DRAGONX_LITE_BACKEND_MANIFEST}")
    endif()
    if(DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE)
        if(NOT DRAGONX_LITE_BACKEND_MANIFEST)
            message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires DRAGONX_LITE_BACKEND_MANIFEST")
        endif()
        file(READ "${DRAGONX_LITE_BACKEND_MANIFEST}" DRAGONX_LITE_BACKEND_MANIFEST_JSON)
        string(JSON DRAGONX_LITE_SIGNATURE_STATUS ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_STATUS_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verification_status)
        if(DRAGONX_LITE_SIGNATURE_STATUS_ERROR)
            message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST is missing signature verification status")
        endif()
        if(NOT DRAGONX_LITE_SIGNATURE_STATUS STREQUAL "verified")
            message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires verified signature metadata")
        endif()
        string(JSON DRAGONX_LITE_SIGNATURE_VERIFIED_SHA ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_VERIFIED_SHA_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verified_artifact_sha256)
        string(JSON DRAGONX_LITE_ARTIFACT_SHA ERROR_VARIABLE DRAGONX_LITE_ARTIFACT_SHA_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" artifact sha256)
        if(DRAGONX_LITE_SIGNATURE_VERIFIED_SHA_ERROR OR DRAGONX_LITE_ARTIFACT_SHA_ERROR)
            message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST is missing artifact/signature SHA-256 metadata")
        endif()
        if(NOT DRAGONX_LITE_SIGNATURE_VERIFIED_SHA STREQUAL DRAGONX_LITE_ARTIFACT_SHA)
            message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST signature metadata does not verify the artifact SHA-256")
        endif()
        string(JSON DRAGONX_LITE_SIGNATURE_PERFORMED ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_PERFORMED_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verification_performed)
        if(DRAGONX_LITE_SIGNATURE_PERFORMED_ERROR OR NOT DRAGONX_LITE_SIGNATURE_PERFORMED)
            message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires verification_performed=true")
        endif()
    endif()

    add_library(dragonx_lite_backend UNKNOWN IMPORTED)
    set_target_properties(dragonx_lite_backend PROPERTIES
        IMPORTED_LOCATION "${DRAGONX_LITE_BACKEND_LIBRARY}"
    )
    if(DRAGONX_LITE_BACKEND_INCLUDE_DIR)
        if(NOT IS_DIRECTORY "${DRAGONX_LITE_BACKEND_INCLUDE_DIR}")
            message(FATAL_ERROR "DRAGONX_LITE_BACKEND_INCLUDE_DIR does not exist: ${DRAGONX_LITE_BACKEND_INCLUDE_DIR}")
        endif()
        set_target_properties(dragonx_lite_backend PROPERTIES
            INTERFACE_INCLUDE_DIRECTORIES "${DRAGONX_LITE_BACKEND_INCLUDE_DIR}"
        )
    endif()
    set(DRAGONX_LITE_BACKEND_READY ON)
endif()

include(CTest)

# Output directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

# -----------------------------------------------------------------------------
# Dependencies
# -----------------------------------------------------------------------------

# OpenGL (Linux only - Windows uses DirectX 11, macOS uses frameworks)
if(NOT WIN32 AND NOT APPLE)
    find_package(OpenGL REQUIRED)
endif()

# miniz - bundled zip library (MIT / public domain)
set(MINIZ_DIR ${CMAKE_SOURCE_DIR}/libs/miniz)
set(MINIZ_SOURCES
    ${MINIZ_DIR}/miniz.c
    ${MINIZ_DIR}/miniz_tdef.c
    ${MINIZ_DIR}/miniz_tinfl.c
    ${MINIZ_DIR}/miniz_zip.c
)

# GLAD - bundled OpenGL loader (Linux/macOS — Windows uses DX11)
set(GLAD_DIR ${CMAKE_SOURCE_DIR}/libs/glad)
if(NOT WIN32)
    set(GLAD_SOURCES ${GLAD_DIR}/src/gl.c)
else()
    set(GLAD_SOURCES "")
endif()
set(GLAD_INCLUDE ${GLAD_DIR}/include)
message(STATUS "Using bundled GLAD for OpenGL loading")

# SDL3 - try system first, then fetch
if(DRAGONX_USE_SYSTEM_SDL3)
    find_package(SDL3 QUIET)
endif()

if(NOT SDL3_FOUND)
    message(STATUS "SDL3 not found, will fetch from source...")
    include(FetchContent)
    FetchContent_Declare(
        SDL3
        GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
        GIT_TAG 267e681a
        GIT_SHALLOW FALSE
    )
    set(SDL_SHARED OFF CACHE BOOL "" FORCE)
    set(SDL_STATIC ON CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(SDL3)
endif()

# nlohmann/json (header-only) — used for RPC, language files, and legacy theme formats
include(FetchContent)
FetchContent_Declare(
    json
    GIT_REPOSITORY https://github.com/nlohmann/json.git
    GIT_TAG v3.11.3
    GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(json)

# toml++ (header-only) — used for UI schema / theme configuration files
FetchContent_Declare(
    tomlplusplus
    GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
    GIT_TAG v3.4.0
    GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(tomlplusplus)

# SQLite amalgamation - local Explorer block-summary cache
FetchContent_Declare(
    sqlite3
    URL https://www.sqlite.org/2024/sqlite-amalgamation-3450300.zip
    URL_HASH SHA256=ea170e73e447703e8359308ca2e4366a3ae0c4304a8665896f068c736781c651
)
FetchContent_GetProperties(sqlite3)
if(NOT sqlite3_POPULATED)
    FetchContent_Populate(sqlite3)
endif()
file(GLOB SQLITE3_AMALGAMATION_C CONFIGURE_DEPENDS
    ${sqlite3_SOURCE_DIR}/sqlite3.c
    ${sqlite3_SOURCE_DIR}/*/sqlite3.c
)
if(NOT SQLITE3_AMALGAMATION_C)
    message(FATAL_ERROR "SQLite amalgamation source not found")
endif()
list(GET SQLITE3_AMALGAMATION_C 0 SQLITE3_SOURCE_FILE)
get_filename_component(SQLITE3_INCLUDE_DIR ${SQLITE3_SOURCE_FILE} DIRECTORY)
add_library(sqlite3_amalgamation STATIC ${SQLITE3_SOURCE_FILE})
target_include_directories(sqlite3_amalgamation PUBLIC ${SQLITE3_INCLUDE_DIR})
target_compile_definitions(sqlite3_amalgamation PRIVATE
    SQLITE_THREADSAFE=1
    SQLITE_OMIT_LOAD_EXTENSION
)

# libcurl for HTTPS RPC connections (more reliable than cpp-httplib with OpenSSL 3.x)
if(WIN32)
    # For Windows cross-compilation, fetch and build libcurl statically
    message(STATUS "Fetching libcurl for Windows build...")
    FetchContent_Declare(
        curl
        URL https://github.com/curl/curl/releases/download/curl-8_5_0/curl-8.5.0.tar.gz
        URL_HASH SHA256=05fc17ff25b793a437a0906e0484b82172a9f4de02be5ed447e0cab8c3475add
    )
    set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE)
    set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
    set(CURL_STATICLIB ON CACHE BOOL "" FORCE)
    set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
    set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE)
    set(CURL_DISABLE_LDAPS ON CACHE BOOL "" FORCE)
    set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE)  # Use Windows native SSL
    set(CURL_USE_OPENSSL OFF CACHE BOOL "" FORCE)
    set(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE)
    set(CURL_ZLIB OFF CACHE BOOL "" FORCE)
    set(HTTP_ONLY ON CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(curl)
    set(CURL_LIBRARIES libcurl_static)
    set(CURL_INCLUDE_DIRS ${curl_SOURCE_DIR}/include ${curl_BINARY_DIR}/lib/curl)
else()
    find_package(CURL REQUIRED)
    set(CURL_LIBRARIES CURL::libcurl)
    set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
endif()

# libsodium - platform-specific
# Search order per platform:
#   1. Local pre-built in libs/libsodium{-mac,-win}/  (downloaded by scripts/fetch-libsodium.sh)
#   2. System libsodium via pkg-config
#   3. Auto-download from source (requires curl + C compiler for target)
set(SODIUM_LIBRARY "")
set(SODIUM_INCLUDE_DIR "")

if(WIN32)
    # Windows (MinGW cross-compile) — no pkg-config fallback; host pkg-config
    # returns Linux paths that can't be used by MinGW.
    if(EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium-win/lib/libsodium.a)
        set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium-win/lib/libsodium.a)
        set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium-win/include)
    elseif(EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
        set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
        set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium/include)
    endif()
elseif(APPLE)
    # macOS (native or osxcross cross-compile) — use local pre-built only;
    # when cross-compiling, host pkg-config returns wrong paths.
    if(EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium-mac/lib/libsodium.a)
        set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium-mac/lib/libsodium.a)
        set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium-mac/include)
    elseif(EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
        set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
        set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium/include)
    endif()
else()
    # Linux: prefer system libsodium, fall back to local build
    find_package(PkgConfig QUIET)
    if(PKG_CONFIG_FOUND)
        pkg_check_modules(SODIUM QUIET libsodium)
    endif()
    if(NOT SODIUM_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
        set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
        set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium/include)
    endif()
endif()

# Bridge pkg-config variables to the ones used in target_link/include
if(SODIUM_FOUND AND NOT SODIUM_LIBRARY)
    set(SODIUM_LIBRARY ${SODIUM_LIBRARIES})
    set(SODIUM_INCLUDE_DIR ${SODIUM_INCLUDE_DIRS})
endif()

# Final check: if still not found, tell the user how to get it
if(NOT SODIUM_LIBRARY AND NOT SODIUM_FOUND)
    message(WARNING
        "libsodium not found. Encryption features require libsodium.\n"
        "  Linux:   sudo apt install libsodium-dev   (or equivalent)\n"
        "  macOS:   ./scripts/fetch-libsodium.sh --mac\n"
        "  Windows: ./scripts/fetch-libsodium.sh --win")
endif()

message(STATUS "Sodium lib: ${SODIUM_LIBRARY}")

# -----------------------------------------------------------------------------
# ImGui
# -----------------------------------------------------------------------------

set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/libs/imgui)

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_demo.cpp
    ${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp
)

set(IMGUI_HEADERS
    ${IMGUI_DIR}/imgui.h
    ${IMGUI_DIR}/imconfig.h
    ${IMGUI_DIR}/imgui_internal.h
    ${IMGUI_DIR}/backends/imgui_impl_sdl3.h
)

# Platform-specific ImGui backend
if(WIN32)
    list(APPEND IMGUI_SOURCES ${IMGUI_DIR}/backends/imgui_impl_dx11.cpp)
    list(APPEND IMGUI_HEADERS ${IMGUI_DIR}/backends/imgui_impl_dx11.h)
else()
    list(APPEND IMGUI_SOURCES ${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp)
    list(APPEND IMGUI_HEADERS ${IMGUI_DIR}/backends/imgui_impl_opengl3.h)
endif()

# -----------------------------------------------------------------------------
# QR Code library (bundled)
# -----------------------------------------------------------------------------

set(QRCODE_DIR ${CMAKE_SOURCE_DIR}/libs/qrcode)

set(QRCODE_SOURCES
    ${QRCODE_DIR}/BitBuffer.cpp
    ${QRCODE_DIR}/QrCode.cpp
    ${QRCODE_DIR}/QrSegment.cpp
)

# -----------------------------------------------------------------------------
# Application Sources
# -----------------------------------------------------------------------------

set(APP_SOURCES
    src/main.cpp
    src/app.cpp
    src/app_network.cpp
    src/app_security.cpp
    src/app_wizard.cpp
    src/services/network_refresh_service.cpp
    src/services/refresh_scheduler.cpp
    src/services/wallet_security_controller.cpp
    src/services/wallet_security_workflow.cpp
    src/services/wallet_security_workflow_executor.cpp
    src/chat/chat_protocol.cpp
    src/wallet/lite_owned_string.cpp
    src/wallet/lite_rollout_policy.cpp
    src/wallet/lite_client_bridge.cpp
    src/wallet/lite_connection_service.cpp
    src/wallet/lite_diagnostics.cpp
    src/wallet/lite_wallet_controller.cpp
    src/wallet/lite_result_parsers.cpp
    src/wallet/lite_sync_service.cpp
    src/wallet/lite_wallet_gateway.cpp
    src/wallet/lite_wallet_state_mapper.cpp
    src/wallet/lite_wallet_lifecycle_ui_adapter.cpp
    src/wallet/lite_wallet_server_selection_adapter.cpp
    src/wallet/lite_wallet_server_lifecycle_readiness.cpp
    src/wallet/lite_wallet_lifecycle_service.cpp
    src/data/wallet_state.cpp
    src/data/transaction_history_cache.cpp
    src/ui/theme.cpp
    src/ui/theme_loader.cpp
    src/ui/explorer/explorer_block_cache.cpp
    src/ui/material/color_theme.cpp
    src/ui/material/typography.cpp
    src/ui/notifications.cpp
    src/ui/windows/main_window.cpp
    src/ui/windows/balance_tab.cpp
    src/ui/windows/balance_components.cpp
    src/ui/windows/balance_address_list.cpp
    src/ui/windows/balance_recent_tx.cpp
    src/ui/windows/balance_tab_helpers.cpp
    src/ui/windows/send_tab.cpp
    src/ui/windows/receive_tab.cpp
    src/ui/windows/transactions_tab.cpp
    src/ui/windows/mining_tab.cpp
    src/ui/windows/mining_earnings.cpp
    src/ui/windows/mining_stats.cpp
    src/ui/windows/mining_controls.cpp
    src/ui/windows/mining_mode_toggle.cpp
    src/ui/windows/mining_benchmark.cpp
    src/ui/windows/mining_pool_panel.cpp
    src/ui/windows/mining_tab_helpers.cpp
    src/ui/windows/peers_tab.cpp
    src/ui/windows/network_tab.cpp
    src/ui/windows/explorer_tab.cpp
    src/ui/windows/market_tab.cpp
    src/ui/windows/console_tab.cpp
    src/ui/windows/console_command_executor.cpp
    src/ui/windows/console_command_reference.cpp
    src/ui/windows/console_input_model.cpp
    src/ui/windows/console_output_model.cpp
    src/ui/windows/console_tab_helpers.cpp
    src/ui/windows/settings_window.cpp
    src/ui/pages/settings_page.cpp
    src/ui/windows/about_dialog.cpp
    src/ui/windows/key_export_dialog.cpp
    src/ui/windows/transaction_details_dialog.cpp
    src/ui/windows/qr_popup_dialog.cpp
    src/ui/windows/validate_address_dialog.cpp
    src/ui/windows/address_book_dialog.cpp
    src/ui/windows/shield_dialog.cpp
    src/ui/windows/request_payment_dialog.cpp
    src/ui/windows/block_info_dialog.cpp
    src/ui/windows/import_key_dialog.cpp
    src/ui/windows/export_all_keys_dialog.cpp
    src/ui/windows/export_transactions_dialog.cpp
    src/ui/windows/backup_wallet_dialog.cpp
    src/ui/widgets/qr_code.cpp
    src/rpc/rpc_client.cpp
    src/rpc/rpc_worker.cpp
    src/rpc/connection.cpp
    src/config/settings.cpp
    src/data/address_book.cpp
    src/data/exchange_info.cpp
    src/util/logger.cpp
    src/util/async_task_manager.cpp
    src/util/amount_format.cpp
    src/util/address_validation.cpp
    src/util/base64.cpp
    src/util/single_instance.cpp
    src/util/i18n.cpp
    src/util/text_format.cpp
    src/util/platform.cpp
    src/util/payment_uri.cpp
    src/util/texture_loader.cpp
    src/util/noise_texture.cpp
    src/daemon/embedded_daemon.cpp
    src/daemon/daemon_controller.cpp
    src/daemon/lifecycle_adapters.cpp
    src/daemon/xmrig_manager.cpp
    src/util/bootstrap.cpp
    src/util/lite_server_probe.cpp
    src/util/pool_registry_core.cpp
    src/util/pool_stats_service.cpp
    src/util/xmrig_updater.cpp
    src/util/xmrig_updater_core.cpp
    src/util/daemon_updater.cpp
    src/util/daemon_updater_core.cpp
    src/util/secure_vault.cpp
    src/ui/effects/framebuffer.cpp
    src/ui/effects/blur_shader.cpp
    src/ui/effects/noise_texture.cpp
    src/ui/effects/acrylic.cpp
    src/ui/effects/imgui_acrylic.cpp
    src/ui/effects/theme_effects.cpp
    src/ui/effects/low_spec.cpp
    src/ui/schema/color_var_resolver.cpp
    src/ui/schema/element_styles.cpp
    src/ui/schema/ui_schema.cpp
    src/ui/schema/skin_manager.cpp
    src/resources/embedded_resources.cpp
)

# Note: The old -O0 workaround for embedded_resources.cpp is no longer needed.
# With INCBIN (.incbin assembler directive), the compiler never parses the
# binary data — the assembler streams it directly into the object file,
# using near-zero compile-time RAM instead of 12 GB+.

# Platform-specific sources
if(WIN32)
    list(APPEND APP_SOURCES
        src/platform/windows_backdrop.cpp
        src/platform/dx11_context.cpp
    )
endif()

set(APP_HEADERS
    src/app.h
    src/services/network_refresh_service.h
    src/services/refresh_scheduler.h
    src/services/wallet_security_controller.h
    src/services/wallet_security_workflow.h
    src/services/wallet_security_workflow_executor.h
    src/wallet/wallet_capabilities.h
    src/wallet/wallet_backend.h
    src/wallet/lite_owned_string.h
    src/wallet/lite_rollout_policy.h
    src/wallet/lite_client_bridge.h
    src/wallet/lite_connection_service.h
    src/wallet/lite_result_parsers.h
    src/wallet/lite_sync_service.h
    src/wallet/lite_wallet_gateway.h
    src/wallet/lite_wallet_state_mapper.h
    src/wallet/lite_wallet_lifecycle_ui_adapter.h
    src/wallet/lite_wallet_server_selection_adapter.h
    src/wallet/lite_wallet_server_lifecycle_readiness.h
    src/wallet/lite_wallet_lifecycle_service.h
    src/chat/chat_protocol.h
    src/config/version.h
    src/data/wallet_state.h
    src/data/transaction_history_cache.h
    src/ui/theme.h
    src/ui/theme_loader.h
    src/ui/explorer/explorer_block_cache.h
    src/ui/notifications.h
    src/ui/windows/main_window.h
    src/ui/windows/balance_tab.h
    src/ui/windows/balance_address_list.h
    src/ui/windows/balance_recent_tx.h
    src/ui/windows/balance_tab_helpers.h
    src/ui/windows/send_tab.h
    src/ui/windows/receive_tab.h
    src/ui/windows/transactions_tab.h
    src/ui/windows/mining_tab.h
    src/ui/windows/mining_benchmark.h
    src/ui/windows/mining_pool_panel.h
    src/ui/windows/mining_tab_helpers.h
    src/ui/windows/peers_tab.h
    src/ui/windows/explorer_tab.h
    src/ui/windows/market_tab.h
    src/ui/windows/console_command_reference.h
    src/ui/windows/console_input_model.h
    src/ui/windows/console_output_model.h
    src/ui/windows/console_tab.h
    src/ui/windows/console_tab_helpers.h
    src/ui/windows/settings_window.h
    src/ui/windows/about_dialog.h
    src/ui/windows/key_export_dialog.h
    src/ui/windows/transaction_details_dialog.h
    src/ui/windows/qr_popup_dialog.h
    src/ui/windows/validate_address_dialog.h
    src/ui/windows/address_book_dialog.h
    src/ui/windows/shield_dialog.h
    src/ui/windows/request_payment_dialog.h
    src/ui/windows/block_info_dialog.h
    src/ui/windows/import_key_dialog.h
    src/ui/windows/export_all_keys_dialog.h
    src/ui/windows/export_transactions_dialog.h
    src/ui/windows/backup_wallet_dialog.h
    src/ui/widgets/qr_code.h
    src/rpc/rpc_client.h
    src/rpc/rpc_worker.h
    src/rpc/connection.h
    src/rpc/types.h
    src/config/settings.h
    src/data/address_book.h
    src/data/exchange_info.h
    src/util/logger.h
    src/util/async_task_manager.h
    src/util/amount_format.h
    src/util/base64.h
    src/util/single_instance.h
    src/util/i18n.h
    src/util/platform.h
    src/util/payment_uri.h
    src/util/secure_vault.h
    src/daemon/embedded_daemon.h
    src/daemon/daemon_controller.h
    src/daemon/lifecycle_adapters.h
    src/daemon/xmrig_manager.h
    src/ui/effects/framebuffer.h
    src/ui/effects/blur_shader.h
    src/ui/effects/noise_texture.h
    src/ui/effects/acrylic.h
)

# Platform-specific headers
if(WIN32)
    list(APPEND APP_HEADERS
        src/platform/windows_backdrop.h
        src/platform/dx11_context.h
    )
endif()

# -----------------------------------------------------------------------------
# Executable
# -----------------------------------------------------------------------------

# Windows application icon + VERSIONINFO (.rc -> .res -> linked into .exe)
if(WIN32)
    set(OBSIDIAN_ICO_PATH "${CMAKE_SOURCE_DIR}/res/img/ObsidianDragon.ico")
    # Generate manifest with version from project()
    configure_file(
        ${CMAKE_SOURCE_DIR}/res/ObsidianDragon.manifest.in
        ${CMAKE_SOURCE_DIR}/res/ObsidianDragon.manifest
        @ONLY
    )
    set(OBSIDIAN_MANIFEST_PATH "${CMAKE_SOURCE_DIR}/res/ObsidianDragon.manifest")
    # Generate .rc with version from project()
    configure_file(
        ${CMAKE_SOURCE_DIR}/res/ObsidianDragon.rc
        ${CMAKE_BINARY_DIR}/generated/ObsidianDragon.rc
        @ONLY
    )
    set(WIN_RC_FILE ${CMAKE_BINARY_DIR}/generated/ObsidianDragon.rc)
endif()

# Generate version values from the single project(VERSION ...) declaration.
# Keep the build-specific app name in the build tree so full/lite configures do
# not rewrite a tracked source header.
configure_file(
    ${CMAKE_SOURCE_DIR}/src/config/version.h.in
    ${CMAKE_BINARY_DIR}/generated/dragonx_generated_version.h
    @ONLY
)

# Generate INCBIN font embedding source with absolute paths to .ttf files
configure_file(
    ${CMAKE_SOURCE_DIR}/src/embedded/embedded_fonts.cpp.in
    ${CMAKE_BINARY_DIR}/generated/embedded_fonts.cpp
    @ONLY
)

# INCBIN uses .incbin assembler directives that reference font files at
# assembly time — CMake doesn't track these implicit dependencies.
# Tell CMake that the generated source depends on the actual font binaries
# so a font file change triggers recompilation.
set_source_files_properties(
    ${CMAKE_BINARY_DIR}/generated/embedded_fonts.cpp
    PROPERTIES OBJECT_DEPENDS
    "${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-R.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Light.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Medium.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/MaterialIcons-Regular.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/NotoSansCJK-Subset.ttf"
)

add_executable(ObsidianDragon
    ${APP_SOURCES}
    ${CMAKE_BINARY_DIR}/generated/embedded_fonts.cpp
    ${IMGUI_SOURCES}
    ${QRCODE_SOURCES}
    ${GLAD_SOURCES}
    ${MINIZ_SOURCES}
    ${WIN_RC_FILE}
)

set_target_properties(ObsidianDragon PROPERTIES OUTPUT_NAME "${DRAGONX_BINARY_NAME}")

target_include_directories(ObsidianDragon PRIVATE
    ${CMAKE_SOURCE_DIR}/src
    ${CMAKE_SOURCE_DIR}/src/embedded
    ${CMAKE_SOURCE_DIR}/src/resources
    ${CMAKE_SOURCE_DIR}/libs
    ${CMAKE_BINARY_DIR}/generated
    ${IMGUI_DIR}
    ${IMGUI_DIR}/backends
    ${QRCODE_DIR}
    ${SODIUM_INCLUDE_DIR}
    ${GLAD_INCLUDE}
    ${CURL_INCLUDE_DIRS}
    ${MINIZ_DIR}
)

target_link_libraries(ObsidianDragon PRIVATE
    SDL3::SDL3
    nlohmann_json::nlohmann_json
    tomlplusplus::tomlplusplus
    sqlite3_amalgamation
    ${CURL_LIBRARIES}
    ${SODIUM_LIBRARY}
)

if(DRAGONX_LITE_BACKEND_READY)
    target_link_libraries(ObsidianDragon PRIVATE dragonx_lite_backend ${DRAGONX_LITE_BACKEND_EXTRA_LIBS})

    # Real-backend smoke tool (only built when a real lite backend is linked).
    add_executable(lite_smoke
        tools/lite_smoke.cpp
        src/wallet/lite_client_bridge.cpp
        src/wallet/lite_owned_string.cpp
        src/wallet/lite_rollout_policy.cpp
        src/wallet/lite_connection_service.cpp
        src/wallet/lite_result_parsers.cpp
    )
    target_include_directories(lite_smoke PRIVATE
        ${CMAKE_SOURCE_DIR}/src
        ${CMAKE_BINARY_DIR}/generated
        ${SODIUM_INCLUDE_DIR}
    )
    target_compile_definitions(lite_smoke PRIVATE DRAGONX_ENABLE_LITE_BACKEND=1)
    target_link_libraries(lite_smoke PRIVATE
        dragonx_lite_backend ${DRAGONX_LITE_BACKEND_EXTRA_LIBS}
        nlohmann_json::nlohmann_json
        ${SODIUM_LIBRARY}
    )
    if(UNIX)
        target_link_libraries(lite_smoke PRIVATE ${CMAKE_DL_LIBS} pthread)
    endif()

    # Real-backend SEND smoke tool — drives the exact GUI send path (bridge.execute("send", ...)).
    add_executable(lite_send_smoke
        tools/lite_send_smoke.cpp
        src/wallet/lite_client_bridge.cpp
        src/wallet/lite_owned_string.cpp
        src/wallet/lite_rollout_policy.cpp
        src/wallet/lite_connection_service.cpp
        src/wallet/lite_result_parsers.cpp
    )
    target_include_directories(lite_send_smoke PRIVATE
        ${CMAKE_SOURCE_DIR}/src
        ${CMAKE_BINARY_DIR}/generated
        ${SODIUM_INCLUDE_DIR}
    )
    target_compile_definitions(lite_send_smoke PRIVATE DRAGONX_ENABLE_LITE_BACKEND=1)
    target_link_libraries(lite_send_smoke PRIVATE
        dragonx_lite_backend ${DRAGONX_LITE_BACKEND_EXTRA_LIBS}
        nlohmann_json::nlohmann_json
        ${SODIUM_LIBRARY}
    )
    if(UNIX)
        target_link_libraries(lite_send_smoke PRIVATE ${CMAKE_DL_LIBS} pthread)
    endif()
endif()

# Platform-specific settings
if(WIN32)
    target_link_libraries(ObsidianDragon PRIVATE ws2_32 winmm imm32 version setupapi dwmapi crypt32 wldap32 psapi iphlpapi d3d11 dxgi d3dcompiler dcomp)
    # Hide console window in release builds
    if(CMAKE_BUILD_TYPE STREQUAL "Release")
        set_target_properties(ObsidianDragon PROPERTIES WIN32_EXECUTABLE TRUE)
    endif()
elseif(APPLE)
    target_link_libraries(ObsidianDragon PRIVATE "-framework Cocoa" "-framework IOKit" "-framework CoreVideo" "-framework OpenGL")
    # When cross-compiling with osxcross, link the compiler-rt builtins (provides ___isPlatformVersionAtLeast etc.)
    if(CMAKE_CROSSCOMPILING AND DEFINED OSXCROSS_COMPILER_RT)
        target_link_libraries(ObsidianDragon PRIVATE "${OSXCROSS_COMPILER_RT}")
    endif()
elseif(UNIX)
    find_package(Threads REQUIRED)
    target_link_libraries(ObsidianDragon PRIVATE OpenGL::GL Threads::Threads ${CMAKE_DL_LIBS})
endif()

# Compiler warnings
if(MSVC)
    target_compile_options(ObsidianDragon PRIVATE /W4)
else()
    target_compile_options(ObsidianDragon PRIVATE -Wall -Wextra -Wpedantic)
endif()

# Compile definitions
target_compile_definitions(ObsidianDragon PRIVATE
    DRAGONX_DEBUG
    DRAGONX_LITE_BUILD=$<BOOL:${DRAGONX_BUILD_LITE}>
    DRAGONX_ENABLE_EMBEDDED_DAEMON=$<BOOL:${DRAGONX_ENABLE_EMBEDDED_DAEMON}>
    DRAGONX_ENABLE_LITE_BACKEND=$<BOOL:${DRAGONX_LITE_BACKEND_READY}>
    DRAGONX_ENABLE_CHAT=$<BOOL:${DRAGONX_ENABLE_CHAT}>
)
if(WIN32)
    target_compile_definitions(ObsidianDragon PRIVATE DRAGONX_USE_DX11)
else()
    target_compile_definitions(ObsidianDragon PRIVATE DRAGONX_HAS_GLAD)
endif()

add_executable(HushChatFixtureCheck
    tools/hushchat_fixture_check.cpp
    src/chat/chat_protocol.cpp
)

target_include_directories(HushChatFixtureCheck PRIVATE
    ${CMAKE_SOURCE_DIR}/src
    ${SODIUM_INCLUDE_DIR}
)

target_link_libraries(HushChatFixtureCheck PRIVATE
    nlohmann_json::nlohmann_json
    ${SODIUM_LIBRARY}
)

target_compile_definitions(HushChatFixtureCheck PRIVATE
    DRAGONX_ENABLE_CHAT=0
)

# -----------------------------------------------------------------------------
# Copy resources
# -----------------------------------------------------------------------------

# Copy font file - try local first, then SilentDragonX
if(EXISTS ${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-R.ttf)
    configure_file(
        ${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-R.ttf
        ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/fonts/Ubuntu-R.ttf
        COPYONLY
    )
elseif(EXISTS ${CMAKE_SOURCE_DIR}/../SilentDragonX/res/Ubuntu-R.ttf)
    configure_file(
        ${CMAKE_SOURCE_DIR}/../SilentDragonX/res/Ubuntu-R.ttf
        ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/fonts/Ubuntu-R.ttf
        COPYONLY
    )
endif()

# Copy language files at BUILD time (not just cmake configure time)
# so edits to res/lang/*.json are picked up by 'make' without re-running cmake.
file(GLOB LANG_FILES ${CMAKE_SOURCE_DIR}/res/lang/*.json)
if(LANG_FILES)
    find_program(XXD_EXECUTABLE NAMES xxd)
    if(NOT XXD_EXECUTABLE)
        message(WARNING "xxd not found; runtime language JSON files will be copied, but embedded build/generated/embedded/lang_*.h files will not be regenerated")
    endif()
    file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated/embedded)

    foreach(LANG_FILE ${LANG_FILES})
        get_filename_component(LANG_FILENAME ${LANG_FILE} NAME)
        add_custom_command(
            OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/lang/${LANG_FILENAME}
            COMMAND ${CMAKE_COMMAND} -E copy_if_different
                ${LANG_FILE}
                ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/lang/${LANG_FILENAME}
            DEPENDS ${LANG_FILE}
            COMMENT "Copying ${LANG_FILENAME}"
        )
        list(APPEND LANG_OUTPUTS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/lang/${LANG_FILENAME})

        # Also regenerate the embedded header so the binary always has fresh translations
        if(XXD_EXECUTABLE)
            get_filename_component(LANG_CODE ${LANG_FILENAME} NAME_WE)
            set(LANG_HEADER ${CMAKE_BINARY_DIR}/generated/embedded/lang_${LANG_CODE}.h)
            add_custom_command(
                OUTPUT ${LANG_HEADER}
                COMMAND ${XXD_EXECUTABLE} -i "res/lang/${LANG_FILENAME}" > "${LANG_HEADER}"
                DEPENDS ${LANG_FILE}
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
                COMMENT "Embedding lang_${LANG_CODE}.h"
            )
            list(APPEND LANG_OUTPUTS ${LANG_HEADER})
        endif()
    endforeach()
    add_custom_target(copy_langs ALL DEPENDS ${LANG_OUTPUTS})
    add_dependencies(ObsidianDragon copy_langs)
    message(STATUS "  Language files: ${LANG_FILES}")
endif()

# Embed ui.toml into the binary so it's always available at runtime
include(${CMAKE_SOURCE_DIR}/cmake/EmbedResources.cmake)
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)
embed_resource(
    ${CMAKE_SOURCE_DIR}/res/themes/ui.toml
    ${CMAKE_BINARY_DIR}/generated/ui_toml_embedded.h
    ui_toml
)
embed_resource(
    ${CMAKE_SOURCE_DIR}/res/default_banlist.txt
    ${CMAKE_BINARY_DIR}/generated/default_banlist_embedded.h
    default_banlist
)

# Note: xmrig is embedded via build.sh (embedded_data.h) for Windows builds,
# following the same pattern as daemon embedding.

# Expand and copy theme files at BUILD time — skin files get layout sections
# from ui.toml appended automatically so users can see/edit all properties.
# Source skin files stay minimal; the merged output goes to build/bin/res/themes/.
find_package(Python3 QUIET COMPONENTS Interpreter)
if(NOT Python3_FOUND)
    find_program(Python3_EXECUTABLE NAMES python3 python)
endif()
file(GLOB THEME_FILES ${CMAKE_SOURCE_DIR}/res/themes/*.toml)
if(THEME_FILES AND Python3_EXECUTABLE)
    add_custom_command(
        OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/.expanded
        COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/expand_themes.py
            ${CMAKE_SOURCE_DIR}/res/themes
            ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes
        COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/.expanded
        DEPENDS ${THEME_FILES} ${CMAKE_SOURCE_DIR}/scripts/expand_themes.py
        COMMENT "Expanding theme files (merging layout from ui.toml)"
    )
    add_custom_target(copy_themes ALL DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/.expanded)
    add_dependencies(ObsidianDragon copy_themes)
    message(STATUS "  Theme files: ${THEME_FILES} (build-time expansion via Python)")
elseif(THEME_FILES)
    # Fallback: plain copy if Python is not available
    message(WARNING "Python3 not found; copying theme files without expand_themes.py layout merge")
    foreach(THEME_FILE ${THEME_FILES})
        get_filename_component(THEME_FILENAME ${THEME_FILE} NAME)
        add_custom_command(
            OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/${THEME_FILENAME}
            COMMAND ${CMAKE_COMMAND} -E copy_if_different
                ${THEME_FILE}
                ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/${THEME_FILENAME}
            DEPENDS ${THEME_FILE}
            COMMENT "Copying ${THEME_FILENAME}"
        )
        list(APPEND THEME_OUTPUTS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/${THEME_FILENAME})
    endforeach()
    add_custom_target(copy_themes ALL DEPENDS ${THEME_OUTPUTS})
    add_dependencies(ObsidianDragon copy_themes)
    message(STATUS "  Theme files: ${THEME_FILES} (plain copy, Python not found)")
endif()

# Copy image files (including backgrounds/ subdirectories and logos/)
file(GLOB IMG_ROOT_FILES ${CMAKE_SOURCE_DIR}/res/img/*.png ${CMAKE_SOURCE_DIR}/res/img/*.jpg ${CMAKE_SOURCE_DIR}/res/img/*.ico)
file(GLOB IMG_BG_TEXTURE_FILES ${CMAKE_SOURCE_DIR}/res/img/backgrounds/texture/*.png ${CMAKE_SOURCE_DIR}/res/img/backgrounds/texture/*.jpg)
file(GLOB IMG_BG_GRADIENT_FILES ${CMAKE_SOURCE_DIR}/res/img/backgrounds/gradient/*.png ${CMAKE_SOURCE_DIR}/res/img/backgrounds/gradient/*.jpg)
file(GLOB IMG_LOGO_FILES ${CMAKE_SOURCE_DIR}/res/img/logos/*.png ${CMAKE_SOURCE_DIR}/res/img/logos/*.jpg)
foreach(IMG_FILE ${IMG_ROOT_FILES})
    get_filename_component(IMG_FILENAME ${IMG_FILE} NAME)
    configure_file(${IMG_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/img/${IMG_FILENAME} COPYONLY)
endforeach()
foreach(IMG_FILE ${IMG_BG_TEXTURE_FILES})
    get_filename_component(IMG_FILENAME ${IMG_FILE} NAME)
    configure_file(${IMG_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/img/backgrounds/texture/${IMG_FILENAME} COPYONLY)
endforeach()
foreach(IMG_FILE ${IMG_BG_GRADIENT_FILES})
    get_filename_component(IMG_FILENAME ${IMG_FILE} NAME)
    configure_file(${IMG_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/img/backgrounds/gradient/${IMG_FILENAME} COPYONLY)
endforeach()
foreach(IMG_FILE ${IMG_LOGO_FILES})
    get_filename_component(IMG_FILENAME ${IMG_FILE} NAME)
    configure_file(${IMG_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/img/logos/${IMG_FILENAME} COPYONLY)
endforeach()
message(STATUS "  Image files: ${IMG_ROOT_FILES} ${IMG_BG_TEXTURE_FILES} ${IMG_BG_GRADIENT_FILES} ${IMG_LOGO_FILES}")

# -----------------------------------------------------------------------------
# Install
# -----------------------------------------------------------------------------

install(TARGETS ObsidianDragon
    RUNTIME DESTINATION bin
)

install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res
    DESTINATION share/${DRAGONX_BINARY_NAME}
    OPTIONAL
)

# -----------------------------------------------------------------------------
# Tests
# -----------------------------------------------------------------------------

if(BUILD_TESTING)
    add_executable(ObsidianDragonTests
        tests/test_phase4.cpp
        src/services/network_refresh_service.cpp
        src/services/refresh_scheduler.cpp
        src/services/wallet_security_controller.cpp
        src/services/wallet_security_workflow.cpp
        src/services/wallet_security_workflow_executor.cpp
        src/chat/chat_protocol.cpp
        src/wallet/lite_owned_string.cpp
        src/wallet/lite_rollout_policy.cpp
        src/wallet/lite_client_bridge.cpp
        src/wallet/lite_connection_service.cpp
        src/wallet/lite_diagnostics.cpp
        src/wallet/lite_wallet_controller.cpp
        src/wallet/lite_result_parsers.cpp
        src/wallet/lite_sync_service.cpp
        src/wallet/lite_wallet_gateway.cpp
        src/wallet/lite_wallet_state_mapper.cpp
        src/wallet/lite_wallet_lifecycle_ui_adapter.cpp
        src/wallet/lite_wallet_server_selection_adapter.cpp
        src/wallet/lite_wallet_server_lifecycle_readiness.cpp
        src/wallet/lite_wallet_lifecycle_service.cpp
        src/ui/explorer/explorer_block_cache.cpp
        src/ui/windows/balance_address_list.cpp
        src/ui/windows/balance_recent_tx.cpp
        src/ui/windows/console_input_model.cpp
        src/ui/windows/console_output_model.cpp
        src/ui/windows/console_tab_helpers.cpp
        src/ui/windows/mining_benchmark.cpp
        src/ui/windows/mining_pool_panel.cpp
        src/ui/windows/mining_tab_helpers.cpp
        src/util/payment_uri.cpp
        src/util/amount_format.cpp
        src/util/address_validation.cpp
        src/util/i18n.cpp
        src/util/text_format.cpp
        src/data/wallet_state.cpp
        src/data/transaction_history_cache.cpp
        src/daemon/lifecycle_adapters.cpp
        src/rpc/connection.cpp
        src/config/settings.cpp
        src/resources/embedded_resources.cpp
        src/util/secure_vault.cpp
        src/util/platform.cpp
        src/util/logger.cpp
        src/util/lite_server_probe.cpp
        src/util/pool_registry_core.cpp
        src/util/xmrig_updater.cpp
        src/util/xmrig_updater_core.cpp
        src/util/daemon_updater.cpp
        src/util/daemon_updater_core.cpp
        ${MINIZ_SOURCES}
    )

    target_include_directories(ObsidianDragonTests PRIVATE
        ${CMAKE_SOURCE_DIR}/src
        ${CMAKE_SOURCE_DIR}/src/resources
        ${CMAKE_SOURCE_DIR}/libs
        ${CMAKE_BINARY_DIR}/generated
        ${IMGUI_DIR}
        ${SODIUM_INCLUDE_DIR}
        ${CURL_INCLUDE_DIRS}
        ${MINIZ_DIR}
    )

    target_link_libraries(ObsidianDragonTests PRIVATE
        nlohmann_json::nlohmann_json
        sqlite3_amalgamation
        ${SODIUM_LIBRARY}
        ${CURL_LIBRARIES}
    )

    target_compile_definitions(ObsidianDragonTests PRIVATE
        DRAGONX_ENABLE_CHAT=$<BOOL:${DRAGONX_ENABLE_CHAT}>
        DRAGONX_LITE_BUILD=$<BOOL:${DRAGONX_BUILD_LITE}>
        DRAGONX_ENABLE_EMBEDDED_DAEMON=$<BOOL:${DRAGONX_ENABLE_EMBEDDED_DAEMON}>
        DRAGONX_ENABLE_LITE_BACKEND=$<BOOL:${DRAGONX_LITE_BACKEND_READY}>
        DRAGONX_TEST_FIXTURE_DIR="${CMAKE_SOURCE_DIR}/tests/fixtures"
    )

    if(DRAGONX_LITE_BACKEND_READY)
        target_link_libraries(ObsidianDragonTests PRIVATE dragonx_lite_backend ${DRAGONX_LITE_BACKEND_EXTRA_LIBS})
    endif()

    if(UNIX)
        target_link_libraries(ObsidianDragonTests PRIVATE ${CMAKE_DL_LIBS})
    endif()

    add_test(NAME ObsidianDragonPhase4Tests COMMAND ObsidianDragonTests)
endif()

# -----------------------------------------------------------------------------
# Summary
# -----------------------------------------------------------------------------

message(STATUS "")
message(STATUS "DragonX ImGui Wallet Configuration:")
message(STATUS "  Version:        ${DRAGONX_APP_VERSION}${DRAGONX_APP_VERSION_SUFFIX} (${DRAGONX_APP_NAME})")
message(STATUS "  Build type:     ${CMAKE_BUILD_TYPE}")
message(STATUS "  C++ Standard:   ${CMAKE_CXX_STANDARD}")
message(STATUS "  ImGui dir:      ${IMGUI_DIR}")
message(STATUS "  SDL3 found:     ${SDL3_FOUND}")
message(STATUS "  Sodium lib:     ${SODIUM_LIBRARY}")
message(STATUS "  Lite build:     ${DRAGONX_BUILD_LITE}")
message(STATUS "  Lite requested: ${DRAGONX_ENABLE_LITE_BACKEND}")
message(STATUS "  Lite backend:   ${DRAGONX_LITE_BACKEND_READY}")
message(STATUS "  Lite lib:       ${DRAGONX_LITE_BACKEND_LIBRARY}")
message(STATUS "  Lite symbols:   ${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
message(STATUS "  Lite manifest:  ${DRAGONX_LITE_BACKEND_MANIFEST}")
message(STATUS "  Lite signature: ${DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE}")
message(STATUS "")
