# CrossCompileWindows.cmake
# Downloads and builds dependencies for cross-compiling openFPGALoader to Windows.

include(ExternalProject)
include(FetchContent)

set(LIBUSB_VERSION "1.0.27" CACHE STRING "libusb version")
set(LIBFTDI_VERSION "1.5" CACHE STRING "libftdi version")

set(LIBUSB_URL "https://github.com/libusb/libusb/releases/download/v${LIBUSB_VERSION}/libusb-${LIBUSB_VERSION}.7z")
set(LIBFTDI_URL "https://www.intra2net.com/en/developer/libftdi/download/libftdi1-${LIBFTDI_VERSION}.tar.bz2")

if(NOT DEFINED CROSS_DEPS_DIR)
    set(CROSS_DEPS_DIR "${CMAKE_BINARY_DIR}/cross-deps")
endif()

set(CROSS_DEPS_INSTALL_DIR "${CROSS_DEPS_DIR}/install")
set(CROSS_DEPS_BUILD_DIR "${CROSS_DEPS_DIR}/build")
set(CROSS_DEPS_SRC_DIR "${CROSS_DEPS_DIR}/src")

file(MAKE_DIRECTORY ${CROSS_DEPS_DIR})
file(MAKE_DIRECTORY ${CROSS_DEPS_INSTALL_DIR})
file(MAKE_DIRECTORY ${CROSS_DEPS_INSTALL_DIR}/include)
file(MAKE_DIRECTORY ${CROSS_DEPS_INSTALL_DIR}/lib)
file(MAKE_DIRECTORY ${CROSS_DEPS_INSTALL_DIR}/lib/pkgconfig)
file(MAKE_DIRECTORY ${CROSS_DEPS_BUILD_DIR})
file(MAKE_DIRECTORY ${CROSS_DEPS_SRC_DIR})

find_program(SEVENZIP_EXECUTABLE NAMES 7z 7za p7zip)
if(NOT SEVENZIP_EXECUTABLE)
    message(FATAL_ERROR "7z/p7zip not found. Please install p7zip or 7zip.")
endif()

function(setup_libusb_windows)
    set(LIBUSB_ARCHIVE "${CROSS_DEPS_SRC_DIR}/libusb-${LIBUSB_VERSION}.7z")
    set(LIBUSB_EXTRACT_DIR "${CROSS_DEPS_SRC_DIR}/libusb-${LIBUSB_VERSION}")

    if(NOT EXISTS ${LIBUSB_ARCHIVE})
        message(STATUS "Downloading libusb ${LIBUSB_VERSION}...")
        file(DOWNLOAD ${LIBUSB_URL} ${LIBUSB_ARCHIVE} SHOW_PROGRESS STATUS DOWNLOAD_STATUS)
        list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
        if(NOT STATUS_CODE EQUAL 0)
            message(FATAL_ERROR "Failed to download libusb: ${DOWNLOAD_STATUS}")
        endif()
    endif()

    if(NOT EXISTS "${LIBUSB_EXTRACT_DIR}/MinGW64")
        message(STATUS "Extracting libusb...")
        file(MAKE_DIRECTORY ${LIBUSB_EXTRACT_DIR})
        execute_process(
            COMMAND ${SEVENZIP_EXECUTABLE} x -y -o${LIBUSB_EXTRACT_DIR} ${LIBUSB_ARCHIVE}
            WORKING_DIRECTORY ${CROSS_DEPS_SRC_DIR}
            RESULT_VARIABLE EXTRACT_RESULT
        )
        if(NOT EXTRACT_RESULT EQUAL 0)
            message(FATAL_ERROR "Failed to extract libusb")
        endif()
    endif()

    message(STATUS "Installing libusb headers and libraries...")
    file(MAKE_DIRECTORY "${CROSS_DEPS_INSTALL_DIR}/include/libusb-1.0")
    file(COPY "${LIBUSB_EXTRACT_DIR}/include/libusb.h"
         DESTINATION "${CROSS_DEPS_INSTALL_DIR}/include/libusb-1.0/")
    file(COPY "${LIBUSB_EXTRACT_DIR}/MinGW64/static/libusb-1.0.a"
         DESTINATION "${CROSS_DEPS_INSTALL_DIR}/lib/")

    file(WRITE "${CROSS_DEPS_INSTALL_DIR}/lib/pkgconfig/libusb-1.0.pc"
"prefix=${CROSS_DEPS_INSTALL_DIR}
exec_prefix=\${prefix}
libdir=\${exec_prefix}/lib
includedir=\${prefix}/include/libusb-1.0

Name: libusb-1.0
Description: C API for USB device access from Windows
Version: ${LIBUSB_VERSION}
Libs: -L\${libdir} -lusb-1.0
Cflags: -I\${includedir}
")

    set(LIBUSB_FOUND TRUE PARENT_SCOPE)
    set(LIBUSB_INCLUDE_DIRS "${CROSS_DEPS_INSTALL_DIR}/include/libusb-1.0" PARENT_SCOPE)
    set(LIBUSB_LIBRARIES "${CROSS_DEPS_INSTALL_DIR}/lib/libusb-1.0.a" PARENT_SCOPE)
    set(LIBUSB_VERSION ${LIBUSB_VERSION} PARENT_SCOPE)
endfunction()

function(setup_libftdi_windows)
    set(LIBFTDI_ARCHIVE "${CROSS_DEPS_SRC_DIR}/libftdi1-${LIBFTDI_VERSION}.tar.bz2")
    set(LIBFTDI_SRC_DIR "${CROSS_DEPS_SRC_DIR}/libftdi1-${LIBFTDI_VERSION}")
    set(LIBFTDI_BUILD_DIR "${CROSS_DEPS_BUILD_DIR}/libftdi1")

    if(NOT EXISTS ${LIBFTDI_ARCHIVE})
        message(STATUS "Downloading libftdi ${LIBFTDI_VERSION}...")
        file(DOWNLOAD ${LIBFTDI_URL} ${LIBFTDI_ARCHIVE} SHOW_PROGRESS STATUS DOWNLOAD_STATUS)
        list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
        if(NOT STATUS_CODE EQUAL 0)
            message(FATAL_ERROR "Failed to download libftdi: ${DOWNLOAD_STATUS}")
        endif()
    endif()

    if(NOT EXISTS ${LIBFTDI_SRC_DIR})
        message(STATUS "Extracting libftdi...")
        execute_process(
            COMMAND ${CMAKE_COMMAND} -E tar xjf ${LIBFTDI_ARCHIVE}
            WORKING_DIRECTORY ${CROSS_DEPS_SRC_DIR}
            RESULT_VARIABLE EXTRACT_RESULT
        )
        if(NOT EXTRACT_RESULT EQUAL 0)
            message(FATAL_ERROR "Failed to extract libftdi")
        endif()
    endif()

    if(NOT EXISTS "${CROSS_DEPS_INSTALL_DIR}/lib/libftdi1.a")
        message(STATUS "Building libftdi for Windows...")
        file(MAKE_DIRECTORY ${LIBFTDI_BUILD_DIR})

        set(LIBFTDI_CMAKE_ARGS
            -DCMAKE_SYSTEM_NAME=Windows
            -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
            -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
            -DCMAKE_INSTALL_PREFIX=${CROSS_DEPS_INSTALL_DIR}
            -DCMAKE_PREFIX_PATH=${CROSS_DEPS_INSTALL_DIR}
            -DLIBUSB_INCLUDE_DIRS=${CROSS_DEPS_INSTALL_DIR}/include/libusb-1.0
            -DLIBUSB_LIBRARIES=${CROSS_DEPS_INSTALL_DIR}/lib/libusb-1.0.a
            -DFTDIPP=OFF -DBUILD_TESTS=OFF -DDOCUMENTATION=OFF
            -DEXAMPLES=OFF -DFTDI_EEPROM=OFF -DPYTHON_BINDINGS=OFF
            -DSTATICLIBS=ON
        )

        if(CMAKE_RC_COMPILER)
            list(APPEND LIBFTDI_CMAKE_ARGS -DCMAKE_RC_COMPILER=${CMAKE_RC_COMPILER})
        endif()

        foreach(_lang C CXX RC)
            if(DEFINED CMAKE_${_lang}_COMPILER_TARGET AND NOT "${CMAKE_${_lang}_COMPILER_TARGET}" STREQUAL "")
                list(APPEND LIBFTDI_CMAKE_ARGS "-DCMAKE_${_lang}_COMPILER_TARGET=${CMAKE_${_lang}_COMPILER_TARGET}")
            endif()
        endforeach()

        foreach(_flags_var CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
            if(DEFINED ${_flags_var} AND NOT "${${_flags_var}}" STREQUAL "")
                list(APPEND LIBFTDI_CMAKE_ARGS "-D${_flags_var}=${${_flags_var}}")
            endif()
        endforeach()

        if(DEFINED CMAKE_SYSROOT AND NOT "${CMAKE_SYSROOT}" STREQUAL "")
            list(APPEND LIBFTDI_CMAKE_ARGS -DCMAKE_SYSROOT=${CMAKE_SYSROOT})
        endif()

        execute_process(
            COMMAND ${CMAKE_COMMAND}
                ${LIBFTDI_CMAKE_ARGS}
                ${LIBFTDI_SRC_DIR}
            WORKING_DIRECTORY ${LIBFTDI_BUILD_DIR}
            RESULT_VARIABLE CONFIG_RESULT
        )
        if(NOT CONFIG_RESULT EQUAL 0)
            message(FATAL_ERROR "Failed to configure libftdi")
        endif()

        execute_process(
            COMMAND ${CMAKE_COMMAND} --build . --parallel
            WORKING_DIRECTORY ${LIBFTDI_BUILD_DIR}
            RESULT_VARIABLE BUILD_RESULT
        )
        if(NOT BUILD_RESULT EQUAL 0)
            message(FATAL_ERROR "Failed to build libftdi")
        endif()

        execute_process(
            COMMAND ${CMAKE_COMMAND} --install .
            WORKING_DIRECTORY ${LIBFTDI_BUILD_DIR}
            RESULT_VARIABLE INSTALL_RESULT
        )
        if(NOT INSTALL_RESULT EQUAL 0)
            message(FATAL_ERROR "Failed to install libftdi")
        endif()

        file(WRITE "${CROSS_DEPS_INSTALL_DIR}/lib/pkgconfig/libftdi1.pc"
"prefix=${CROSS_DEPS_INSTALL_DIR}
exec_prefix=\${prefix}
libdir=\${prefix}/lib
includedir=\${prefix}/include/libftdi1

Name: libftdi1
Description: Library to program and control the FTDI USB controller
Requires: libusb-1.0
Version: ${LIBFTDI_VERSION}
Libs: -L\${libdir} -lftdi1
Cflags: -I\${includedir}
")
    endif()

    set(LIBFTDI_FOUND TRUE PARENT_SCOPE)
    set(LIBFTDI_INCLUDE_DIRS "${CROSS_DEPS_INSTALL_DIR}/include/libftdi1" PARENT_SCOPE)
    set(LIBFTDI_LIBRARIES "${CROSS_DEPS_INSTALL_DIR}/lib/libftdi1.a" PARENT_SCOPE)
    set(LIBFTDI_VERSION ${LIBFTDI_VERSION} PARENT_SCOPE)
endfunction()

function(setup_windows_cross_compile_deps)
    message(STATUS "Setting up Windows cross-compilation dependencies...")
    message(STATUS "  Dependencies directory: ${CROSS_DEPS_DIR}")
    message(STATUS "  Install directory: ${CROSS_DEPS_INSTALL_DIR}")

    setup_libusb_windows()
    setup_libftdi_windows()

    list(APPEND CMAKE_PREFIX_PATH ${CROSS_DEPS_INSTALL_DIR})
    set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
    set(ENV{PKG_CONFIG_PATH} "${CROSS_DEPS_INSTALL_DIR}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")

    set(LIBUSB_FOUND TRUE PARENT_SCOPE)
    set(LIBUSB_INCLUDE_DIRS "${CROSS_DEPS_INSTALL_DIR}/include/libusb-1.0" PARENT_SCOPE)
    set(LIBUSB_LIBRARIES "${CROSS_DEPS_INSTALL_DIR}/lib/libusb-1.0.a" PARENT_SCOPE)

    set(LIBFTDI_FOUND TRUE PARENT_SCOPE)
    set(LIBFTDI_INCLUDE_DIRS "${CROSS_DEPS_INSTALL_DIR}/include/libftdi1" PARENT_SCOPE)
    set(LIBFTDI_LIBRARIES "${CROSS_DEPS_INSTALL_DIR}/lib/libftdi1.a" PARENT_SCOPE)

    set(CROSS_DEPS_INSTALL_DIR ${CROSS_DEPS_INSTALL_DIR} PARENT_SCOPE)

    message(STATUS "Windows cross-compilation dependencies ready!")
endfunction()
