// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "docksettings.h"
#include "dsglobal.h"
#include "constants.h"
#include "dockpanel.h"
#include "dockdbusproxy.h"

#include <QObject>
#include <QJsonObject>

#ifdef HAVE_DDE_API_EVENTLOGGER
#include <dde-api/eventlogger.hpp>
#endif

#include <DWindowManagerHelper>
#include <appletbridge.h>

DGUI_USE_NAMESPACE
DS_USE_NAMESPACE
namespace {
constexpr auto DockVersionV1 = "1.0";
constexpr qint64 EVENT_LOGGER_TRAY_PLUGIN_LIST = 1000610007;

QList<DS_NAMESPACE::DAppletDock *> dockApplets(dock::DockPanel *panel)
{
    QList<DS_NAMESPACE::DAppletDock *> list;
    if (!panel) return list;

    for (auto *applet : panel->applets()) {
        if (applet->pluginMetaData().value("dock").toMap().value("version").toString() == DockVersionV1) {
            if (auto dockApplet = qobject_cast<DS_NAMESPACE::DAppletDock *>(applet)) {
                list.append(dockApplet);
            }
        }
    }
    return list;
}
}
namespace dock {
DockDBusProxy::DockDBusProxy(DockPanel* parent)
    : QObject(parent)
    , m_oldDockApplet(nullptr)
    , m_trayApplet(nullptr)
{
    registerPluginInfoMetaType();

    connect(DockSettings::instance(), &DockSettings::pluginsVisibleChanged, this, [this] (const QVariantMap &pluginsVisible) {
        updateDockPluginsVisible(pluginsVisible);
    });
    connect(parent, &DockPanel::rootObjectChanged, this, [this]() {
        auto pluginsVisible = DockSettings::instance()->pluginsVisible();
        updateDockPluginsVisible(pluginsVisible);
    });

    for (auto *dockApplet : dockApplets(parent)) {
        connect(dockApplet, &DS_NAMESPACE::DAppletDock::supportedChanged, this, [this]() {
            auto pluginsVisible = DockSettings::instance()->pluginsVisible();
            updateDockPluginsVisible(pluginsVisible);
            Q_EMIT pluginsChanged();
        });
    }

    // Communicate with the other module
    auto getOtherApplet = [ = ] {
        {
            DAppletBridge bridge("org.deepin.ds.dock.tray");
            m_trayApplet = bridge.applet();
        }

        return m_trayApplet;
    };

    // TODO: DQmlGlobal maybe missing a  signal which named `appletListChanged`?
    QTimer *timer = new QTimer;
    timer->setInterval(1000);
    connect(timer, &QTimer::timeout, this, [ = ] {
        if (getOtherApplet()) {
            timer->stop();
            timer->deleteLater();
            connect(m_trayApplet, SIGNAL(pluginsChanged()), this, SIGNAL(pluginsChanged()));
            // Log the initial plugin list after 30s delay to ensure the list has fully loaded
            QTimer::singleShot(30000, this, [this]() {
                logInitialPluginState();
            });
        }
    });
    timer->start();
}

DockPanel* DockDBusProxy::parent() const
{
    return static_cast<DockPanel*>(QObject::parent());
}

QString DockDBusProxy::getAppID(const QString &desktopfile)
{
    const QString desktopLeft = "/applications/";
    const QString desktopSuffix = ".desktop";
    return desktopfile.mid(desktopfile.lastIndexOf(desktopLeft) + desktopLeft.size(), desktopfile.lastIndexOf(desktopSuffix) - desktopfile.lastIndexOf(desktopLeft) - desktopLeft.size());
}

QRect DockDBusProxy::geometry()
{
    return parent()->window() ? parent()->window()->geometry() : QRect();
}

void DockDBusProxy::updateDockPluginsVisible(const QVariantMap &pluginsVisible)
{
    for (auto *dockApplet : dockApplets(parent())) {
        if (!dockApplet->isSupported()) {
            continue;
        }
        DockItemInfo itemInfo = dockApplet->dockItemInfo();
        QString itemKey = itemInfo.itemKey;
        if (pluginsVisible.contains(itemKey)) {
           dockApplet->setVisible(pluginsVisible[itemKey].toBool());
        } else {
            auto settingPluginsVisible = DockSettings::instance()->pluginsVisible();
            settingPluginsVisible[itemKey] = true;
            DockSettings::instance()->setPluginsVisible(settingPluginsVisible);
        }
    }
}

QRect DockDBusProxy::frontendWindowRect()
{
    return parent()->frontendWindowRect();
}

Position DockDBusProxy::position()
{
    return parent()->position();
}

void DockDBusProxy::setPosition(Position position)
{
    parent()->setPosition(position);
}

HideMode DockDBusProxy::hideMode()
{
    return parent()->hideMode();
}

void DockDBusProxy::setHideMode(HideMode mode)
{
    parent()->setHideMode(mode);
}

HideState DockDBusProxy::hideState()
{
    return parent()->hideState();
}

uint DockDBusProxy::windowSizeEfficient()
{
    return parent()->dockSize();
}

void DockDBusProxy::setWindowSizeEfficient(uint size)
{
    qDebug() << size;
    parent()->setDockSize(size);
}

uint DockDBusProxy::windowSizeFashion()
{
    return parent()->dockSize();
}

void DockDBusProxy::setWindowSizeFashion(uint size)
{
    parent()->setDockSize(size);
}

int DockDBusProxy::displayMode()
{
    return parent()->itemAlignment();
}

void DockDBusProxy::setDisplayMode(int displayMode)
{
    parent()->setItemAlignment(static_cast<ItemAlignment>(displayMode));
}

bool DockDBusProxy::RequestDock(const QString &desktopFile, int index) {
    Q_UNUSED(index);
    QString id = getAppID(desktopFile);
    if (id.isEmpty())
        return false;

    DAppletBridge bridge("org.deepin.ds.dock.taskmanager");
    auto appletItem = bridge.applet();
    if (nullptr == appletItem)
        return false;
    bool res = true;
    QMetaObject::invokeMethod(appletItem, "RequestDock", Qt::DirectConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, id));
    return res;
}

bool DockDBusProxy::IsDocked(const QString &desktopFile)
{
    QString id = getAppID(desktopFile);
    if (id.isEmpty())
        return false;

    DAppletBridge bridge("org.deepin.ds.dock.taskmanager");
    auto appletItem = bridge.applet();
    if (nullptr == appletItem)
        return false;
    bool res = true;
    QMetaObject::invokeMethod(appletItem, "IsDocked", Qt::DirectConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, id));
    return res;
}

bool DockDBusProxy::RequestUndock(const QString &desktopFile)
{
    QString id = getAppID(desktopFile);
    DAppletBridge bridge("org.deepin.ds.dock.taskmanager");
    auto appletItem = bridge.applet();
    if (nullptr == appletItem)
        return false;
    bool res = true;
    QMetaObject::invokeMethod(appletItem, "RequestUndock", Qt::DirectConnection, Q_RETURN_ARG(bool, res), Q_ARG(QString, id));
    return res;
}

void DockDBusProxy::ActivateWindow(uint32_t win)
{
    qDebug() << "ActivateWindow" << win;
    
    // 通过 taskmanager 来激活窗口，避免直接依赖 X11Utils
    DAppletBridge bridge("org.deepin.ds.dock.taskmanager");
    auto appletItem = bridge.applet();
    if (appletItem) {
        QMetaObject::invokeMethod(appletItem, "activateWindow", Qt::QueuedConnection, Q_ARG(uint32_t, win));
    } else {
        qWarning() << "Cannot activate window: taskmanager applet not available";
    }
}

QStringList DockDBusProxy::GetLoadedPlugins()
{
    // TODO: implement this function
    return QStringList();
}

DockItemInfos DockDBusProxy::plugins()
{
    DockItemInfos iteminfos;
    if (m_trayApplet) {
        QMetaObject::invokeMethod(m_trayApplet, "dockItemInfos", Qt::DirectConnection, qReturnArg(iteminfos));
    }

    for (auto *dockApplet : dockApplets(parent())) {
        if (!dockApplet->isSupported()) {
            continue;
        }
        iteminfos.append(dockApplet->dockItemInfo());
    }

    return iteminfos;
}

void DockDBusProxy::logInitialPluginState()
{
    QStringList visibleItemKeys;
    for (const auto &info : plugins()) {
        if (info.visible) {
            visibleItemKeys.append(info.itemKey);
        }
    }

#ifdef HAVE_DDE_API_EVENTLOGGER
    DDE_EventLogger::EventLogger::instance().writeEventLog(
        DDE_EventLogger::EventLoggerData(EVENT_LOGGER_TRAY_PLUGIN_LIST, QStringLiteral("dock_init"), {
            {QStringLiteral("tray_plugin_list"), visibleItemKeys.join(QStringLiteral(","))}
        }));
#endif
}

void DockDBusProxy::logCurrentVisiblePluginList(const QString &changedItemKey, bool changedVisible)
{
    QStringList visibleItemKeys;
    for (const auto &info : plugins()) {
        if (info.itemKey == changedItemKey) {
            if (changedVisible)
                visibleItemKeys.append(info.itemKey);
        } else if (info.visible) {
            visibleItemKeys.append(info.itemKey);
        }
    }

#ifdef HAVE_DDE_API_EVENTLOGGER
    DDE_EventLogger::EventLogger::instance().writeEventLog(
        DDE_EventLogger::EventLoggerData(EVENT_LOGGER_TRAY_PLUGIN_LIST, QStringLiteral("dock_plugins_changed"), {
            {QStringLiteral("tray_plugin_list"), visibleItemKeys.join(QStringLiteral(","))}
        }));
#endif
}

void DockDBusProxy::ReloadPlugins()
{
    parent()->ReloadPlugins();
}

void DockDBusProxy::callShow()
{
    parent()->callShow();
}

void DockDBusProxy::setItemOnDock(const QString &settingKey, const QString &itemKey, bool visible)
{
    for (auto *dockApplet : dockApplets(parent())) {
        if (dockApplet->dockItemInfo().itemKey == itemKey) {
            dockApplet->setVisible(visible);
            auto pluginsVisible = DockSettings::instance()->pluginsVisible();
            pluginsVisible[itemKey] = visible;
            DockSettings::instance()->setPluginsVisible(pluginsVisible);
            Q_EMIT pluginVisibleChanged(itemKey, visible);
            logCurrentVisiblePluginList(itemKey, visible);
            return;
        }
    }

    if (m_trayApplet) {
        Q_EMIT pluginVisibleChanged(itemKey, visible);
        QMetaObject::invokeMethod(m_trayApplet, "setItemOnDock", Qt::QueuedConnection, settingKey, itemKey, visible);
        logCurrentVisiblePluginList(itemKey, visible);
    }
}

void DockDBusProxy::setPluginVisible(const QString &pluginName, bool visible)
{
    // TODO: implement this function
    Q_UNUSED(pluginName)
    Q_UNUSED(visible)
}

bool DockDBusProxy::getPluginVisible(const QString &pluginName)
{
    // TODO: implement this function
    Q_UNUSED(pluginName)
    return true;
}

QString DockDBusProxy::getPluginKey(const QString &pluginName)
{
    // TODO: implement this function
    Q_UNUSED(pluginName)
    return QString();
}

void DockDBusProxy::resizeDock(int offset, bool dragging)
{
    Q_UNUSED(dragging)
    parent()->setIsResizing(true);
    parent()->setDockSize(offset);
    parent()->setIsResizing(false);
}

bool DockDBusProxy::showInPrimary() const
{
    return parent()->showInPrimary();
}

void DockDBusProxy::setShowInPrimary(bool newShowInPrimary)
{
    if (parent()->showInPrimary() == newShowInPrimary)
        return;

    parent()->setShowInPrimary(newShowInPrimary);
}

bool DockDBusProxy::locked() const
{
    return parent()->locked();
}

void DockDBusProxy::setLocked(bool newLocked)
{
    if (parent()->locked() == newLocked)
        return;

    parent()->setLocked(newLocked);
}

}
