#include "multitaskviewmodel.h"

#include <algorithm>

#include <QDesktopWidget>
#include <QtDBus>
#include <QGSettings>

const QString UKUI_STYLE_SCHEMA = "org.ukui.style";
const QString UKUI_STYLE_SCHEMA_STYLENAME = "styleName";
const QString UKUI_STYLE_SCHEMA_FONTSIZE = "systemFontSize";

namespace MultitaskView {

MultitaskViewModel::MultitaskViewModel(QObject *parent) : QObject(parent)
{
    lastDesktopIndex = getCurrentDesktop();

    updateScreenCount();

    updateModelData();

    connectSignals();

    if (QGSettings::isSchemaInstalled(UKUI_STYLE_SCHEMA.toUtf8()))
    {
        settings_ = new QGSettings(UKUI_STYLE_SCHEMA.toUtf8());
    }
}

MultitaskViewModel::~MultitaskViewModel() = default;

QList<QVariant> MultitaskViewModel::getAppWindowList(int desktopIndex, int screenIndex) const
{
    if (!validateDesktopIndex(desktopIndex)
            ||!validateScreenIndex(screenIndex))
    {
        return QList<QVariant>();
    }

    auto desktopIter = std::find_if(desktopList_.cbegin(),
                                    desktopList_.cend(),
                                    [desktopIndex](const QVariant& desktop)
    {
        return desktop.value<Desktop>().index() == desktopIndex;
    });
    if (desktopIter == desktopList_.cend())
    {
        return QList<QVariant>();
    }

    const auto& desktop = desktopIter->value<Desktop>();
    const auto& screenList = desktop.screenList();
    auto screenIter = std::find_if(screenList.cbegin(),
                                   screenList.cend(),
                                   [screenIndex](const QVariant& screen)
    {
        return screen.value<Screen>().index() == screenIndex;
    });
    if (screenIter == screenList.cend())
    {
        return QList<QVariant>();
    }

    const auto& screen = screenIter->value<Screen>();
    auto appWindowList = screen.windowList();
    std::reverse(appWindowList.begin(), appWindowList.end());

    return appWindowList;
}

void MultitaskViewModel::removeAppWindow(QUuid windowId)
{
    auto* window = KWin::effects->findWindow(windowId);
    if (window == nullptr)
    {
        return;
    }

    window->closeWindow();
}

void MultitaskViewModel::removeAllAppWindowOnDesktop(int desktopIndex)
{
    if (!validateDesktopIndex(desktopIndex))
    {
        return;
    }

    for (int screenIndex = 0; screenIndex < logicalScreenCount(); ++screenIndex)
    {
        QList<QVariant> windowList = getAppWindowList(desktopIndex, screenIndex);

        for (QVariant &windowVariant : windowList)
        {
            Window window = windowVariant.value<Window>();

            removeAppWindow(window.uuid());
        }
    }
}

void MultitaskViewModel::moveAppWindowToDesktop(QUuid windowId,
                                                int srcDesktopInex,
                                                int destDesktopInex) const
{
    auto* window = KWin::effects->findWindow(windowId);
    if (window == nullptr
            || window->isOnAllDesktops()
            || !validateDesktopIndex(srcDesktopInex)
            || !validateDesktopIndex(destDesktopInex))
    {
        return;
    }

    auto desktopIndexList = window->desktops();
    if (!desktopIndexList.contains(srcDesktopInex))
    {
        return;
    }

    desktopIndexList.remove(desktopIndexList.indexOf(srcDesktopInex));

    if (desktopIndexList.contains(destDesktopInex))
    {
        return;
    }

    desktopIndexList.append(destDesktopInex);

    KWin::effects->windowToDesktops(window, desktopIndexList);
}

void MultitaskViewModel::activateAppWindow(QUuid windowId)
{
    auto* window = KWin::effects->findWindow(windowId);
    if (window == nullptr)
    {
        return;
    }

    KWin::effects->activateWindow(window);
}

QIcon MultitaskViewModel::getWindowIcon(QUuid windowId)
{
    // auto id = static_cast<WId>(windowId);
    auto* window = KWin::effects->findWindow(windowId);
    if(window == nullptr)
    {
        return QIcon("");
    }

    return window->icon();
}

QList<QVariant> MultitaskViewModel::getDesktopList() const
{
    return desktopList_;
}

void MultitaskViewModel::removeDesktop(int desktopIndex)
{
    auto desktopCount = KWin::effects->numberOfDesktops();
    if (desktopCount <= 1)
    {
        return;
    }

    if (!validateDesktopIndex(desktopIndex))
    {
        return;
    }

    const auto oldDesktopIndex = desktopIndex;
    if (desktopIndex == 1)
    {
        moveDesktopWindows(1, 2);

        desktopIndex = 2;
    }

    for (; desktopIndex <= desktopCount; desktopIndex++)
    {
        moveDesktopWindows(desktopIndex, desktopIndex - 1);
    }

    KWin::effects->setNumberOfDesktops(desktopCount - 1);

    updateModelData();

    emit desktopRemoved(oldDesktopIndex);
}

void MultitaskViewModel::appendDesktop()
{
    auto desktopCount = KWin::effects->numberOfDesktops();
    if (desktopCount >= MAX_DESKTOP_COUNT)
    {
        return;
    }

    KWin::effects->setNumberOfDesktops(desktopCount + 1);

    updateModelData();

    emit desktopAppended(desktopCount + 1);
}

void MultitaskViewModel::moveDesktop(int srcDesktopIndex, int destDesktopIndex)
{
    if (!validateDesktopIndex(srcDesktopIndex)
            || !validateDesktopIndex(destDesktopIndex))
    {
        return;
    }

    if (srcDesktopIndex == destDesktopIndex)
    {
        return;
    }

    const auto desktopCount = KWin::effects->numberOfDesktops();
    KWin::effects->setNumberOfDesktops(desktopCount + 1);

    auto tempDesktopIndex = desktopCount + 1;
    moveDesktopWindows(destDesktopIndex, tempDesktopIndex);
    // 将临时虚拟桌面的数据暂时更新到模型数据中，
    // 方便后面调用moveDesktopWindows函数将
    // 临时虚拟桌面中的窗口移动到来源虚拟桌面
    updateModelData();

    moveDesktopsOneStep(createDesktopIndexList(srcDesktopIndex, destDesktopIndex));

    moveDesktopWindows(tempDesktopIndex, srcDesktopIndex);

    KWin::effects->setNumberOfDesktops(desktopCount);

    updateModelData();

    emit desktopMoved(srcDesktopIndex, destDesktopIndex);
}

int MultitaskViewModel::getCurrentDesktop()
{
    return KWin::effects->currentDesktop();
}

void MultitaskViewModel::setCurrentDesktop(int desktopIndex)
{
    if (!validateDesktopIndex(desktopIndex))
    {
        return;
    }

    KWin::effects->setCurrentDesktop(desktopIndex);
}

QRect MultitaskViewModel::getFullScreenGeometry(int desktopIndex, int screenIndex) const
{
    KWin::EffectScreen* screen = KWin::effects->findScreen(screenIndex);
    return KWin::effects->clientArea(KWin::clientAreaOption::FullScreenArea,
                                     screen, desktopIndex);
}

bool MultitaskViewModel::hasModalChildWindow(QUuid windowId)
{
    auto* window = KWin::effects->findWindow(windowId);
    if (window == nullptr)
    {
        return false;
    }

    return window->findModal() != nullptr;
}

QString MultitaskViewModel::getUKUIStyleName() const
{
    // default to ukui-default
    QString styleName = "ukui-default";

    if (settings_ && settings_->keys().contains(UKUI_STYLE_SCHEMA_STYLENAME))
    {
        styleName = settings_->get(UKUI_STYLE_SCHEMA_STYLENAME).toString();
    }
    else
    {
        qWarning() << QString("未查询到Gesetting的键值(%1)").arg(UKUI_STYLE_SCHEMA_STYLENAME);
    }

    return styleName;
}

int MultitaskViewModel::getUKUIFontSize() const
{
    // default to 11
    int fontSize = 11;

    if (settings_ && settings_->keys().contains(UKUI_STYLE_SCHEMA_FONTSIZE))
    {
        fontSize = settings_->get(UKUI_STYLE_SCHEMA_FONTSIZE).toInt();
    }
    else
    {
        qWarning() << QString("未查询到Gesetting的键值(%1)").arg(UKUI_STYLE_SCHEMA_FONTSIZE);
    }

    return fontSize;
}

void MultitaskViewModel::onWindowAdded(KWin::EffectWindow *window)
{
    if(!shouldAcceptWindow(window))
    {
        if(!window->isModal()) {
            return;
        }
    }

    updateModelData();

    emit appWindowCreated(getWindowId(window));
}

void MultitaskViewModel::onWindowDeleted(KWin::EffectWindow *window)
{
    updateModelData();

    emit appWindowRemoved(getWindowId(window));
}

void MultitaskViewModel::onWindowActivated(KWin::EffectWindow* window)
{
    // KWin在发送windowActivated信号时
    // window对象是有可能为空的。对于多任务
    // 视图来说，只关心真正有窗口被激活的清空。
    // 因此，如果window对象为空，则不转发
    // 该信号
    if (window == nullptr)
    {
        return;
    }
    // 切换虚拟桌面时发出的窗口激活信号不需要发送到qml。
    if (window->isDesktop() || getCurrentDesktop() != lastDesktopIndex)
    {
        lastDesktopIndex = getCurrentDesktop();
        return;
    }

    emit appWindowActivated();
}

void MultitaskViewModel::onDesktopPresenceChanged(KWin::EffectWindow *window,
                                                  int oldDesktopIndex, int newDesktopIndex)
{
    if (!shouldAcceptWindow(window))
    {
        return;
    }

    updateModelData();

    emit appWindowDesktopChanged(getWindowId(window), oldDesktopIndex, newDesktopIndex);
}

void MultitaskViewModel::onNumberScreensChanged()
{
    updateModelData();

    const auto oldScreenCount = screenCount_;

    updateScreenCount();

    emit screenCountChanged(oldScreenCount, screenCount_);
}

void MultitaskViewModel::updateScreenCount()
{
    screenCount_ = KWin::effects->screens().size();
}

// static
bool MultitaskViewModel::isExtensionMode()
{
    QDesktopWidget desktop;
    for (int index = 0; index < desktop.numScreens(); index++)
    {
        if(KWin::effects->virtualScreenGeometry()
                == desktop.screenGeometry(index))
        {
            return false;
        }
    }

    return true;
}

int MultitaskViewModel::logicalScreenCount() const
{
    const auto screenCount = KWin::effects->screens().size();
    if (screenCount == 1
            || !isExtensionMode())
    {
        return 1;
    }

    return screenCount;
}

bool MultitaskViewModel::isTabletMode() const
{
    // default to PC mode.
    bool tabletMode = false;

    QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.statusmanager.interface",
                                                          "/",
                                                          "com.kylin.statusmanager.interface",
                                                          "get_current_tabletmode");

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

    if (response.type() == QDBusMessage::ReplyMessage)
    {
        tabletMode = response.arguments().takeFirst().toBool();
    }
    else
    {
        // invalid response, keep default.
    }

    return tabletMode;
}

QString MultitaskViewModel::rotationMode() const
{
    // default to "normal".
    QString rotation = "normal";

    QDBusMessage message = QDBusMessage::createMethodCall("com.kylin.statusmanager.interface",
                                                          "/",
                                                          "com.kylin.statusmanager.interface",
                                                          "get_current_rotation");

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

    if (response.type() == QDBusMessage::ReplyMessage)
    {
        rotation = response.arguments().takeFirst().toString();
    }
    else
    {
        // invalid response, keep default.
    }

    return rotation;
}

QList<QVariant> MultitaskViewModel::createWindowList(int desktopIndex, int screenIndex)
{
    if (!validateDesktopIndex(desktopIndex)
            || !validateScreenIndex(screenIndex))
    {
        return QList<QVariant>();
    }
    KWin::EffectScreen* screen = KWin::effects->findScreen(screenIndex);

    QList<QVariant> windowList;

    const auto& effectWindowList = KWin::effects->stackingOrder();
    for (auto iter = effectWindowList.cbegin();
         iter != effectWindowList.cend(); iter++)
    {
        auto* effectWindow = *iter;
        if (!shouldAcceptWindow(effectWindow))
        {
            continue;
        }

        if (!effectWindow->desktops().contains(desktopIndex)
                || effectWindow->screen() != screen)
        {
            continue;
        }

        Window window(getWindowId(effectWindow),
                      effectWindow->caption(),
                      effectWindow->pos().x(),
                      effectWindow->pos().y(),
                      effectWindow->decorationInnerRect().width(),
                      effectWindow->decorationInnerRect().height(),
                      effectWindow->isMinimized(),
                      getUuid(effectWindow));

        windowList.append(QVariant::fromValue(window));
    }

    return windowList;
}

void MultitaskViewModel::updateModelData()
{
    QList<QVariant> desktopList;

    const auto desktopCount = KWin::effects->numberOfDesktops();
    for (int desktopIndex = 1; desktopIndex <= desktopCount; desktopIndex++)
    {
        QList<QVariant> screenList;
        for (int screenIndex = 0; screenIndex < logicalScreenCount(); screenIndex++)
        {
            auto screen = QVariant::fromValue(Screen(screenIndex, createWindowList(desktopIndex, screenIndex)));
            screenList.append(std::move(screen));
        }

        auto desktop = QVariant::fromValue(Desktop(desktopIndex, screenList));
        desktopList.append(std::move(desktop));
    }

    desktopList_ = std::move(desktopList);
}

void MultitaskViewModel::moveDesktopWindows(int srcDesktopIndex, int destDesktopIndex)
{
    if (!validateDesktopIndex(srcDesktopIndex)
            || !validateDesktopIndex(destDesktopIndex))
    {
        return;
    }

    auto srcDesktopIter = std::find_if(desktopList_.cbegin(),
                                       desktopList_.cend(),
                                       [srcDesktopIndex](const QVariant& desktop)
    {
       return desktop.value<Desktop>().index() == srcDesktopIndex;
    });
    if (srcDesktopIter == desktopList_.cend())
    {
        return;
    }

    const auto& desktop = srcDesktopIter->value<Desktop>();
    const auto& screenList = desktop.screenList();
    for (auto screenIter = screenList.cbegin();
         screenIter != screenList.cend(); screenIter++)
    {
        const auto& screen = screenIter->value<Screen>();
        const auto& windowList = screen.windowList();
        for (auto windowIter = windowList.begin();
             windowIter != windowList.end(); windowIter++)
        {
            const auto& window = windowIter->value<Window>();
            moveAppWindowToDesktop(window.uuid(), srcDesktopIndex, destDesktopIndex);
        }
    }
}

void MultitaskViewModel::moveDesktopsOneStep(const QVector<int>& desktopIndexList)
{
    if (desktopIndexList.size() < 2)
    {
        return;
    }

    for (auto iter = desktopIndexList.cbegin();
         iter != desktopIndexList.cend() -1; iter++)
    {
        const auto destDesktopIndex = *iter;
        const auto srcDesktopIndex = *(iter + 1);
        moveDesktopWindows(srcDesktopIndex, destDesktopIndex);
    }
}

// static
WId MultitaskViewModel::getWindowId(KWin::EffectWindow *window)
{
    return window->parent()->property("windowId").toULongLong();
}

QUuid MultitaskViewModel::getUuid(KWin::EffectWindow *window)
{
//    qDebug() << "Uuid: " << window->parent()->property("internalId").toUuid();
    return window->parent()->property("internalId").toUuid();
}


// static
QVector<int> MultitaskViewModel::createDesktopIndexList(int srcDesktopIndex, int destDesktopIndex)
{
    QVector<int> desktopIndexList;

    if (!validateDesktopIndex(srcDesktopIndex)
            || !validateDesktopIndex(destDesktopIndex))
    {
        return desktopIndexList;
    }

    if (srcDesktopIndex > destDesktopIndex)
    {
        for (auto index = destDesktopIndex; index <= srcDesktopIndex; index++)
        {
            desktopIndexList.append(index);
        }
    }
    else
    {
        for (auto index = destDesktopIndex; index >= srcDesktopIndex; index--)
        {
            desktopIndexList.append(index);
        }
    }

    return desktopIndexList;
}

// static
bool MultitaskViewModel::validateDesktopIndex(int desktopIndex)
{
    return desktopIndex >= 1 && desktopIndex <= MAX_DESKTOP_COUNT;
}

bool MultitaskViewModel::validateScreenIndex(int screenIndex) const
{
    return screenIndex >= 0 && screenIndex < logicalScreenCount();
}

// static
bool MultitaskViewModel::shouldAcceptWindow(KWin::EffectWindow* window)
{
    return !window->isDeleted()
            && window->isNormalWindow()
            && !window->isSkipSwitcher()
            && window->isOnCurrentActivity()
            && window->acceptsFocus();
}

void MultitaskViewModel::connectSignals()
{
    QObject::connect(KWin::effects, &KWin::EffectsHandler::windowAdded,
                     this, &MultitaskViewModel::onWindowAdded);
    QObject::connect(KWin::effects, &KWin::EffectsHandler::windowDeleted,
                     this, &MultitaskViewModel::onWindowDeleted);
    QObject::connect(KWin::effects, &KWin::EffectsHandler::windowActivated,
                     this, &MultitaskViewModel::onWindowActivated);
    QObject::connect(KWin::effects, &KWin::EffectsHandler::desktopPresenceChanged,
                     this, &MultitaskViewModel::onDesktopPresenceChanged);
    QObject::connect(KWin::effects, SIGNAL(desktopChanged(int, int)),
                     this, SIGNAL(currentDesktopChanged(int, int)));
    // QObject::connect(KWin::effects, &KWin::EffectsHandler::numberScreensChanged,
    //                  this, &MultitaskViewModel::onNumberScreensChanged);
    QObject::connect(KWin::effects, &KWin::EffectsHandler::virtualScreenSizeChanged,
                     this, &MultitaskViewModel::screenSizeChanged);
    QDBusConnection::sessionBus().connect("com.kylin.statusmanager.interface",
                                          "/",
                                          "com.kylin.statusmanager.interface",
                                          "mode_change_signal",
                                          this,
                                          SLOT(tabletModeChanged(bool)));
    QDBusConnection::sessionBus().connect("com.kylin.statusmanager.interface",
                                          "/",
                                          "com.kylin.statusmanager.interface",
                                          "rotations_change_signal",
                                          this,
                                          SLOT(rotationModeChanged(QString)));
}

} // namespace MultitaskView
