# Copyright libOpenCOR contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function(build_package PACKAGE_NAME)
    # Configure and run a CMake script to build the package for us.

    string(REPLACE ";" "\n" EP_ADD_ARGS "${ARGN}")

    configure_file(${CMAKE_SOURCE_DIR}/cmake/buildpackage.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/CMakeLists.txt)

    execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -S . -B build
                    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
                    RESULT_VARIABLE RESULT)

    if(NOT RESULT EQUAL 0)
        message(FATAL_ERROR "The ${PACKAGE_NAME} package could not be configured.")
    endif()

    execute_process(COMMAND ${CMAKE_COMMAND} --build build
                    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
                    RESULT_VARIABLE RESULT)

    if(NOT RESULT EQUAL 0)
        message(FATAL_ERROR "The ${PACKAGE_NAME} package could not be built.")
    endif()
endfunction()

function(fix_import_prefix CONFIG_FILE)
    file(READ ${CONFIG_FILE} FILE_CONTENTS)

    string(REPLACE
           "# The installation prefix configured by this project.
set(_IMPORT_PREFIX \"${INSTALL_DIR}\")"
           "# Compute the installation prefix relative to this file.
get_filename_component(_IMPORT_PREFIX \"\${CMAKE_CURRENT_LIST_FILE}\" PATH)
get_filename_component(_IMPORT_PREFIX \"\${_IMPORT_PREFIX}\" PATH)
get_filename_component(_IMPORT_PREFIX \"\${_IMPORT_PREFIX}\" PATH)
if(_IMPORT_PREFIX STREQUAL \"/\")
  set(_IMPORT_PREFIX \"\")
endif()"
           FILE_CONTENTS "${FILE_CONTENTS}")

    file(WRITE ${CONFIG_FILE} "${FILE_CONTENTS}")
endfunction()

function(create_package PACKAGE_NAME PACKAGE_VERSION PACKAGE_REPOSITORY RELEASE_TAG)
    # Create the package.

    set(PACKAGE_FILE ${CMAKE_BINARY_DIR}/${PACKAGE_NAME}.${PACKAGE_VERSION}.${TARGET_PLATFORM}.tar.gz)

    execute_process(COMMAND ${CMAKE_COMMAND} -E tar -czf ${PACKAGE_FILE} ${ARGN}
                    WORKING_DIRECTORY ${PREBUILT_DIR}/${PACKAGE_NAME}
                    RESULT_VARIABLE RESULT
                    OUTPUT_QUIET)

    # Calculate the SHA-1 value of our package, if it exists, and let people
    # know how we should call the retrieve_package() function.

    if(RESULT EQUAL 0 AND EXISTS ${PACKAGE_FILE})
        file(SHA1 ${PACKAGE_FILE} SHA1_VALUE)

        message("To retrieve the ${PACKAGE_NAME} package, use:
    retrieve_package(\$\{PACKAGE_NAME\} \$\{PACKAGE_VERSION\}
                     \$\{PACKAGE_REPOSITORY\} \$\{RELEASE_TAG\}
                     ${SHA1_VALUE})")
    else()
        if(EXISTS ${PACKAGE_FILE})
            file(REMOVE ${PACKAGE_FILE})
        endif()

        message(FATAL_ERROR "The ${PACKAGE_NAME} package could not be created.")
    endif()
endfunction()

function(add_package PACKAGE)
    add_subdirectory(${PACKAGE})

    set(PACKAGES "${PACKAGES};${PACKAGE}")

    list(SORT PACKAGES)

    set(PACKAGES "${PACKAGES}" CACHE INTERNAL "Available packages.")
endfunction()

function(retrieve_package PACKAGE_NAME PACKAGE_VERSION PACKAGE_REPOSITORY RELEASE_TAG SHA1_VALUE)
    # Check whether the package has already been retrieved by simply checking
    # whether its installation directory exists.

    set(INSTALL_DIR ${PREBUILT_DIR}/${PACKAGE_NAME})

    message(STATUS "Retrieving Package ${PACKAGE_NAME}")

    if(EXISTS ${INSTALL_DIR})
        message(STATUS "Retrieving Package ${PACKAGE_NAME} - Success")
    else()
        # Retrieve the package.

        set(PACKAGE_FILE ${PACKAGE_NAME}.${PACKAGE_VERSION}.${TARGET_PLATFORM}.tar.gz)
        set(REAL_PACKAGE_FILE ${INSTALL_DIR}/${PACKAGE_FILE})
        set(PACKAGE_URL "https://github.com/opencor/${PACKAGE_REPOSITORY}/releases/download/${RELEASE_TAG}/${PACKAGE_FILE}")

        file(DOWNLOAD ${PACKAGE_URL} ${REAL_PACKAGE_FILE} STATUS STATUS)

        # Uncompress the package, should we have managed to retrieve it.

        list(GET STATUS 0 STATUS_CODE)

        if(${STATUS_CODE} EQUAL 0)
            set(REAL_PACKAGE_FILE ${PREBUILT_DIR}/${PACKAGE_NAME}/${PACKAGE_FILE})

            file(SHA1 ${REAL_PACKAGE_FILE} REAL_SHA1_VALUE)

            if(NOT "${REAL_SHA1_VALUE}" STREQUAL "${SHA1_VALUE}")
                file(REMOVE_RECURSE ${INSTALL_DIR})

                message(STATUS "Retrieving Package ${PACKAGE_NAME} - Failed")
                message(FATAL_ERROR "The ${PACKAGE_NAME} package (downloaded from ${PACKAGE_URL}) does not have the expected SHA-1 value.")
            endif()

            execute_process(COMMAND ${CMAKE_COMMAND} -E tar -xf ${REAL_PACKAGE_FILE}
                            WORKING_DIRECTORY ${INSTALL_DIR}
                            RESULT_VARIABLE RESULT
                            OUTPUT_QUIET ERROR_QUIET)

            if(NOT RESULT EQUAL 0)
                file(REMOVE_RECURSE ${INSTALL_DIR})

                message(STATUS "Retrieving Package ${PACKAGE_NAME} - Failed")
                message(FATAL_ERROR "The ${PACKAGE_NAME} package (downloaded from ${PACKAGE_URL}) could not be uncompressed.")
            endif()

            file(REMOVE ${REAL_PACKAGE_FILE})

            message(STATUS "Retrieving Package ${PACKAGE_NAME} - Success")
        else()
            file(REMOVE_RECURSE ${INSTALL_DIR})

            message(STATUS "Retrieving Package ${PACKAGE_NAME} - Failed")
            message(FATAL_ERROR "The ${PACKAGE_NAME} package could not be retrieved from ${PACKAGE_URL}.")
        endif()
    endif()

    # Create a dummy target for our package.
    # Note: this is needed so that we can build a package that depends on other
    #       packages that have been retrieved.

    add_custom_target(${PACKAGE_NAME})
endfunction()

function(escape_path OLD_PATH NEW_PATH)
    # Escape spaces and parentheses in a path.

    string(REPLACE " " "\\ " NEW_PATH_VALUE "${OLD_PATH}")
    string(REPLACE "(" "\\(" NEW_PATH_VALUE "${NEW_PATH_VALUE}")
    string(REPLACE ")" "\\)" NEW_PATH_VALUE "${NEW_PATH_VALUE}")

    set(${NEW_PATH} ${NEW_PATH_VALUE} PARENT_SCOPE)
endfunction()

# Determine the platform on which we are.

if(WIN32)
    if(RELEASE_MODE)
        set(TARGET_PLATFORM windows.release)
    else()
        set(TARGET_PLATFORM windows.debug)
    endif()
elseif(APPLE)
    set(TARGET_PLATFORM macos)
else()
    set(TARGET_PLATFORM linux)
endif()

# Build our third-party libraries the same way that we build libOpenCOR.
# Note: the build type on Linux/macOS is always Release since we don't need to
#       debug third-party libraries and a debug library can use release
#       libraries.

include(ExternalProject)

escape_path(${CMAKE_C_COMPILER} EP_CMAKE_C_COMPILER)
escape_path(${CMAKE_CXX_COMPILER} EP_CMAKE_CXX_COMPILER)

if(WIN32)
    set(EP_CMAKE_BUILD_TYPE ${LIBOPENCOR_BUILD_TYPE})
else()
    set(EP_CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_ARGS
    -DCMAKE_C_COMPILER=${EP_CMAKE_C_COMPILER}
    -DCMAKE_CXX_COMPILER=${EP_CMAKE_CXX_COMPILER}
    -DCMAKE_BUILD_TYPE=${EP_CMAKE_BUILD_TYPE}
)

if(CMAKE_C_COMPILER_LAUNCHER AND CMAKE_CXX_COMPILER_LAUNCHER)
    escape_path(${CMAKE_C_COMPILER_LAUNCHER} EP_CMAKE_C_COMPILER_LAUNCHER)
    escape_path(${CMAKE_CXX_COMPILER_LAUNCHER} EP_CMAKE_CXX_COMPILER_LAUNCHER)

    list(APPEND CMAKE_ARGS
        -DCMAKE_C_COMPILER_LAUNCHER=${EP_CMAKE_C_COMPILER_LAUNCHER}
        -DCMAKE_CXX_COMPILER_LAUNCHER=${EP_CMAKE_CXX_COMPILER_LAUNCHER}
    )
endif()

if(APPLE)
    list(APPEND CMAKE_ARGS
        -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}
    )
endif()

# Get ready to use nmake/make for some third-party libraries (e.g., OpenSSL).

if(WIN32)
    set(MAKE_NMAKE_COMMAND nmake)
else()
    set(MAKE_NMAKE_COMMAND make)

    if(APPLE)
        list(APPEND MAKE_NMAKE_COMMAND
            SDKROOT=${CMAKE_OSX_SYSROOT}
        )
    endif()

    include(ProcessorCount)

    ProcessorCount(PROCESSOR_COUNT)

    if(NOT PROCESSOR_COUNT EQUAL 0)
        list(APPEND MAKE_NMAKE_COMMAND
            -j ${PROCESSOR_COUNT}
        )
    endif()
endif()
