/*
 * Copyright (C) 2024, KylinSoft Co., Ltd.

 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2, 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 Library General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "multitaskviewmanager.h"

// kf5
#include <KF5/KGlobalAccel/KGlobalAccel>
#include <KF5/KWindowSystem/KWindowSystem>
// kysdk
#include <libkysysinfo.h>
#include <libkydatacollect.h>
// Qt
#include <QAction>
#include <QApplication>
#include <QQmlContext>
#include <QtDBus>
#include <QTranslator>
#include <QLocale>
#include <QtConcurrent>

#include "../desktopbackground.h"
#include "../icontheme.h"
#include "../windowmanagerinterface.h"
#include "../appwindowfiltermodel.h"
#include "../ukui-flow.h"

bool MultitaskViewManager::m_isOpenGLBackend = true;

MultitaskViewManager::MultitaskViewManager()
{
    initializeProjectType();
    registerQmlTypes();
    registerShortcut();
    initDBusService();
    initGuiLayouDirection();
    installTranslator();
}

MultitaskViewManager::~MultitaskViewManager()
{
    m_dbusService.reset();
    close();
    if (m_translator) {
        m_translator->deleteLater();
        m_translator = nullptr;
    }
}

bool MultitaskViewManager::isCursorNavigationKey(QKeyEvent *e) const
{
    if (e->key() == Qt::Key_Tab || e->key() == Qt::Key_Left ||
        e->key() == Qt::Key_Right || e->key() == Qt::Key_Up ||
        e->key() == Qt::Key_Down || e->key() == Qt::Key_Home ||
        e->key() == Qt::Key_End || e->key() == Qt::Key_PageUp ||
        e->key() == Qt::Key_PageDown) {
        return true;
    }
    return false;
}

void MultitaskViewManager::grabbedKeyboardEvent(QKeyEvent *e)
{
    if (m_view.get()) {
        if (e->type() == QEvent::KeyPress && isCursorNavigationKey(e)) {
            if (!m_isShowTabBorder) {
                m_isShowTabBorder = true;
            }
            //当该事件会作为走焦事件处理时，进行数据埋点
            QByteArray keyName = QKeySequence(e->key()).toString().toLatin1();
            QtConcurrent::run([keyName]() {
                const char* text = keyName.data();
                KCustomProperty property[1];
                property[0].key = (char*)"keyName";
                property[0].value = (char*)text;
                KTrackData *node = kdk_dia_data_init(KEVENTSOURCE_DESKTOP, KEVENT_CLICK);
                kdk_dia_append_custom_property(node, property, 1);
                kdk_dia_upload_default(node, (char*)"keyboard_navigation_event", (char*)"ukui-window-switch");
                kdk_dia_data_free(node);
            });
        }
        m_view->contentItem()->setFocus(true);
        QCoreApplication::sendEvent(m_view.get(), e);
    }
}

void MultitaskViewManager::activeAreaChangeDataCollect(const QString &areaName)
{
    QByteArray name = areaName.toLatin1();
    QtConcurrent::run([name]() {
        const char* text = name.data();
        KCustomProperty property[1];
        property[0].key = (char*)"activeAreaName";
        property[0].value = (char*)text;
        KTrackData *node = kdk_dia_data_init(KEVENTSOURCE_DESKTOP, KEVENT_CUSTOM);
        kdk_dia_append_custom_property(node, property, 1);
        kdk_dia_upload_default(node, (char*)"keyboard_navigation_active_area_changed_event", (char*)"ukui-window-switch");
        kdk_dia_data_free(node);
    });
}

void MultitaskViewManager::open()
{
    qDebug() << "开启多任务视图" << __FUNCTION__ << __LINE__ << isOpen << m_isKeyboardGrab;
    // Alt + Tab 快捷键快速连续触发时，xcb_grab_keyboard_reply 函数返回的 grab 状态为 GrabInvalidTime，然后
    // 导致 grabKeyboard 失败。大致原因为：xcb_grab_keyboard_unchecked() 指定的时间早于 last-keyboard-grab
    // 的时间或晚于当前 X server 的时间。具体原因待确认。
    // 因此在 grabKeyboard 失败后需要再次进行 grabKeyboard 操作。
    if (isOpen && !m_isKeyboardGrab) {
        m_isKeyboardGrab = KWin::effects->grabKeyboard(this);
        KWin::effects->setActiveFullScreenEffect(this);
        return;
    }
    // 可旋转的机器，多任务视图打开的时候，禁用旋转
    if (m_isLoadTabletModeQML && getRotationModeStatus()) {
        setRotationModeStatus(!m_isAutoRotateBeforeShowMultiTask);
    }
    initModel();
    m_isShowTabBorder = false;
    initView();
    if (!m_isKeyboardGrab) {
        m_isKeyboardGrab = KWin::effects->grabKeyboard(this);
        KWin::effects->setActiveFullScreenEffect(this);
    }
    isOpen = true;
    QtConcurrent::run([]() {
        KTrackData *node = kdk_dia_data_init(KEVENTSOURCE_DESKTOP, KEVENT_CUSTOM);
        kdk_dia_upload_default(node, (char*)"ukui_window_switch_start_event", (char*)"ukui-window-switch");
        kdk_dia_data_free(node);
    });
}

void MultitaskViewManager::close()
{
    if (!isOpen) {
        return;
    }
    //可旋转的机器，多任务视图打开前 是打开了旋转的 那么关闭多任务视图的同时  打开旋转
    if (m_isLoadTabletModeQML && m_isAutoRotateBeforeShowMultiTask) {
        setRotationModeStatus(m_isAutoRotateBeforeShowMultiTask);
    }
    if (m_isKeyboardGrab) {
        KWin::effects->ungrabKeyboard();
        m_isKeyboardGrab = false;
    }
    KWin::effects->setActiveFullScreenEffect(nullptr);
    destroyView();
    isOpen = false;
    QtConcurrent::run([]() {
        KTrackData *node = kdk_dia_data_init(KEVENTSOURCE_DESKTOP, KEVENT_CUSTOM);
        kdk_dia_upload_default(node, (char*)"ukui_window_switch_exit_event", (char*)"ukui-window-switch");
        kdk_dia_data_free(node);
    });
}

bool MultitaskViewManager::getRotationModeStatus()
{
    QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.statusmanager.interface",
                                                          "/",
                                                          "com.kylin.statusmanager.interface",
                                                          "get_auto_rotation");

    QDBusMessage response = QDBusConnection::sessionBus().call(message);

    if (response.type() == QDBusMessage::ReplyMessage) {
        m_isAutoRotateBeforeShowMultiTask = response.arguments().takeFirst().toBool();
    } else {
        qDebug() << "get Rotation mode failed" << response.type();
    }
    return m_isAutoRotateBeforeShowMultiTask;
}

void MultitaskViewManager::setRotationModeStatus(bool rotationModeStatus)
{
    QDBusInterface iface("com.kylin.statusmanager.interface",
                         "/",
                         "com.kylin.statusmanager.interface");

    if (!iface.isValid()) {
        qDebug() << QDBusConnection::sessionBus().lastError().message();
    }
    QDBusReply<void> reply = iface.call("set_auto_rotation",rotationModeStatus,"","");
    if (reply.isValid()) {
        qDebug() << "set Rotation mode success";
        return;
    }
    qDebug() << "set Rotation mode failed" << reply.error();
}

void MultitaskViewManager::initGuiLayouDirection()
{
    QString systemLang = QLocale::system().name();
    if (systemLang == "ug_CN" || systemLang == "ky_KG" || systemLang == "kk_KZ") {
        m_isLayoutLeftToRight = false;
    } else {
        m_isLayoutLeftToRight = true;
    }
}

void MultitaskViewManager::setShowingDesktop(bool showing)
{
    KWin::effects->setShowingDesktop(showing);
}

void MultitaskViewManager::initModel()
{
    qDebug() << "创建model数据" << __FUNCTION__;
    m_desktopListModel.reset(new DesktopListModel());
    m_appWindowListModel.reset(new AppWindowListModel());
}

void MultitaskViewManager::initializeProjectType()
{
    if ( getProjectCodeName() == QStringLiteral("V10SP1-edu")
      || getProjectSubName() == QStringLiteral("mavis")
      || getProductFeatures() == 2 ) {
        m_projectType = MultitaskViewManager::TABLET;
    } else if (getProjectCodeName() == QStringLiteral("V10SP1") && getProductFeatures() == '\u0000') {
        // '\u0000' 空格的ASCII码
        m_projectType = MultitaskViewManager::MASTER;
        m_isViewVerticalLayout = false;
    } else {
        m_projectType = MultitaskViewManager::TWOINONE;
        m_isViewVerticalLayout = true;
    }
}

void MultitaskViewManager::setQmlUrl()
{
    if (m_projectType == MultitaskViewManager::TABLET
        || (m_projectType == MultitaskViewManager::TWOINONE && WindowManagerInterface::getInstance()->isTabletMode())) {
        m_qmlUrl = QUrl("qrc:/qml/TabletViewMain.qml");
        m_isLoadTabletModeQML = true;
    } else if (m_projectType == MultitaskViewManager::MASTER
        || (m_projectType == MultitaskViewManager::TWOINONE && !WindowManagerInterface::getInstance()->isTabletMode())) {
        m_qmlUrl = QUrl("qrc:/qml/MultitaskView.qml");
        m_isLoadTabletModeQML = false;
    }
}

void MultitaskViewManager::registerQmlTypes()
{
    qmlRegisterType<DesktopBackground>("org.kylin.background", 3, 0, "DesktopBackground");
    qmlRegisterType<IconTheme>("IconTheme", 1, 0, "IconTheme");
    qmlRegisterType<AppWindowFilterModel>("AppWindowFilterModel", 1, 0, "AppWindowFilterModel");
    qmlRegisterType<DesktopListModel>("DesktopListModel", 1, 0, "DesktopListModel");
    qRegisterMetaType<QList<Window> >("QList<Window>");
    qmlRegisterUncreatableType<UkuiPositionerAttached>("org.ukui.positioner", 1, 0, "UkuiPositioner", "UkuiPositioner is an attached property");
    qmlRegisterUncreatableType<UkuiBasePositioner>("org.ukui.positioner", 1, 0, "UkuiBasePositioner", "UkuiBasePositioner is an abstract type that is only available as an attached property.");
    qmlRegisterType<UkuiFlow>("org.ukui.positioner", 1, 0, "UkuiFlow");
}

void MultitaskViewManager::registerShortcut()
{
    auto* action = new QAction(this);
    action->setObjectName("ShowMultitaskView");
    action->setText("Show Multitask View");

    QKeySequence keySeq(Qt::META + Qt::Key_Tab);
    KGlobalAccel::self()->setDefaultShortcut(action, QList<QKeySequence>() << keySeq);
    KGlobalAccel::self()->setShortcut(action, QList<QKeySequence>() << keySeq);
    connect(action, &QAction::triggered, this, &MultitaskViewManager::open);
}

void MultitaskViewManager::initDBusService()
{
    m_dbusService.reset(new DBusService(this));
}

void MultitaskViewManager::installTranslator()
{
    m_translator = new QTranslator(qApp);
    m_translator->load(QLocale::system(), QLatin1String("ukui-window-switch"),
                       QLatin1String("_"), TRANSLATION_DIR);
    QApplication::installTranslator(m_translator);
}

void MultitaskViewManager::initView()
{
    qDebug() << " 初始化多任务视图 " << __FUNCTION__;
    m_view.reset(new QQuickView());
    m_view->setFlags(Qt::BypassWindowManagerHint | Qt::FramelessWindowHint);
    // TODO: wayland 环境下不设置的话，会显示多任务视图的关闭动画
    KWindowSystem::setType(m_view->winId(), NET::Dock);
    qDebug() << "[DEBUG]: multitask view uses " << m_view->sceneGraphBackend();
    if (m_view->rendererInterface()->graphicsApi() < 2 || !KWin::effects->isOpenGLCompositing()) {
        m_isOpenGLBackend = false;
        WindowManagerInterface::getInstance()->setIsOpenGLBackend(m_isOpenGLBackend);
    }
    // 通过 QQmlContext::setContextProperty() 注册 C++ 的对象实例到 QML 中
    m_view->rootContext()->setContextProperty("multitaskViewManager", this);
    m_view->rootContext()->setContextProperty("multitaskViewModel", WindowManagerInterface::getInstance());
    m_view->rootContext()->setContextProperty("desktopListModel", m_desktopListModel.get());
    m_view->rootContext()->setContextProperty("appWindowListModel", m_appWindowListModel.get());
    m_view->rootContext()->setContextProperty("devicePixelRatio", qApp->primaryScreen()->logicalDotsPerInch() / 96.0);
    m_view->show();
    setQmlUrl();
    qDebug() << "加载的qml为 " << m_qmlUrl;
    m_view->setSource(m_qmlUrl);
    // 默认情况视图会随着QML中的根项调整大小，可以通过 setResizeMode(QQuickView::ResizeMode) 修改默认行为
    // 但是多任务视图的 rootObject 是 Repeater，且没有设置大小，所以必须通过 setGeometry 设置视图大小。
    // TODO： Repeater 继承了 Item 的 anchor 属性，Repeater 是否可以设置大小？
    // 必须在加载了QML之后才能设置 m_view 对象的大小，否则视图大小仍为 0x0
    qDebug() << " 设置的多任务视图的位置在" << KWin::effects->virtualScreenGeometry();
    m_view->setGeometry(KWin::effects->virtualScreenGeometry());
    //wayland环境下通过将active设空使其他窗口失去active状态，
    //否则打开多任务视图时，开始菜单等监听deactive信号的窗口不会关闭
    KWin::effects->activateWindow(nullptr);
    connectSignals();
    m_view->requestActivate();
}

void MultitaskViewManager::destroyView()
{
    if (m_view == nullptr) {
        return;
    }
    if (m_view->isVisible()) {
        m_view->hide();
    }

    // 1. 由于可以在QML中通过JS调用到close函数，
    // 因此，不能直接销毁 m_view 对象；必须延迟
    // 销毁该对象。
    // 2. m_model 对象可以立刻销毁。但是，为了对称
    // 和简化逻辑，需要保证 m_view 对象和 m_model 对象的
    // 互相依赖的关系。因此，同时延迟销毁 m_model 对象。
    // 3. MultitaskViewManager的析构函数需要确保
    // m_view 对象和 m_model 对象完成销毁。为了简化逻辑，
    // MultitaskViewManager的析构函数选择直接调用
    // destroyView函数，即采用了延迟销毁的做法。
    m_view.release()->deleteLater();
    m_desktopListModel.release()->deleteLater();
    m_appWindowListModel.release()->deleteLater();
}

void MultitaskViewManager::connectSignals()
{
    qDebug() << "创建信号" << __FUNCTION__;
    connect(WindowManagerInterface::getInstance(), &WindowManagerInterface::appWindowCreated, this, &MultitaskViewManager::close);
    connect(WindowManagerInterface::getInstance(), &WindowManagerInterface::screenCountChanged, this, &MultitaskViewManager::close);
    connect(WindowManagerInterface::getInstance(), &WindowManagerInterface::screenSizeChanged, this, &MultitaskViewManager::close);
    connect(WindowManagerInterface::getInstance(), &WindowManagerInterface::tabletModeChanged, this, &MultitaskViewManager::close);
    // 作为kwin插件，多任务视图view需要设置Qt::BypassWindowManagerHint，否则无法显示（原因未知）
    // 由于不受窗管管控，需要监听锁屏信号主动退出
    QDBusConnection::sessionBus().connect("org.ukui.ScreenSaver", "/", "org.ukui.ScreenSaver", "lock", this, SLOT(close()));
}

QString MultitaskViewManager::getProjectCodeName()
{
    return QString(kdk_system_get_projectName());
}

QString MultitaskViewManager::getProjectSubName()
{
    return QString(kdk_system_get_projectSubName());
}

QString MultitaskViewManager::getProductFeatures()
{
    return QString(kdk_system_get_productFeatures());
}
