cmake_minimum_required(VERSION 3.24)
project(live-caption LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_AUTOMOC ON)
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()

option(LC_BUILD_TESTS "构建单元测试" ON)

# ---------------------------------------------------------------------------
# llama.cpp / libmtmd 的搜索路径
#
# Debian/Deepin 把 llama 的 CMake config 放在非标准目录 cmake-private/，
# 而 pkg-config 的 llama.pc 又漏了 -lmtmd。所以这里把两个路径都塞进搜索前缀，
# 并单独用 find_library 找 mtmd。
# ---------------------------------------------------------------------------
foreach(_p
        /usr/lib/x86_64-linux-gnu/llama
        /usr/lib/x86_64-linux-gnu/llama/cmake-private
        /usr/lib/x86_64-linux-gnu/llama/pkgconfig)
    if(EXISTS "${_p}")
        list(APPEND CMAKE_PREFIX_PATH "${_p}")
        set(ENV{PKG_CONFIG_PATH} "${_p}:$ENV{PKG_CONFIG_PATH}")
    endif()
endforeach()

find_package(Qt6 REQUIRED COMPONENTS Core Widgets Network)
find_package(PkgConfig REQUIRED)
find_package(Threads REQUIRED)

pkg_check_modules(SAMPLERATE REQUIRED IMPORTED_TARGET samplerate)
pkg_check_modules(PULSE      REQUIRED IMPORTED_TARGET libpulse)

# --- llama：优先系统 pkg-config，其次 CMake config，最后 FetchContent 兜底 ---
pkg_check_modules(LLAMA QUIET IMPORTED_TARGET llama)
if(LLAMA_FOUND)
    set(LC_LLAMA_TARGET PkgConfig::LLAMA)
    message(STATUS "llama: 使用系统 pkg-config (${LLAMA_VERSION})")
else()
    message(STATUS "llama: 系统未找到，改用 FetchContent 拉取固定版本")
    include(FetchContent)
    set(FETCHCONTENT_TRY_FIND_PACKAGE_MODE ALWAYS)   # 先 find_package，失败再拉源码
    FetchContent_Declare(llama
        GIT_REPOSITORY https://github.com/ggml-org/llama.cpp.git
        GIT_TAG        b9951
        FIND_PACKAGE_ARGS NAMES llama CONFIG)
    set(LLAMA_BUILD_TESTS    OFF CACHE BOOL "" FORCE)
    set(LLAMA_BUILD_SERVER   OFF CACHE BOOL "" FORCE)
    set(LLAMA_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
    FetchContent_MakeAvailable(llama)
    set(LC_LLAMA_TARGET llama)
endif()

# --- mtmd：llama.pc 里没有，单独找 ---
find_library(LC_MTMD_LIB NAMES mtmd HINTS /usr/lib/x86_64-linux-gnu/llama)
if(NOT LC_MTMD_LIB)
    message(FATAL_ERROR "找不到 libmtmd（应随 libllama0 一起安装）")
endif()
message(STATUS "mtmd: ${LC_MTMD_LIB}")

# ---------------------------------------------------------------------------
# sherpa-onnx（X-ASR / zipformer / Nemotron 等流式 ONNX 模型）
#
# 注意：官方预编译库是用旧 C++ ABI 编译的，所以只能用它的 C API。
# 安装：把官方的 sherpa-onnx-vX.Y.Z-linux-x64-shared.tar.bz2 解到某个目录，
#       用 -DSHERPA_ONNX_ROOT=<dir> 指定（默认见下）。
# ---------------------------------------------------------------------------
set(SHERPA_ONNX_ROOT "$ENV{HOME}/.local/opt/sherpa-onnx" CACHE PATH "sherpa-onnx 安装目录")
set(LC_HAS_SHERPA OFF)
if(EXISTS "${SHERPA_ONNX_ROOT}/include/sherpa-onnx/c-api/c-api.h")
    find_library(LC_SHERPA_C_API NAMES sherpa-onnx-c-api
                 HINTS "${SHERPA_ONNX_ROOT}/lib" REQUIRED)
    find_library(LC_SHERPA_ORT NAMES onnxruntime
                 HINTS "${SHERPA_ONNX_ROOT}/lib" REQUIRED)
    set(LC_HAS_SHERPA ON)
    message(STATUS "sherpa-onnx: ${SHERPA_ONNX_ROOT}")
else()
    message(WARNING "未找到 sherpa-onnx（${SHERPA_ONNX_ROOT}），流式 ONNX 后端将不可用")
endif()

# ---------------------------------------------------------------------------
# core：全部业务逻辑，刻意不依赖 Qt（方便测试与复用）
# ---------------------------------------------------------------------------
add_library(livecaption_core STATIC
    src/core/transcript.cpp
    src/core/audio.cpp
    src/core/qwen3asr.cpp
    src/core/streamingasr.cpp
    src/core/livepipeline.cpp
)
if(LC_HAS_SHERPA)
    target_sources(livecaption_core PRIVATE src/core/sherpaasr.cpp)
    target_compile_definitions(livecaption_core PUBLIC LC_HAVE_SHERPA=1)
    target_include_directories(livecaption_core PRIVATE "${SHERPA_ONNX_ROOT}/include")
    target_link_libraries(livecaption_core PUBLIC ${LC_SHERPA_C_API} ${LC_SHERPA_ORT})
endif()
target_include_directories(livecaption_core PUBLIC src/core)
target_link_libraries(livecaption_core PUBLIC
    Threads::Threads
    PkgConfig::SAMPLERATE
    PkgConfig::PULSE
    ${LC_LLAMA_TARGET}
    ${LC_MTMD_LIB}
)

# ---------------------------------------------------------------------------
# models：模型注册表与存储（Qt；注册表编译进二进制）
# ---------------------------------------------------------------------------
add_library(livecaption_models STATIC
    src/models/modelstore.cpp
    src/models/modeldownloader.cpp
)
target_include_directories(livecaption_models PUBLIC src/models)
target_link_libraries(livecaption_models PUBLIC Qt6::Core Qt6::Network)
qt_add_resources(livecaption_models "registry"
    PREFIX "/"
    BASE "${CMAKE_CURRENT_SOURCE_DIR}"
    FILES data/models.json)


# ---------------------------------------------------------------------------
# ui：悬浮窗与 Qt 适配层
# ---------------------------------------------------------------------------
add_library(livecaption_ui STATIC
    src/ui/captionwindow.cpp
    src/ui/livetranscriber.cpp
    src/ui/appsettings.cpp
    src/ui/settingsdialog.cpp
)
target_include_directories(livecaption_ui PUBLIC src/ui)
target_link_libraries(livecaption_ui PUBLIC livecaption_core livecaption_models Qt6::Widgets)

# ---------------------------------------------------------------------------
# 可执行程序
# ---------------------------------------------------------------------------
# GUI：悬浮字幕窗
add_executable(live-caption apps/gui/main.cpp)
target_include_directories(live-caption PRIVATE apps/common)
target_link_libraries(live-caption PRIVATE livecaption_ui livecaption_models)

# CLI：文件 / 流式文件 / 命令行实时；不链接 Qt Widgets
add_executable(live-caption-cli apps/cli/main.cpp)
target_include_directories(live-caption-cli PRIVATE apps/common)
target_link_libraries(live-caption-cli PRIVATE livecaption_core livecaption_models)

if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    foreach(_t livecaption_core livecaption_ui livecaption_models live-caption live-caption-cli)
        target_compile_options(${_t} PRIVATE -Wall -Wextra -Wno-unused-parameter)
    endforeach()
endif()

# ---------------------------------------------------------------------------
if(LC_BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()
