# SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
# SPDX-License-Identifier: BSD-2-Clause
#
# kcoreaddons_add_plugin(plugin_name
#     [SOURCES <src> [<src> [...]]] # optional since 5.83, required before
#     [STATIC]
#     [INSTALL_NAMESPACE "servicename"]
# )
#
# This macro helps simplifying the creation of plugins for KPluginFactory
# based systems.
# It will create a plugin given the SOURCES list and the INSTALL_NAMESPACE so that
# the plugin is installed with the rest of the plugins from the same sub-system,
# within ${KDE_INSTALL_PLUGINDIR}.
# Since 5.89 the macro supports static plugins by passing in the STATIC option.
#
# Example:
#   kcoreaddons_add_plugin(kdeconnect_share SOURCES ${kdeconnect_share_SRCS} INSTALL_NAMESPACE "kdeconnect")
#
# Since 5.10.0

function(kcoreaddons_add_plugin plugin)
    set(options STATIC)
    set(oneValueArgs INSTALL_NAMESPACE)
    set(multiValueArgs SOURCES)
    cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

    if (NOT ARGS_INSTALL_NAMESPACE)
        message(FATAL_ERROR "Must specify INSTALL_NAMESPACE for ${plugin}")
    endif()
    if (ARGS_UNPARSED_ARGUMENTS)
        message(FATAL_ERROR "kcoreaddons_add_plugin method call received unexpected arguments: ${ARGS_UNPARSED_ARGUMENTS}")
    endif()
    if (NOT BUILD_SHARED_LIBS)
        set(ARGS_STATIC ON)
    endif()

    if (ARGS_STATIC)
        add_library(${plugin} STATIC ${ARGS_SOURCES})
        target_compile_definitions(${plugin} PRIVATE QT_STATICPLUGIN)
        string(REPLACE "/" "_" SANITIZED_PLUGIN_NAMESPACE ${ARGS_INSTALL_NAMESPACE})

        if (NOT ${plugin} IN_LIST KCOREADDONS_STATIC_PLUGINS${SANITIZED_PLUGIN_NAMESPACE})
            set(KCOREADDONS_STATIC_PLUGINS${SANITIZED_PLUGIN_NAMESPACE} "${KCOREADDONS_STATIC_PLUGINS${SANITIZED_PLUGIN_NAMESPACE}};${plugin}" CACHE INTERNAL "list of known static plugins for ${ARGS_INSTALL_NAMESPACE} namespace")
        endif()
        set_target_properties(${plugin} PROPERTIES PLUGIN_INSTALL_NAMESPACE "${ARGS_INSTALL_NAMESPACE}" PLUGIN_NAME "${plugin}")
        set_property(TARGET ${plugin} APPEND PROPERTY EXPORT_PROPERTIES "PLUGIN_INSTALL_NAMESPACE;PLUGIN_NAME")
    else()
        add_library(${plugin} MODULE ${ARGS_SOURCES})
    endif()

    string(REPLACE "." "_" PLUGIN_FACTORY_NAME ${plugin}_factory)
    string(REPLACE "-" "_" PLUGIN_FACTORY_NAME ${PLUGIN_FACTORY_NAME})
    target_compile_definitions(${plugin} PRIVATE KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME=${PLUGIN_FACTORY_NAME})

    # If we have static plugins there are no plugins to install
    if (ARGS_STATIC)
        return()
    endif()

    set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${ARGS_INSTALL_NAMESPACE}")

    if (NOT KCOREADDONS_INTERNAL_SKIP_PLUGIN_INSTALLATION)
        if(NOT ANDROID)
            install(TARGETS ${plugin} DESTINATION ${KDE_INSTALL_PLUGINDIR}/${ARGS_INSTALL_NAMESPACE})
        else()
            string(REPLACE "/" "_" pluginprefix "${ARGS_INSTALL_NAMESPACE}")
            set_property(TARGET ${plugin} PROPERTY PREFIX "libplugins_")
            if(NOT pluginprefix STREQUAL "")
                set_property(TARGET ${plugin} APPEND_STRING PROPERTY PREFIX "${pluginprefix}_")
            endif()
            install(TARGETS ${plugin} DESTINATION ${KDE_INSTALL_PLUGINDIR})
        endif()
    endif()
endfunction()

# This macro imports the plugins for the given namespace that were
# registered using the kcoreaddons_add_plugin function.
# This includes the initailization statements and linking the plugins to the given target.
#
# In case the plugins are used in both the executable and multiple autotests it it recommended to
# bundle the static plugins in a shared lib for the autotests. In case of shared libs the plugin registrations
# will take effect when the test link against it.
#
# Since 5.89
function(kcoreaddons_target_static_plugins app_target)
    cmake_parse_arguments(ARGS "" "LINK_OPTION;NAMESPACE;TARGETS" "" ${ARGN})
    if ((ARGS_LINK_OPTION AND ARGS_NAMESPACE) OR (NOT ARGS_NAMESPACE AND NOT ARGS_TARGETS))
        message(FATAL_ERROR "Either NAMESPACE or TARGETS must be set")
    endif()

    set(START_COMMENTS "// This file is autogenerated by kcoreaddons_target_static_plugins, do not edit!\n//clazy:excludeall=non-pod-global-static\n")
    # We access this as extern, because then we don't have to install the header file for it
    set(PLUGIN_REGISTRATION_AUTOGEN "${START_COMMENTS}#include <QPluginLoader>\nextern void kRegisterStaticPluginFunction(const QString &, const QString &, QStaticPlugin)\;\n\n")

    macro(generate_plugin_registration PLUGIN_NAME PLUGIN_NAMESPACE)
        string(REPLACE "." "_" PLUGIN_FACTORY_NAME ${PLUGIN_NAME}_factory)
        string(REPLACE "-" "_" PLUGIN_FACTORY_NAME ${PLUGIN_FACTORY_NAME})
        set(REGISTER_PLUGIN_CALL "
extern const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_${PLUGIN_FACTORY_NAME}()\;
class Static${PLUGIN_FACTORY_NAME}PluginInstance
{
public:
Static${PLUGIN_FACTORY_NAME}PluginInstance()
{
kRegisterStaticPluginFunction(
    QStringLiteral(\"${PLUGIN_NAME}\"),
    QStringLiteral(\"${PLUGIN_NAMESPACE}\"),
    qt_static_plugin_${PLUGIN_FACTORY_NAME}()
)\;
}
}\;
static Static${PLUGIN_FACTORY_NAME}PluginInstance static${PLUGIN_FACTORY_NAME}Instance\;")
    set(PLUGIN_REGISTRATION_AUTOGEN "${PLUGIN_REGISTRATION_AUTOGEN}${REGISTER_PLUGIN_CALL}\n")
endmacro()

    if(ARGS_NAMESPACE)
        string(REPLACE "/" "_" SANITIZED_PLUGIN_NAMESPACE ${ARGS_NAMESPACE})
        set(TMP_PLUGIN_FILE "${CMAKE_CURRENT_BINARY_DIR}/kcoreaddons_static_${SANITIZED_PLUGIN_NAMESPACE}_plugins_tmp.cpp")
        set(PLUGIN_FILE "${CMAKE_CURRENT_BINARY_DIR}/kcoreaddons_static_${SANITIZED_PLUGIN_NAMESPACE}_plugins.cpp")

        foreach(PLUGIN_NAME IN LISTS KCOREADDONS_STATIC_PLUGINS${SANITIZED_PLUGIN_NAMESPACE})
            if (PLUGIN_NAME)
                generate_plugin_registration(${PLUGIN_NAME} ${ARGS_NAMESPACE})
                target_link_libraries(${app_target} ${ARGS_LINK_OPTION} ${PLUGIN_NAME})
            endif()
        endforeach()
    elseif(ARGS_TARGETS)
        set(TMP_PLUGIN_FILE "${CMAKE_CURRENT_BINARY_DIR}/kcoreaddons_static_${app_target}_plugins_tmp.cpp")
        set(PLUGIN_FILE "${CMAKE_CURRENT_BINARY_DIR}/kcoreaddons_static_${app_target}_plugins.cpp")
        foreach(ARG_TARGET IN LISTS ARGS_TARGETS)
            get_target_property(PLUGIN_NAME ${ARG_TARGET} PLUGIN_NAME)
            get_target_property(PLUGIN_INSTALL_NAMESPACE ${ARG_TARGET} PLUGIN_INSTALL_NAMESPACE)
            generate_plugin_registration(${PLUGIN_NAME} ${PLUGIN_INSTALL_NAMESPACE})
            target_link_libraries(${app_target} ${ARGS_LINK_OPTION} ${ARG_TARGET})
        endforeach()
    endif()

    file(WRITE ${TMP_PLUGIN_FILE} ${PLUGIN_REGISTRATION_AUTOGEN})
    execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${TMP_PLUGIN_FILE} ${PLUGIN_FILE})
    file(REMOVE ${TMP_PLUGIN_FILE})
    target_sources(${app_target} PRIVATE ${PLUGIN_FILE})
endfunction()

# Clear previously set plugins, otherwise the linker will fail due to the symbols of a removed pugin being missing
get_directory_property(_cache_vars CACHE_VARIABLES)
foreach(CACHED_VAR IN LISTS _cache_vars)
    if (CACHED_VAR MATCHES "^KCOREADDONS_STATIC_PLUGINS")
        set(${CACHED_VAR} "" CACHE INTERNAL "")
    endif()
endforeach()
