/*
 * Copyright 2023 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "eventwatcher.h"
#include <KWindowSystem>
#include "policy.h"

bool EventWatcher::m_isTablet = false;
EventWatcher::EventWatcher(QObject *parent) 
    : QObject(parent)
    , m_statusManagerInterface(new com::kylin::statusmanager::interface("com.kylin.statusmanager.interface", "/",
                                                                    QDBusConnection::sessionBus(), this))
    , m_kwinInterface(new org::ukui::KWin("org.ukui.KWin", "/KWin",
                                          QDBusConnection::sessionBus(), this))
    , m_processManager(new com::kylin::ProcessManager("com.kylin.ProcessManager",
                                                      "/com/kylin/ProcessManager",
                                                       QDBusConnection::systemBus()))
    , m_appLauncherDaemon(new AppLauncherDaemon(this))
{
    if (!m_statusManagerInterface->isValid()) {
        m_isTablet = false;
        qWarning() << "com.kylin.statusmanager.interface is not valid.";
    } else {
        m_isTablet = m_statusManagerInterface->get_current_tabletmode();
    }   
    initConnections();
#ifdef QT_DEBUG
//    m_isTablet = true;
#endif
}

bool EventWatcher::isTabletMode()
{
    return m_isTablet;
}

void EventWatcher::onTabletModeChanged(bool tabletMode)
{
    qDebug() << "onTabletModeChanged " << tabletMode;
    if (tabletMode == m_isTablet) {
        return;
    }

    m_isTablet = tabletMode;
    Q_EMIT tabletModeChanged(tabletMode);
}

void EventWatcher::onActiveWindowChanged(quint32 wid)
{
    doWindowActiveChanged(wid);
}

void EventWatcher::onWindowAdded(quint32 wid)
{
    KWindowInfo windowInfo(wid, NET::WMPid);
    int pid = windowInfo.pid();
    if (pid <= 0) {
        qWarning() << __FUNCTION__ << "pid is inValid!";
        return;
    }
    Q_EMIT windowAdded(wid, pid);

}

void EventWatcher::onWindowRemoved(quint32 wid)
{
    Q_EMIT windowRemoved(wid);
}

void EventWatcher::onActiveWaylandWindowChanged(kdk::WindowId wid)
{
    doWindowActiveChanged(wid.toUInt());
}

void EventWatcher::onWaylandWindowAdded(kdk::WindowId wid)
{
    int pid = kdk::WindowManager::getPid(wid);
    if (pid <= 0) {
        qWarning() << __FUNCTION__ << "pid is inValid!";
        return;
    }
    Q_EMIT windowAdded(wid.toUInt(), pid);
}

void EventWatcher::onWaylandWindowRemoved(kdk::WindowId wid)
{
    Q_EMIT windowRemoved(wid.toUInt());
}

void EventWatcher::onDbusNameAcquired(QString name, QString newOwner, QString oldOwner)
{
    Q_UNUSED(newOwner)
    Q_UNUSED(oldOwner)

    if (!name.contains(mprisPrefix)) {
        return;
    }
    QDBusInterface mediaInterface("org.freedesktop.DBus",
                                  "/org/freedesktop/DBus",
                                  "org.freedesktop.DBus",
                                  QDBusConnection::sessionBus());

    QDBusPendingReply<quint32> pidReply = mediaInterface.call(QStringLiteral("GetConnectionUnixProcessID"), name);
    quint32 pid = pidReply.value();
    if (pid > 0) {
        Q_EMIT mprisDbusAdded(pid, name);
    }
}

void EventWatcher::initConnections()
{
    connect(m_appLauncherDaemon, &AppLauncherDaemon::aboutToQuitAppManager,
            this, &EventWatcher::onAboutToQuitApp);

    connect(m_appLauncherDaemon, &AppLauncherDaemon::childProcessFinished,
            this, &EventWatcher::onChildPidFinished);

    connect(m_statusManagerInterface, &com::kylin::statusmanager::interface::mode_change_signal,
            this, &EventWatcher::onTabletModeChanged);

    connect(m_processManager, &com::kylin::ProcessManager::ResourceThresholdWarning,
            this, &EventWatcher::onResourceThresholdWarning);

    QDBusConnection::sessionBus().connect("org.freedesktop.DBus",
                                          "/org/freedesktop/DBus",
                                          "org.freedesktop.DBus",
                                          "NameOwnerChanged", this,
                                          SLOT(onDbusNameAcquired(QString,QString,QString)));

    bool m_isWayland = Policy::isWaylandPlatform();
    if (m_isWayland) {
        connect(kdk::WindowManager::self(), &kdk::WindowManager::activeWindowChanged,
                    this, &EventWatcher::onActiveWaylandWindowChanged);

        connect(kdk::WindowManager::self(), &kdk::WindowManager::windowAdded,
                    this, &EventWatcher::onWaylandWindowAdded);

        connect(kdk::WindowManager::self(), &kdk::WindowManager::windowRemoved,
                this, &EventWatcher::onWaylandWindowRemoved);
    } else {
        connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged,
                this, &EventWatcher::onActiveWindowChanged);

        connect(KWindowSystem::self(), &KWindowSystem::windowAdded,
                this, &EventWatcher::onWindowAdded);

        connect(KWindowSystem::self(), &KWindowSystem::windowRemoved,
                this, &EventWatcher::onWindowRemoved);
    }
}

void EventWatcher::doWindowActiveChanged(quint32 wid)
{
    static quint32 preWid = 0;
    static int prePid = 0;

    int pid = 0;
    if (Policy::isWaylandPlatform()) {
        pid = kdk::WindowManager::getPid(wid);
    } else {
        KWindowInfo windowInfo(wid, NET::WMPid);
        pid = windowInfo.pid();
    }
    if (pid <= 0) {
        qWarning() << __FUNCTION__ << "pid is inValid!";
        return;
    }

    Q_EMIT activeWindowChanged(wid, pid, preWid, prePid);

    preWid = wid;
    prePid = pid;
}

void EventWatcher::onResourceThresholdWarning(const QString &resource, int level)
{
    qDebug() << "onResourceThresholdWarning " << resource << level;
    QMetaEnum meta = QMetaEnum::fromType<Policy::Feature>();
    Policy::Feature emResource = (Policy::Feature)meta.keyToValue(resource.toLocal8Bit().data());
    Q_EMIT resourceThresholdWarning(emResource, Policy::ResourceUrgency(level - 1));
}

void EventWatcher::onAboutToQuitApp()
{
    Q_EMIT aboutToQuitApp();
}

void EventWatcher::onChildPidFinished(const int &pid)
{
    Q_EMIT ChildPidFinished(pid);
}
