# CMakeLists.txt -- I Have Issues, KDE (Qt 5 + KDE Frameworks 5) port.
#
# ONE target: Debian 12 (bookworm), which ships Qt 5.15.8, KDE Frameworks 5.103,
# extra-cmake-modules 5.103 and CMake 3.25. There is deliberately no Qt 6 / KF 6
# path anywhere in this port -- no version option, no QT_VERSION_MAJOR branch,
# no dual-target source. One toolchain, one set of spellings.
#
# Minimum versions and why:
#   Qt 5.15.0 -- the release that added QNetworkRequest::setTransferTimeout(int),
#                which src/github/QtHttpClient.cpp needs so a stalled request
#                cannot pin the sync worker thread forever. Everything else the
#                port uses is older: sendCustomRequest()'s QIODevice* overload
#                (5.0) and its QByteArray overload (5.8), rawHeaderPairs,
#                QNetworkAccessManager::setRedirectPolicy (5.9),
#                QStyledItemDelegate, QSplitter, QStackedWidget, QSaveFile.
#                The floor is 5.15.0 rather than bookworm's exact 5.15.8 so a
#                point-release difference cannot fail configure for no reason.
#   KF 5.103  -- what bookworm ships. The real API floor is 5.100, which added
#                KMessageBox::warningTwoActionsCancel() and the PrimaryAction /
#                SecondaryAction button codes that
#                MainWindow::confirmDiscardChanges() reads; the pointer-to-member
#                KStandardAction overloads in setupActions() need 5.23. The KF
#                classes used (KXmlGuiWindow, KActionCollection, KStandardAction,
#                KMessageBox, KLocalizedString, KAboutData, KColorScheme,
#                KWallet) are all long-standing KF5.
#
# The shared core in libs/issueskit is compiled straight into a static library
# here, exactly as libs/issueskit/README.md prescribes. It contains no Qt and no
# Q_OBJECT, so AUTOMOC is switched OFF for that target.
cmake_minimum_required(VERSION 3.16)

# The version is declared HERE and nowhere else. src/main.cpp receives it as the
# IHI_VERSION macro (see target_compile_definitions below) rather than repeating
# the literal, because the three copies this port used to carry -- project(),
# KAboutData and the AppStream metainfo -- had already drifted apart. This is the
# same arrangement apps/Gnome uses with meson.project_version().
project(ihaveissues VERSION 1.0.3 LANGUAGES CXX)

# The GitHub sync feature: the src/github/ HTTP + wallet layer, the sync dialog,
# the Sync action, and the (inert, never-synced) Azure DevOps coordinates on the
# Project Settings sheet. OFF by default, which matches the Windows client.
#
# Turning it OFF removes the code from the build entirely -- the sources are not
# compiled and KF5::Wallet / Qt5::Network are not linked -- rather than merely
# hiding the menu entry. Documents still round-trip both the github and the
# azureDevOps integration blocks untouched: that lives in libs/issueskit and is
# not conditional.
option(IHAVEISSUES_ENABLE_SYNC "Build the GitHub sync feature (sync dialog, HTTP client, wallet-backed token store, Azure DevOps coordinates)." OFF)

set(QT_MIN_VERSION 5.15.0)
set(KF_MIN_VERSION 5.103.0)

# extra-cmake-modules is released in lockstep with the frameworks, so the ECM
# floor is simply KF_MIN_VERSION. ECM 5.103 also brings the KDEInstallDirs
# variables used further down (KDE_INSTALL_KXMLGUI5DIR, KDE_INSTALL_METAINFODIR).
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})

# Chosen BEFORE KDECompilerSettings is included, on purpose: ECM only picks a
# default C++ standard when CMAKE_CXX_STANDARD has not already been set, so
# setting it first is what makes C++17 survive the include rather than be
# quietly replaced.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(FeatureSummary)

# Network is only needed by src/github/QtHttpClient.cpp, which is the ONLY file
# in this port that includes a QtNetwork header -- verified by grep over src/.
# With sync off, nothing in the target references QNetworkAccessManager, so the
# component is not even searched for.
set(IHAVEISSUES_QT_COMPONENTS Core Widgets)
if(IHAVEISSUES_ENABLE_SYNC)
    list(APPEND IHAVEISSUES_QT_COMPONENTS Network)
endif()

find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS ${IHAVEISSUES_QT_COMPONENTS})

# CoreAddons  -- KAboutData
# I18n        -- KLocalizedString / i18n()
# XmlGui      -- KXmlGuiWindow, KActionCollection
# Config      -- KConfig, pulled in by the window-state saving KMainWindow does
# ConfigWidgets  -- KStandardAction, KColorScheme
# WidgetsAddons  -- KMessageBox, KStandardGuiItem
# Wallet      -- KWallet::Wallet, only when IHAVEISSUES_ENABLE_SYNC is ON
#
# ConfigWidgets and WidgetsAddons are listed explicitly even though XmlGui
# depends on both: this build includes <KStandardAction>, <KColorScheme>,
# <KMessageBox> and <KStandardGuiItem> directly, and a target should link what it
# includes rather than inherit it by accident.
set(IHAVEISSUES_KF_COMPONENTS
    CoreAddons
    I18n
    XmlGui
    Config
    ConfigWidgets
    WidgetsAddons
)
if(IHAVEISSUES_ENABLE_SYNC)
    list(APPEND IHAVEISSUES_KF_COMPONENTS Wallet)
endif()

find_package(KF5 ${KF_MIN_VERSION} REQUIRED COMPONENTS ${IHAVEISSUES_KF_COMPONENTS})

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC OFF)          # no .ui files: every widget is built in code
set(CMAKE_AUTORCC OFF)          # no .qrc files

# ---------------------------------------------------------------------------
# The shared core.
# ---------------------------------------------------------------------------

set(ISSUESKIT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libs/issueskit)

# Globbed on purpose: libs/issueskit/README.md asks every consumer to glob, so
# that adding a file to the core cannot silently leave one port behind.
# CONFIGURE_DEPENDS makes CMake re-glob when the directory changes.
file(GLOB ISSUESKIT_SRCS CONFIGURE_DEPENDS ${ISSUESKIT_DIR}/src/*.cpp)
if(NOT ISSUESKIT_SRCS)
    message(FATAL_ERROR "No sources found in ${ISSUESKIT_DIR}/src -- is the "
                        "shared library checked out?")
endif()

add_library(issueskit STATIC ${ISSUESKIT_SRCS})
target_include_directories(issueskit PUBLIC ${ISSUESKIT_DIR}/include)
target_compile_features(issueskit PUBLIC cxx_std_17)
# The core is pure C++17 with no Q_OBJECT anywhere. Scanning it with moc would
# be wasted work at best and a portability violation at worst.
set_target_properties(issueskit PROPERTIES
    AUTOMOC OFF
    POSITION_INDEPENDENT_CODE ON
)

# ---------------------------------------------------------------------------
# The application.
# ---------------------------------------------------------------------------

# ClaudeSkill.cpp is in this unconditional list on purpose: the skill installer
# writes a markdown description of the .issues format into a project folder and
# has nothing to do with GitHub sync, so it is built whether or not
# IHAVEISSUES_ENABLE_SYNC is on -- the same way the Windows client offers it.
#
# ErrorLog.cpp is unconditional for the same kind of reason: it is the message
# handler main() installs before anything else, and a build without sync has
# exactly as much need of a crash log as one with it. It adds no dependency --
# QFile, QDir, QDateTime and QMutex are all Qt5::Core, which is already linked.
set(ihaveissues_SRCS
    src/main.cpp

    src/ClaudeSkill.cpp
    src/ErrorLog.cpp

    src/ui/IssuePresentation.cpp
    src/ui/IssueListModel.cpp
    src/ui/IssueItemDelegate.cpp
    src/ui/IssueDetailWidget.cpp
    src/ui/IssueEditDialog.cpp
    src/ui/ProjectSettingsDialog.cpp
    src/ui/MainWindow.cpp
)

if(IHAVEISSUES_ENABLE_SYNC)
    list(APPEND ihaveissues_SRCS
        src/github/QtHttpClient.cpp
        src/github/KWalletTokenStore.cpp
        src/github/SyncWorker.cpp

        src/ui/GitHubSyncDialog.cpp
    )
endif()

add_executable(ihaveissues ${ihaveissues_SRCS})

target_include_directories(ihaveissues PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)

# IHI_VERSION must reach the compiler as -DIHI_VERSION="1.0.3", i.e. expanding to
# a C string literal, which is why the quotes are inside the value.
#
# IHAVEISSUES_ENABLE_SYNC is a compile definition as well as a source-list switch:
# the headers that declare the sync slot and the sync action need the same #ifdef,
# and AUTOMOC is given the target's COMPILE_DEFINITIONS, so moc sees it too.
target_compile_definitions(ihaveissues
    PRIVATE
        IHI_VERSION="${PROJECT_VERSION}"
)

if(IHAVEISSUES_ENABLE_SYNC)
    target_compile_definitions(ihaveissues PRIVATE IHAVEISSUES_ENABLE_SYNC)
endif()

target_link_libraries(ihaveissues
    PRIVATE
        issueskit
        Qt5::Core
        Qt5::Widgets
        KF5::CoreAddons
        KF5::I18n
        KF5::XmlGui
        KF5::ConfigCore
        KF5::ConfigWidgets
        KF5::WidgetsAddons
)

if(IHAVEISSUES_ENABLE_SYNC)
    target_link_libraries(ihaveissues
        PRIVATE
            Qt5::Network
            KF5::Wallet
    )
endif()

# ---------------------------------------------------------------------------
# Installation -- the standard KDE plumbing.
# ---------------------------------------------------------------------------

install(TARGETS ihaveissues ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

# KXmlGui looks the file up as <kxmlguidir>/<componentName>/<file>, and the
# component name is the first argument of KAboutData in src/main.cpp.
#
# The variable carries a "5": ECM 5 spells the KF5-versioned data directories
# KDE_INSTALL_KXMLGUI5DIR, KDE_INSTALL_KSERVICES5DIR, KDE_INSTALL_KSERVICETYPES5DIR
# and KDE_INSTALL_KNOTIFY5RCDIR. ECM 6 dropped the "5" from those names, so the
# unsuffixed KDE_INSTALL_KXMLGUIDIR is the ECM 6 spelling and is NOT defined here.
#
# The guard is not paranoia about a variable that might not exist: an undefined
# CMake variable expands to nothing, which would turn the DESTINATION into the
# absolute path "/ihaveissues". CMake would install to the filesystem root,
# ignoring CMAKE_INSTALL_PREFIX, WITHOUT any configure error -- and the first
# symptom would be a running app whose setupGUI() finds no .rc file and so comes
# up with no menus and no toolbar. Fail at configure time instead.
if(NOT KDE_INSTALL_KXMLGUI5DIR)
    message(FATAL_ERROR
        "KDE_INSTALL_KXMLGUI5DIR is not set -- ECM's KDEInstallDirs did not "
        "provide it. Installing ihaveissuesui.rc would silently target the "
        "filesystem root.")
endif()

install(FILES ihaveissuesui.rc
        DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/ihaveissues)

install(PROGRAMS com.druware.IHaveIssues.desktop
        DESTINATION ${KDE_INSTALL_APPDIR})

install(FILES com.druware.IHaveIssues.metainfo.xml
        DESTINATION ${KDE_INSTALL_METAINFODIR})

# shared-mime-info: registers *.issues as a subclass of application/json.
install(FILES com.druware.IHaveIssues.xml
        DESTINATION ${KDE_INSTALL_MIMEDIR})

# ---------------------------------------------------------------------------
# Translations.
# ---------------------------------------------------------------------------
#
# Every user-visible string goes through i18n(), so the catalogue is ready to be
# extracted. ki18n_install() is only called when a po/ directory actually exists,
# because the macro errors out on a missing directory.
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po)
    ki18n_install(po)
endif()

feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
