# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: Copyright (C) 2026 Max A. Vilkki a.k.a. codise
#
# libusb (LGPL-2.1-or-later) from lib/libusb (v1.0.30): USB access for SID devices, first
# the HardSID 4U (DESIGN §7.3). Linked statically; the licence notice comes with #243.
#
# Upstream builds with autotools, Xcode or MSBuild. This mirrors libusb/Makefile.am's
# source lists, and generated/config.h stands in for configure's output: one hand-written
# file that picks the platform per compilation, because the universal macOS build compiles
# both slices in one configure.
#
# Logging is off (no ENABLE_LOGGING, as configure --disable-log): it saves the message
# strings, and nothing may write to stderr from the SID-out service thread anyway.
#
# Size (Release, AppleClang 17, macOS 11 target, measured 2026-10-10, before
# dead-stripping): __TEXT 56,842 B on arm64 and 67,205 B on x86_64, __DATA about 1.9 KB.
# What it adds to the app is measured once something links it (#291).
#
# Built and link-checked on macOS (arm64 and x86_64) and with MSVC 19.44 (x64), where
# hardsid_probe starts it and it reports hotplug; no device has been opened through it on
# Windows yet. The Linux branch is read from the clone's Makefile.am and configure.ac and
# has not been compiled (unverified).

set(libusbDir "${FLIPSID_LIB_DIR}/libusb/libusb")
set(libusbGenerated "${CMAKE_CURRENT_LIST_DIR}/generated")

# generated/config.h is written for one version; a pin bump has to revisit it.
function(flipsid_libusb_check_version)
    file(READ "${libusbDir}/version.h" versionHeader)
    foreach(part MAJOR MINOR MICRO)
        string(REGEX MATCH "#define LIBUSB_${part} ([0-9]+)" _ "${versionHeader}")
        list(APPEND cloneVersion "${CMAKE_MATCH_1}")
    endforeach()
    list(JOIN cloneVersion "." cloneVersion)
    if(NOT cloneVersion STREQUAL "1.0.30")
        message(FATAL_ERROR
            "libusb: lib/libusb is ${cloneVersion} but lib/cmake/libusb is written for "
            "1.0.30.\n"
            "Check generated/config.h against configure.ac, Xcode/config.h and "
            "msvc/config.h, and the source lists against libusb/Makefile.am.")
    endif()
endfunction()
# file(READ) doesn't make CMake watch a file.
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${libusbDir}/version.h")
flipsid_libusb_check_version()

set(libusbSources
    core.c
    descriptor.c
    hotplug.c
    io.c
    strerror.c
    sync.c
)
if(WIN32)
    # windows_usbdk.c stays in although we only use WinUSB: windows_common.c refers to
    # usbdk_backend unconditionally. It only does anything when the UsbDk driver is
    # installed, and the linker can't drop it (it is reached through a function table).
    #
    # windows_hotplug.c: v1.0.30's Windows hotplug is opt-in (configure
    # --enable-windows-hotplug, the *-Hotplug MSBuild configurations) and needs
    # LIBUSB_WINDOWS_HOTPLUG below. Without it libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)
    # is false on Windows. It starts a thread with a hidden message-only window.
    list(APPEND libusbSources
        os/events_windows.c
        os/threads_windows.c
        os/windows_common.c
        os/windows_hotplug.c
        os/windows_usbdk.c
        os/windows_winusb.c
    )
else()
    list(APPEND libusbSources
        os/events_posix.c
        os/threads_posix.c
    )
    if(APPLE)
        list(APPEND libusbSources os/darwin_usb.c)
    elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
        # Netlink instead of linux_udev.c: no libudev to link or to require at run time.
        list(APPEND libusbSources
            os/linux_usbfs.c
            os/linux_netlink.c
        )
    else()
        message(FATAL_ERROR "libusb: no backend chosen for ${CMAKE_SYSTEM_NAME}.")
    endif()
endif()
list(TRANSFORM libusbSources PREPEND "${libusbDir}/")

add_library(ext_libusb STATIC ${libusbSources})
add_library(ext::libusb ALIAS ext_libusb)

# Users write #include <libusb.h>. The same directory holds libusbi.h and version.h,
# which nothing outside this target should include.
target_include_directories(ext_libusb SYSTEM PUBLIC "${libusbDir}")
# The sources include <config.h>.
target_include_directories(ext_libusb PRIVATE "${libusbGenerated}")

# configure requires C11 (_Thread_local, <stdatomic.h>) and prefers gnu11, so extensions
# stay on. Not for MSVC: upstream's msvc/*.props set no C language standard (libusbi.h
# uses the Interlocked functions there instead of <stdatomic.h>), and /std:c11 would ask
# for a newer Windows SDK than the build otherwise needs (unverified, like the rest of the
# Windows branch).
set_target_properties(ext_libusb PROPERTIES C_VISIBILITY_PRESET hidden)
if(NOT MSVC)
    set_target_properties(ext_libusb PROPERTIES
        C_STANDARD 11
        C_STANDARD_REQUIRED ON
        C_EXTENSIONS ON
    )
endif()

if(APPLE)
    # configure.ac's LIBS for darwin.
    target_link_libraries(ext_libusb PRIVATE
        "-framework IOKit"
        "-framework CoreFoundation"
        "-framework Security"
        objc
    )
elseif(WIN32)
    target_compile_definitions(ext_libusb PRIVATE
        # msvc/Base.props and configure.ac: the oldest Windows libusb supports.
        _WIN32_WINNT=_WIN32_WINNT_VISTA
        _CRT_SECURE_NO_WARNINGS
        LIBUSB_WINDOWS_HOTPLUG=1
    )
    # SetupAPI, Cfgmgr32, AdvAPI32 and hid are loaded at run time (DLL_GET_HANDLE), so
    # they aren't link dependencies. windows_hotplug.c's window calls are in user32;
    # upstream names only kernel32 and relies on the toolchain's defaults for the rest.
    target_link_libraries(ext_libusb PRIVATE user32)
else()
    find_package(Threads REQUIRED)
    target_link_libraries(ext_libusb PRIVATE Threads::Threads)
endif()

# Third-party warnings aren't ours to fix.
if(MSVC)
    target_compile_options(ext_libusb PRIVATE /w)
else()
    target_compile_options(ext_libusb PRIVATE -w)
endif()

flipsid_configure_third_party(ext_libusb)
