#-----------------------------------------------------------------------------
#
# Copyright (C) 2022 Bjarne von Horn <vh@igh.de>
#               2022 Florian Pose <fp@igh.de>
#
# This file is part of the QtPdCom library.
#
# The QtPdCom library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# The QtPdCom library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with the QtPdCom Library. If not, see <http://www.gnu.org/licenses/>.
#
#-----------------------------------------------------------------------------

cmake_minimum_required(VERSION 3.10)

project(QtPdCom1 VERSION 1.2.0)
set(SOVERSION 2)

include(CMakeDependentOption)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)

# Configure build

option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
option(BUILD_EXAMPLE "Also build example" OFF)
option(USE_SASL "Use LoginManager with SASL" ON)
option(USE_SYMBOL_VERSIONING "Use ELF symbol versioning" OFF)
cmake_dependent_option(
    CHECK_SYMBOL_VERSIONING
    "Verify symbol versioning after build"
    ON
    "USE_SYMBOL_VERSIONING"
    OFF
)

configure_file(Doxyfile.in "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile" @ONLY)

# Load required packages

set(PUBLIC_QT_MODULES Core Gui Network)

if (USE_SASL)
    find_package(PdCom5 REQUIRED COMPONENTS sasl)
else()
    find_package(PdCom5 REQUIRED)
endif()

find_package(Qt5 REQUIRED
    COMPONENTS ${PUBLIC_QT_MODULES} LinguistTools Xml
)

if (USE_SYMBOL_VERSIONING)
    find_package(PythonInterp 3 REQUIRED)
endif()

# handle translation files

set(TS_FILES
    QtPdCom_de.ts
    QtPdCom_nl.ts
)

configure_file(QtPdCom_ts.qrc "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY)
qt5_add_translation(QM_FILES ${TS_FILES})

# add source files and headers

set(PUBLIC_HEADERS
    ${PROJECT_NAME}/BroadcastModel.h
    ${PROJECT_NAME}/ClientStatisticsModel.h
    ${PROJECT_NAME}/Export.h
    ${PROJECT_NAME}/FutureWatchers.h
    ${PROJECT_NAME}/FutureWatchersDetails.h
    ${PROJECT_NAME}/Message.h
    ${PROJECT_NAME}/MessageModel.h
    ${PROJECT_NAME}/Process.h
    ${PROJECT_NAME}/ScalarSubscriber.h
    ${PROJECT_NAME}/ScalarVariable.h
    ${PROJECT_NAME}/TableColumn.h
    ${PROJECT_NAME}/TableModel.h
    ${PROJECT_NAME}/Translator.h
    ${PROJECT_NAME}/Transmission.h
    ${PROJECT_NAME}/VariableList.h
)

set(SOURCES
    src/BroadcastModel.cpp
    src/ClientStatisticsModel.cpp
    src/Message.cpp
    src/MessageImpl.cpp
    src/MessageItem.cpp
    src/MessageManager.cpp
    src/MessageModel.cpp
    src/MessageModelImpl.cpp
    src/Process.cpp
    src/ScalarSubscriber.cpp
    src/TableColumn.cpp
    src/TableModel.cpp
    src/Translator.cpp
    src/Transmission.cpp
)

if (USE_SASL)
    list(APPEND PUBLIC_HEADERS "${PROJECT_NAME}/LoginManager.h")
    list(APPEND SOURCES "src/LoginManager.cpp")
endif()

add_library(${PROJECT_NAME}
    ${PUBLIC_HEADERS}
    ${SOURCES}

    ${QM_FILES}
    ${CMAKE_CURRENT_BINARY_DIR}/QtPdCom_ts.qrc
)

configure_file(QtPdCom1.h.in "${CMAKE_CURRENT_BINARY_DIR}/QtPdCom1.h")

# set include path

target_include_directories(${PROJECT_NAME} PUBLIC
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}>"
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
    "$<INSTALL_INTERFACE:include>"
)

# link to required and optional libraries

target_link_libraries(${PROJECT_NAME} PUBLIC
    EtherLab::pdcom5
    Qt5::Core
    Qt5::Gui
    Qt5::Network
  PRIVATE
    Qt5::Xml
)

if(WIN32)
    # for gethostname in Process.cpp
    target_link_libraries(${PROJECT_NAME} PRIVATE -lwsock32)
endif()

# use sasl?

if (USE_SASL)
    target_link_libraries(${PROJECT_NAME} PRIVATE
        EtherLab::pdcom5-sasl
    )
    target_compile_definitions(${PROJECT_NAME} PRIVATE PDCOM_USE_SASL)
endif()

# bake in version information

if (VERSION_HASH)
    # Hash was given as option, so write it directly
    # useful for OBS packaging
    file (WRITE "${CMAKE_CURRENT_BINARY_DIR}/git_revision_hash.h"
        "#define GIT_REV \"${VERSION_HASH}\"
")
else()
    # recompute hash on every `make` invocation
    # Note: CMake variable definitions (-D) must be before the -P option!
    add_custom_target (GitRevision
        BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/git_revision_hash.h"
        COMMAND ${CMAKE_COMMAND}
            -DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
            -DHASH_MACRO_NAME="GIT_REV"
            -DTARGET_FILE="${CMAKE_CURRENT_BINARY_DIR}/git_revision_hash.h"
            -P ${CMAKE_CURRENT_SOURCE_DIR}/git_revision.cmake
    )
    add_dependencies(${PROJECT_NAME} GitRevision)
endif()

# configure symbol versioning

if (USE_SYMBOL_VERSIONING)
    math(EXPR VERSCRIPT_ARCH "8 * ${CMAKE_SIZEOF_VOID_P}")
    add_custom_command(OUTPUT QtPdCom.map
        COMMAND "${PYTHON_EXECUTABLE}"
        ARGS "${CMAKE_CURRENT_SOURCE_DIR}/symbol-versioning/symbol_version_tool.py"
                -i "${CMAKE_CURRENT_SOURCE_DIR}/QtPdCom.map.yaml"
                -o "${CMAKE_CURRENT_BINARY_DIR}/QtPdCom.map"
                --arch "generic${VERSCRIPT_ARCH}"
                GenerateLinkerVersionScript
        MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/QtPdCom.map.yaml"
        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/symbol-versioning/symbol_version_tool.py"
    )
    # to force relinking upon changing the map file,
    # we add an OBJECT_DEPENDS
    set(PROCESS_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/QtPdCom.map")
    set_source_files_properties(src/Process.cpp PROPERTIES OBJECT_DEPENDS "${PROCESS_DEPENDS}")
    # inform linker about version script
    string(APPEND CMAKE_SHARED_LINKER_FLAGS " -Wl,-version-script=${CMAKE_CURRENT_BINARY_DIR}/QtPdCom.map")

    if (CHECK_SYMBOL_VERSIONING)
        add_custom_target(CheckVersionScript ALL
            "${PYTHON_EXECUTABLE}"
            "${CMAKE_CURRENT_SOURCE_DIR}/symbol-versioning/symbol_version_tool.py"
            -i "${CMAKE_CURRENT_SOURCE_DIR}/QtPdCom.map.yaml"
            --arch "generic${VERSCRIPT_ARCH}"
            --library $<TARGET_FILE:${PROJECT_NAME}>
            CheckGeneratedLibraries
            COMMENT "Checking whether all symbols are versioned correctly..."
        )
    endif()
endif()

# enable resource compiler etc.

set_target_properties(${PROJECT_NAME} PROPERTIES
    AUTOUIC ON
    AUTOMOC ON
    AUTORCC ON
    PUBLIC_HEADER "${PUBLIC_HEADERS}"
    CXX_VISIBILITY_PRESET "hidden"
    VISIBILITY_INLINES_HIDDEN 1
    VERSION "${SOVERSION}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
    SOVERSION ${SOVERSION}
)

target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)
# install library and headers

install(TARGETS ${PROJECT_NAME}
    EXPORT ${PROJECT_NAME}Targets
    ARCHIVE DESTINATION       "${CMAKE_INSTALL_LIBDIR}"
    LIBRARY DESTINATION       "${CMAKE_INSTALL_LIBDIR}"
    RUNTIME DESTINATION       "${CMAKE_INSTALL_BINDIR}"
    PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}"
)

install(FILES "${CMAKE_CURRENT_BINARY_DIR}/QtPdCom1.h"
        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)

# export target for use in other projects

set(ConfigPackageLocation "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
configure_package_config_file(Config.cmake.in
        "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake"
    INSTALL_DESTINATION ${ConfigPackageLocation}
)
write_basic_package_version_file(
        "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake"
    COMPATIBILITY SameMajorVersion
)

install(EXPORT ${PROJECT_NAME}Targets
    NAMESPACE EtherLab::
    FILE "${PROJECT_NAME}Targets.cmake"
    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
install(FILES
        "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake"
        "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake"
    DESTINATION ${ConfigPackageLocation}
)
export(EXPORT ${PROJECT_NAME}Targets
    FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Targets.cmake"
    NAMESPACE EtherLab::
)

# build example

if(BUILD_EXAMPLE)
    add_library(EtherLab::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
    add_subdirectory(example)
endif()
