cmake_minimum_required(VERSION 3.24)

project(r_audio_player VERSION 2.3.0 LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(APP_ID r_audio_player)
set(APP_NAME "r audio player")
file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/version.txt" CONTENT "${PROJECT_VERSION}\n")

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

find_package(Qt6 REQUIRED COMPONENTS Widgets Network Concurrent OpenGLWidgets)
find_package(Python3 COMPONENTS Interpreter QUIET)
option(R_AUDIO_PLAYER_BUNDLE_LYRICSYNC_ML "Bundle lyricsync ml tools" ON)
option(R_AUDIO_PLAYER_SETUP_LYRICSYNC_ML_AFTER_BUILD "Set up the lyricsync ml environment automatically after building" OFF)
find_package(TagLib CONFIG QUIET)
if(NOT TagLib_FOUND)
    find_package(PkgConfig QUIET)
    if(PkgConfig_FOUND)
        pkg_check_modules(TAGLIB QUIET IMPORTED_TARGET taglib)
    endif()
endif()
if(NOT TagLib_FOUND AND NOT TARGET PkgConfig::TAGLIB)
    message(FATAL_ERROR "taglib wasn't found; for vcpkg builds, run scripts/build-windows.ps1 -installdeps or scripts/build-linux.sh --install-deps once. normal builds keep VCPKG_MANIFEST_INSTALL=OFF & do not reinstall packages when the local vcpkg_installed tree is complete")
endif()
find_package(ZLIB REQUIRED)
find_package(FFmpeg REQUIRED COMPONENTS avformat avcodec avutil swresample)

if(TARGET TagLib::tag)
    set(TAGLIB_TARGET TagLib::tag)
elseif(TARGET TagLib::TagLib)
    set(TAGLIB_TARGET TagLib::TagLib)
elseif(TARGET tag)
    set(TAGLIB_TARGET tag)
elseif(TARGET PkgConfig::TAGLIB)
    set(TAGLIB_TARGET PkgConfig::TAGLIB)
else()
    message(FATAL_ERROR "taglib target not found")
endif()

find_path(MINIAUDIO_INCLUDE_DIR miniaudio.h REQUIRED)

qt_add_executable(r_audio_player
    src/main.cpp
    src/lowend/LowEndLexer.c
    src/lowend/LowEndLexer.h
    src/lowend/LowEndValue.c
    src/lowend/LowEndValue.h
    src/app/AppSettings.cpp
    src/app/AppSettings.h
    src/app/BackgroundImage.cpp
    src/app/BackgroundImage.h
    src/app/ReleaseNotes.cpp
    src/app/ReleaseNotes.h
    src/app/PortablePaths.cpp
    src/app/PortablePaths.h
    src/app/DiscordRichPresence.cpp
    src/app/DiscordRichPresence.h
    src/app/ListenAlongService.cpp
    src/app/ListenAlongService.h
    src/app/Skin.cpp
    src/app/Skin.h
    src/audio/DspProcessor.cpp
    src/audio/DspProcessor.h
    src/cd/RipCore.h
    src/audio/LyricsSync.cpp
    src/audio/LyricsSync.h
    src/audio/VocalActivityModel.cpp
    src/audio/VocalActivityModel.h
    src/audio/PlaybackEngine.cpp
    src/audio/PlaybackEngine.h
    src/media/ArtworkReader.cpp
    src/media/ArtworkReader.h
    src/media/AudioDecoder.cpp
    src/media/AudioDecoder.h
    src/media/LibraryScanner.cpp
    src/media/LibraryScanner.h
    src/media/MediaTypes.h
    src/ui/ClickSlider.h
    src/ui/ExactRowScroll.h
    src/ui/GpuBackgroundWidget.cpp
    src/ui/GpuBackgroundWidget.h
    src/ui/PlaylistTrackTreeWidget.cpp
    src/ui/PlaylistTrackTreeWidget.h
    src/ui/DspPanel.cpp
    src/ui/DspPanel.h
    src/ui/MainWindow.cpp
    src/ui/MainWindowRemote.cpp
    src/ui/MainWindowUpdates.cpp
    src/ui/MainWindowCd.cpp
    src/ui/MainWindowTags.cpp
    src/ui/MainWindowFileTools.cpp
    src/ui/MainWindowPlaylists.cpp
    src/ui/MainWindow.h
    src/ui/SettingsDialog.cpp
    src/ui/SettingsDialog.h
    resources/resources.qrc
)

if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
    add_library(r_cd_ripper STATIC
        src/cd/RipCore.c
        src/cd/RipCore.h
    )
else()
    add_library(r_cd_ripper SHARED
        src/cd/RipCore.c
        src/cd/RipCore.h
    )
endif()
target_compile_definitions(r_cd_ripper PRIVATE RAP_CD_RIPPER_BUILD=1)
set_target_properties(r_cd_ripper PROPERTIES OUTPUT_NAME "r_cd_ripper")
if(WIN32)
    set_target_properties(r_cd_ripper PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
target_include_directories(r_cd_ripper PRIVATE
    ${MINIAUDIO_INCLUDE_DIR}
)
target_link_libraries(r_cd_ripper PRIVATE
    FFmpeg::avformat
    FFmpeg::avcodec
    FFmpeg::avutil
    FFmpeg::swresample
)
if(WIN32)
    target_compile_definitions(r_cd_ripper PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
endif()
if(UNIX AND NOT APPLE)
    target_link_libraries(r_cd_ripper PRIVATE pthread m)
endif()

if(WIN32)
    configure_file(resources/app.rc.in ${CMAKE_BINARY_DIR}/app.rc @ONLY)
    target_sources(r_audio_player PRIVATE ${CMAKE_BINARY_DIR}/app.rc)
    set_target_properties(r_audio_player PROPERTIES WIN32_EXECUTABLE TRUE OUTPUT_NAME "${APP_NAME}")
    target_compile_definitions(r_audio_player PRIVATE UNICODE _UNICODE NOMINMAX)
elseif(APPLE)
    set_target_properties(r_audio_player PROPERTIES OUTPUT_NAME "${APP_NAME}" MACOSX_BUNDLE TRUE MACOSX_BUNDLE_BUNDLE_NAME "${APP_NAME}" MACOSX_BUNDLE_GUI_IDENTIFIER "com.rshc.r-audio-player")
else()
    set_target_properties(r_audio_player PROPERTIES OUTPUT_NAME "${APP_ID}")
endif()

target_include_directories(r_audio_player PRIVATE
    src
    ${MINIAUDIO_INCLUDE_DIR}
)

target_compile_definitions(r_audio_player PRIVATE
    QT_NO_KEYWORDS
    QT_DISABLE_DEPRECATED_BEFORE=0x060000
    QT_DEPRECATED_WARNINGS_SINCE=0x060000
    APP_ID="${APP_ID}"
    APP_NAME="${APP_NAME}"
    APP_VERSION="${PROJECT_VERSION}"
)

target_link_libraries(r_audio_player PRIVATE
    Qt6::Widgets
    Qt6::Network
    Qt6::Concurrent
    Qt6::OpenGLWidgets
    ${TAGLIB_TARGET}
    ZLIB::ZLIB
    FFmpeg::avformat
    FFmpeg::avcodec
    FFmpeg::avutil
    FFmpeg::swresample
    r_cd_ripper
)
add_dependencies(r_audio_player r_cd_ripper)
if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux"))
    add_custom_command(TARGET r_audio_player POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
        $<TARGET_FILE:r_cd_ripper>
        $<TARGET_FILE_DIR:r_audio_player>
    )
endif()

if(UNIX AND NOT APPLE)
    target_link_libraries(r_audio_player PRIVATE dl pthread m)
elseif(APPLE)
    set_target_properties(r_audio_player PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE INSTALL_RPATH "@executable_path")
    set_target_properties(r_cd_ripper PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE INSTALL_RPATH "@executable_path")
endif()

configure_file(resources/r_audio_player.desktop.in ${CMAKE_BINARY_DIR}/${APP_ID}.desktop @ONLY)

add_custom_command(TARGET r_audio_player POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    ${CMAKE_CURRENT_SOURCE_DIR}/skins
    $<TARGET_FILE_DIR:r_audio_player>/skins
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
    ${CMAKE_BINARY_DIR}/version.txt
    $<TARGET_FILE_DIR:r_audio_player>/version.txt
)

if(R_AUDIO_PLAYER_BUNDLE_LYRICSYNC_ML)
    add_custom_command(TARGET r_audio_player POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_directory
        ${CMAKE_CURRENT_SOURCE_DIR}/tools
        $<TARGET_FILE_DIR:r_audio_player>/tools
    )
endif()

if(R_AUDIO_PLAYER_BUNDLE_LYRICSYNC_ML AND R_AUDIO_PLAYER_SETUP_LYRICSYNC_ML_AFTER_BUILD AND Python3_FOUND)
    add_custom_command(TARGET r_audio_player POST_BUILD
        COMMAND "${Python3_EXECUTABLE}" "$<TARGET_FILE_DIR:r_audio_player>/tools/lyricsync_ml_bootstrap.py" --tools-dir "$<TARGET_FILE_DIR:r_audio_player>/tools" --if-missing --quiet
    )
endif()

install(TARGETS r_audio_player BUNDLE DESTINATION . RUNTIME DESTINATION bin)
install(TARGETS r_cd_ripper RUNTIME DESTINATION bin LIBRARY DESTINATION bin ARCHIVE DESTINATION lib)
install(DIRECTORY skins DESTINATION share/${APP_ID})
if(R_AUDIO_PLAYER_BUNDLE_LYRICSYNC_ML)
    install(DIRECTORY tools DESTINATION bin)
endif()
if(R_AUDIO_PLAYER_BUNDLE_LYRICSYNC_ML AND R_AUDIO_PLAYER_SETUP_LYRICSYNC_ML_AFTER_BUILD AND Python3_FOUND)
    install(CODE "execute_process(COMMAND \"${Python3_EXECUTABLE}\" \"\${CMAKE_INSTALL_PREFIX}/bin/tools/lyricsync_ml_bootstrap.py\" --tools-dir \"\${CMAKE_INSTALL_PREFIX}/bin/tools\" --if-missing --quiet)")
endif()
if(NOT APPLE)
    install(FILES ${CMAKE_BINARY_DIR}/${APP_ID}.desktop DESTINATION share/applications)
    install(FILES resources/icons/app.png DESTINATION share/icons/hicolor/256x256/apps RENAME ${APP_ID}.png)
endif()
install(FILES ${CMAKE_BINARY_DIR}/version.txt DESTINATION bin)

include(CTest)
if(BUILD_TESTING)
    find_package(Qt6 REQUIRED COMPONENTS Test)
    qt_add_executable(r_audio_player_tests
        tests/CoreTests.cpp
        tests/MiniaudioImplementation.cpp
        src/lowend/LowEndLexer.c
        src/lowend/LowEndLexer.h
        src/lowend/LowEndValue.c
        src/lowend/LowEndValue.h
        src/app/AppSettings.cpp
        src/app/AppSettings.h
        src/app/BackgroundImage.cpp
        src/app/BackgroundImage.h
        src/app/PortablePaths.cpp
        src/app/PortablePaths.h
        src/app/ReleaseNotes.cpp
        src/app/ReleaseNotes.h
        src/app/Skin.cpp
        src/app/Skin.h
        src/audio/DspProcessor.cpp
        src/audio/DspProcessor.h
        src/audio/LyricsSync.cpp
        src/audio/LyricsSync.h
        src/audio/VocalActivityModel.cpp
        src/audio/VocalActivityModel.h
        src/media/AudioDecoder.cpp
        src/media/AudioDecoder.h
        src/media/LibraryScanner.cpp
        src/media/LibraryScanner.h
        src/media/MediaTypes.h
        src/ui/DspPanel.cpp
        src/ui/DspPanel.h
    )
    target_include_directories(r_audio_player_tests PRIVATE src ${MINIAUDIO_INCLUDE_DIR})
    target_compile_definitions(r_audio_player_tests PRIVATE
        QT_NO_KEYWORDS
        QT_DISABLE_DEPRECATED_BEFORE=0x060000
        QT_DEPRECATED_WARNINGS_SINCE=0x060000
    )
    target_link_libraries(r_audio_player_tests PRIVATE
        Qt6::Test
        Qt6::Widgets
        ${TAGLIB_TARGET}
        FFmpeg::avformat
        FFmpeg::avcodec
        FFmpeg::avutil
        FFmpeg::swresample
        r_cd_ripper
    )
    add_dependencies(r_audio_player_tests r_cd_ripper)
    add_test(NAME core COMMAND r_audio_player_tests)
    set_tests_properties(core PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:Qt6::Core>")
    if(NOT WIN32)
        set_tests_properties(core PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
    endif()
endif()

if(WIN32)
    option(R_AUDIO_PLAYER_DEPLOY_AFTER_BUILD "Deploy runtime files after build" ON)
    set(_qt_tool_hints)
    set(_runtime_search_dirs "$<TARGET_FILE_DIR:r_audio_player>")
    if(DEFINED Qt6_DIR)
        get_filename_component(_qt_prefix "${Qt6_DIR}/../../.." ABSOLUTE)
        list(APPEND _qt_tool_hints "${_qt_prefix}/bin" "${_qt_prefix}/tools/Qt6/bin")
        list(APPEND _runtime_search_dirs "${_qt_prefix}/bin")
    endif()
    if(DEFINED CMAKE_PREFIX_PATH)
        foreach(_prefix IN LISTS CMAKE_PREFIX_PATH)
            list(APPEND _qt_tool_hints "${_prefix}/bin" "${_prefix}/tools/Qt6/bin")
            list(APPEND _runtime_search_dirs "${_prefix}/bin")
        endforeach()
    endif()
    if(DEFINED VCPKG_INSTALLED_DIR AND DEFINED VCPKG_TARGET_TRIPLET)
        list(APPEND _qt_tool_hints "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/tools/Qt6/bin" "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin")
        list(APPEND _runtime_search_dirs "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin")
    endif()
    find_program(WINDEPLOYQT_EXECUTABLE NAMES windeployqt.exe windeployqt HINTS ${_qt_tool_hints})
    set(_runtime_search_dirs_arg "${_runtime_search_dirs}")
    string(REPLACE ";" "@@@" _runtime_search_dirs_arg "${_runtime_search_dirs_arg}")
    if(R_AUDIO_PLAYER_DEPLOY_AFTER_BUILD)
        if(WINDEPLOYQT_EXECUTABLE)
            add_custom_command(TARGET r_audio_player POST_BUILD
                COMMAND "${WINDEPLOYQT_EXECUTABLE}" --release --compiler-runtime --no-translations --dir "$<TARGET_FILE_DIR:r_audio_player>" "$<TARGET_FILE:r_audio_player>"
                COMMAND ${CMAKE_COMMAND} "-DTARGET_FILE=$<TARGET_FILE:r_audio_player>" "-DTARGET_DIR=$<TARGET_FILE_DIR:r_audio_player>" "-DSEARCH_DIRS=${_runtime_search_dirs_arg}" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/DeployWindowsRuntime.cmake"
                COMMAND ${CMAKE_COMMAND} "-DTARGET_DIR=$<TARGET_FILE_DIR:r_audio_player>" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TrimWindowsRuntime.cmake"
            )
        else()
            add_custom_command(TARGET r_audio_player POST_BUILD
                COMMAND ${CMAKE_COMMAND} "-DTARGET_FILE=$<TARGET_FILE:r_audio_player>" "-DTARGET_DIR=$<TARGET_FILE_DIR:r_audio_player>" "-DSEARCH_DIRS=${_runtime_search_dirs_arg}" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/DeployWindowsRuntime.cmake"
                COMMAND ${CMAKE_COMMAND} "-DTARGET_DIR=$<TARGET_FILE_DIR:r_audio_player>" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TrimWindowsRuntime.cmake"
            )
        endif()

    endif()
endif()
