/*
 * Peony-Qt
 *
 * 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 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/>.
 *
 * Authors: yangyanwei <yangyanwei@kylinos.cn>
 *
 */

#include "desktop-window-manager.h"
#include "peony-desktop-application.h"
#include "advanced-desktop-item-model.h"
#include "desktop-menu-plugin-manager.h"
#include <KWindowSystem>

#include "common.h"
#include "file-info.h"
#include "file-info-job.h"
#include "file-meta-info.h"
#include "global-settings.h"
#include "file-action-controler.h"
#include "global-settings-adapter.h"
#include "background-manager.h"
#include "screen-manager.h"
#include "theme-style.h"

#include "windowmanager/windowmanager.h"
#include "desktop-item-proxy-model.h"
#include "platformwindow.h"

#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlEngine>
#include <QJSEngine>
#include <QQuickItem>
#include <QScreen>
#include <QQuickView>
#include <QTimer>

static int desktop_window_id = 0;
static DesktopWindowManager *global_instance = nullptr;

DesktopWindowManager *DesktopWindowManager::getInstance()
{
    if (!global_instance) {
        global_instance = new DesktopWindowManager;
    }
    return global_instance;
}

DesktopWindowManager::DesktopWindowManager(QObject *parent) : QObject(parent)
{
    registerCppTypeToQml();
    initPeonyDbusService();
    m_model = PeonyDesktopApplication::getModel();
    connect(m_model, &AdvancedDesktopItemModel::emitFinish, this, &DesktopWindowManager::initModelFinish);
    connect(m_model, &AdvancedDesktopItemModel::sig_fileCreated, this, &DesktopWindowManager::fileCreated);
    connect(m_model, &AdvancedDesktopItemModel::setInfoPos, this, &DesktopWindowManager::setInfoPos);

    m_desktopAutoLayout = GlobalSettings::getInstance()->getValue(DESKTOP_USE_AUTO_LAYOUT).toBool();
    connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=](const QString &key){
        if (key == DESKTOP_USE_AUTO_LAYOUT) {
            m_desktopAutoLayout = GlobalSettings::getInstance()->getValue(DESKTOP_USE_AUTO_LAYOUT).toBool();
        }
    });

    m_layoutTimeLine = new QTimeLine(500, this);
    m_modeTimeLine = new QTimeLine(500, this);
    connect(m_modeTimeLine, &QTimeLine::finished, this, &DesktopWindowManager::switchDisplayMode);
    connect(SCREEN_MANAGER, &ScreenManager::displayModeChanged,  this, [=](){
        m_layoutTimeLine->stop();
        if (m_modeTimeLine->state() != QTimeLine::Running) {
            m_modeTimeLine->start();
        } else {
            m_modeTimeLine->setCurrentTime(0);
        }
    });

    connect(SCREEN_MANAGER, &ScreenManager::windowsRelocateRequired, this, [=](){
        if (m_modeTimeLine->state() == QTimeLine::Running) {
            return;
        }
        if (m_layoutTimeLine->state() != QTimeLine::Running) {
            m_layoutTimeLine->start();
        } else {
            m_layoutTimeLine->setCurrentTime(0);
        }
    });

    QDBusConnection connection = QDBusConnection::sessionBus();
    if (!connection.isConnected()) {
        qDebug() << "信号创建失败:" << connection.lastError().message();
        return;
    }
    bool isDbusAvailable = connection.connect("org.ukui.ScreenSaver", "/", "org.ukui.ScreenSaver",
                       "unlock", this,SLOT(startInitAnima()));
    m_dbusInterface = new QDBusInterface("org.ukui.ScreenSaver",
                                        "/",
                                        "org.ukui.ScreenSaver",
                                        QDBusConnection::sessionBus());

    bool isQuickStart = qEnvironmentVariableIsSet("UKUI_QUICK_START") && qEnvironmentVariableIntValue("UKUI_QUICK_START") == 1;
    isQuickStartAvailable = isQuickStart && isDbusAvailable;
}


DesktopWindowManager::~DesktopWindowManager()
{
    if (m_peonyDbusService) {
        delete m_peonyDbusService;
        m_peonyDbusService = nullptr;
    }
}

void DesktopWindowManager::registerCppTypeToQml()
{
    qmlRegisterType<Peony::AdvancedDesktopItemModel>("org.ukui.desktop", 1, 0, "AdvancedDesktopItemModel");
    qmlRegisterType<Peony::DesktopItemProxyModel>("org.ukui.desktop", 1, 0, "DesktopItemProxyModel");
    qmlRegisterType<FileActionController>("org.ukui.desktop", 1, 0, "FileActionController");
    qmlRegisterType<GlobalSettingsAdapter>("org.ukui.desktop", 1, 0, "GlobalSettingsAdapter");
    qmlRegisterType<ThemeStyle>("org.ukui.desktop", 1, 0, "ThemeStyle");
}

void DesktopWindowManager::addQmlDesktop(QScreen *screen)
{
    static bool pluginLoad = false;
    int screenId = SCREEN_MANAGER->getScreenId(screen);
    PlatformWindow *window = new PlatformWindow(screenId, screen);
    if (!pluginLoad) {
        connect(window, &PlatformWindow::viewUpdated, DesktopMenuPluginManager::getInstance(), &DesktopMenuPluginManager::loadAsync);
        pluginLoad = true;
    }
    qDebug() << "windowid" << screenId;
    SCREEN_MANAGER->addBackgroundWindow(window, screen, screenId);
    connect(this, &DesktopWindowManager::setSortType, window, &PlatformWindow::setSortType);
    connect(this, &DesktopWindowManager::setSortOrder, window, &PlatformWindow::setSortOrder);
    connect(this, &DesktopWindowManager::modeChanged, window, &PlatformWindow::updateView);
    connect(this, &DesktopWindowManager::emitFinish, window, &PlatformWindow::loadData);
    connect(m_layoutTimeLine, &QTimeLine::finished, window, &PlatformWindow::updateWindowGeometry);

    window->show();
    initializeWindowView(window);
    setupWindowConnections(window);

    if (m_initMetaInfo)
        window->loadData();
}

void DesktopWindowManager::singleScreenMode()
{
    qDebug() <<"change singleScreenMode: backgroundwindows count:" <<SCREEN_MANAGER->getBackgroundWindows().count()<<" screen count:"<<SCREEN_MANAGER->allScreens().count();

    bool isMainScreenOnlyData = true;
    for (auto window : SCREEN_MANAGER->getBackgroundWindows()) {
        PlatformWindow *tmpWindow = qobject_cast<PlatformWindow*>(window);
        if (!SCREEN_MANAGER->isPrimaryScreen(tmpWindow->bindScreen())) {
            tmpWindow->lower();
            qDebug()<<"lower screen window :"<<tmpWindow->dataCount()<<tmpWindow->id();
            if (0 != tmpWindow->dataCount()) {
                isMainScreenOnlyData = false;
            }
//            break;
        } else {
            qDebug()<<"raise screen window :"<<tmpWindow->dataCount()<<tmpWindow->id();
            SCREEN_MANAGER->getWindowById(0)->raise();
        }
    }
    qDebug() << "Has primary screen data " << isMainScreenOnlyData << "total item:" << m_model->rowCount();
    if (SCREEN_MANAGER->displayMode() == ScreenManager::Extend  && isMainScreenOnlyData) {
        m_model->clearExtendItemPos();
        return;
    }

    SCREEN_MANAGER->getWindowById(0)->requestActivate();

    m_model->getAllRestoreInfo();
    m_model->saveExtendItemInfo();
    Q_EMIT modeChanged();
}

void DesktopWindowManager::multiscreenMode()
{
    qDebug() <<"change multiscreen: backgroundwindows count:" <<SCREEN_MANAGER->getBackgroundWindows().count()<<" screen count:"<<SCREEN_MANAGER->allScreens().count();
    if (SCREEN_MANAGER->getBackgroundWindows().count() != SCREEN_MANAGER->allScreens().count()) {
        return;
    }

    QSet<int> idList;
    QList<QScreen*> screens = SCREEN_MANAGER->allScreens();
    for (QScreen *screen : screens) {
        int id = SCREEN_MANAGER->getScreenId(screen);
        idList.insert(id);
    }
    m_model->getAllRestoreInfo();
    m_model->resetExtendItemInfo(idList);
    Q_EMIT modeChanged();
}

void DesktopWindowManager::relocateIconView()
{
    qInfo() << "start relocate icon view";
    //task#74174 更新多屏显示,根据id过滤元素
    int primaryId = SCREEN_MANAGER->getPrimaryScreenId();
    PlatformWindow *primaryWindow = nullptr;

    for (auto window : SCREEN_MANAGER->getBackgroundWindows()) {
        PlatformWindow *tmpWindow = qobject_cast<PlatformWindow*>(window);
        if (SCREEN_MANAGER->isPrimaryScreen(tmpWindow->bindScreen())) {
            primaryWindow = qobject_cast<PlatformWindow*>(window);
            break;
        }
    }
    qDebug() << "primary screen id:" << primaryId;

    if (primaryWindow) {
        QList<QQuickView*> list = SCREEN_MANAGER->getBackgroundWindows();
        for (int i = 0 ; i < list.count(); i++) {
            PlatformWindow *window = qobject_cast<PlatformWindow*>(list[i]);
            if (window->id() == 0 && window != primaryWindow) {
                m_model->getAllRestoreInfo();
                // 交换ID
                qDebug() << "切换主副屏:" << primaryId;
                window->setId(primaryWindow->id());
                primaryWindow->setId(primaryId);
                window->updateView();
                primaryWindow->updateView();
                break;
            }
        }
        QTimer::singleShot(100, primaryWindow, [=]() {
             primaryWindow->requestActivate();
        });
    }
}

void DesktopWindowManager::raiseWid()
{
    QTimer::singleShot(2000, this, [=]() {
        //kdk::WindowManager::activateWindow(SCREEN_MANAGER->getWindowById(0), nullptr);
        return;
    });
}

void DesktopWindowManager::layoutViewItems()
{
//    for (auto bgWindow : m_bgWindows) {
//        auto view = bgWindow->getIconView();
//        view->layoutItems();
//    }
}

void DesktopWindowManager::initModelFinish()
{
    if (m_initMetaInfo)
        return;

    switchDisplayMode();
    relocateIconView();
    m_initMetaInfo = true;
    Q_EMIT emitFinish();
}

void DesktopWindowManager::markCreateFilePos(const QPoint &pos)
{
    //如果当前创建文件的屏幕满了，则找到一个空的屏幕，按照顺序添加
    PlatformWindow *window =qobject_cast<PlatformWindow*>(SCREEN_MANAGER->getWindowAt(pos));
    int screenId = SCREEN_MANAGER->getScreenIdAt(pos);
    QPoint localPos = SCREEN_MANAGER->mapFromGlobal(screenId, pos);
    if (QGuiApplication::layoutDirection() == Qt::RightToLeft) {
        int newLocalX = SCREEN_MANAGER->getScreenSize(screenId).isValid()? SCREEN_MANAGER->getScreenSize(screenId).width() - localPos.x() : localPos.x();
        localPos.setX(newLocalX);
    }
    //鼠标坐标点转化成网格标点；
    QPoint tmpPos = window->getGridPosFromMousePos(localPos);
    if (window->isFull()) {
        QList<QQuickView*> list = SCREEN_MANAGER->getBackgroundWindows();
        for (int i = 0 ; i < list.count(); i++) {
            PlatformWindow *window = qobject_cast<PlatformWindow*>(list[i]);
            if (!window->isFull()) {
                m_markMousePressPos = QPoint(0, 0);
                m_markMousePressId = window->id();
                return;
            }
        }
        m_markMousePressPos = QPoint(0, 0);
        m_markMousePressId = window->id();
    } else {
        m_markMousePressPos = window->checkGridPos(tmpPos);
        m_markMousePressId = window->id();
    }
}

void DesktopWindowManager::createIdAndPos(int *id, QPoint *pos)
{
    if (m_markMousePressPos == QPoint(-1, -1)) {
        PlatformWindow *window =qobject_cast<PlatformWindow*>(SCREEN_MANAGER->getWindowAt(QCursor::pos()));
        bool isFull = window? window->isFull() : true;
        if (!isFull) {
            *id = window->id();
            *pos = window->findNextEmptyGridPos();
            return;
        }

        QList<QQuickView*> list = SCREEN_MANAGER->getBackgroundWindows();
        for (int i = 0 ; i < list.count(); i++) {
            PlatformWindow *emptyPosWindow = qobject_cast<PlatformWindow*>(list[i]);
            isFull = emptyPosWindow? emptyPosWindow->isFull() : true;
            if (!isFull) {
                *id = emptyPosWindow->id();
                *pos = emptyPosWindow->findNextEmptyGridPos();
                return;
            }
        }
        *id = window->id();
        *pos = QPoint(-1, -1);
        return;
    }
    PlatformWindow *window =qobject_cast<PlatformWindow*>(SCREEN_MANAGER->getWindowById(m_markMousePressId));
    *id = m_markMousePressId;
    *pos = m_desktopAutoLayout? window->findNextEmptyGridPos(): m_markMousePressPos;//自动排列的情况下清空位置的记录
    m_markMousePressPos = QPoint(-1, -1);
    m_markMousePressId = 0;
    return;
}

void DesktopWindowManager::setInfoPos(const QString &uri)
{
    auto info = FileInfo::fromUri(uri);
    QPoint currentPos;
    int id;
    createIdAndPos(&id, &currentPos);
    info->setProperty("createId", id);
    info->setProperty("createPos", currentPos);
    qDebug() << "Make the coordinate points of the newly creted file : "<<id<<currentPos ;
}

void DesktopWindowManager::fileCreated(int id, const QString &uri)
{
    auto index = m_model->indexFromUri(uri);
    QPoint metaPos = m_model->data(index,PositionRole).toPoint();
    bool isRenaming =  qApp->property("rename").toBool() && metaPos.x() >= 0 && metaPos.x() >= 0;
    if (isRenaming) {
        qApp->setProperty("rename", false);
    }

    bool isHidden = false;
    auto info = FileInfo::fromUri(uri);
    if (!GlobalSettings::getInstance()->getValue(SHOW_HIDDEN_PREFERENCE).toBool()) {
        auto metainfo = FileMetaInfo::fromUri(uri);
        if (metainfo->hasHiddenInfo()) {
            isHidden = metainfo->isHidden();
        } else {
            isHidden = info->isHiddenFile();
        }
    }

    if (isHidden) return;

    if (isRenaming) {
        int screenId = m_model->data(index, ScreenIdRole).toInt();
        Q_EMIT m_model->idChanged(screenId, -1,  QStringList(uri));
    } else {
        int oldId = m_model->data(index, ScreenIdRole).toInt();
        id = SCREEN_MANAGER->displayMode() == ScreenManager::Mirror? 0 : id;
        m_model->setData(index, id, ScreenIdRole);
        Q_EMIT m_model->idChanged(id, oldId, QStringList(uri));
    }
}

void DesktopWindowManager::clearAllRestoreInfo()
{
    for (int i = 0; i < m_model->rowCount(); i++) {
        auto index = m_model->index(i, 0);
        m_model->setData(index, QStringList(""), ExceptedPositionRole);
    }
}

void DesktopWindowManager::initializeWithScreens()
{
    for (auto screen : SCREEN_MANAGER->allScreens()) {
        addQmlDesktop(screen);
    }

    connect(SCREEN_MANAGER, &ScreenManager::screenAdded, this, [this](QScreen* screen, int id) {
        this->addQmlDesktop(screen);
    });

    connect(SCREEN_MANAGER, &ScreenManager::primaryScreenChanged, this, &DesktopWindowManager::relocateIconView);

/*    connect(SCREEN_MANAGER, &ScreenManager::singleScreenModeActivated, this, &DesktopWindowManager::singleScreenMode);
    connect(SCREEN_MANAGER, &ScreenManager::multiScreenModeActivated, this, &DesktopWindowManager::multiscreenMode)*/;
}

bool DesktopWindowManager::getQuickStartAvailable()
{
    return isQuickStartAvailable;
}

void DesktopWindowManager::initializeWindowView(QQuickView* window)
{
    if (!window) return;

    if (m_initMetaInfo) {
        switchDisplayMode();
        relocateIconView();
    } else if (window->screen() == SCREEN_MANAGER->primaryScreen()) {
        m_model->refresh();
    }
}

void DesktopWindowManager::setupWindowConnections(QQuickView* window)
{
    if (!window) return;

    QScreen* screen = qobject_cast<PlatformWindow*>(window)->bindScreen();

    connect(SCREEN_MANAGER, &ScreenManager::screenRemoved, window, [=](QScreen* removedScreen, int id) {
        qDebug() << "removedScreen "<<id<<"primay screen geometry:"<<SCREEN_MANAGER->primaryScreen()->geometry();
        if (screen != removedScreen) return;
        int currentMode = SCREEN_MANAGER->displayMode();
        if (currentMode == ScreenManager::Extend) {
            m_model->saveExtendItemInfo(id);
            PlatformWindow *window =qobject_cast<PlatformWindow*>(SCREEN_MANAGER->getWindowById(0));
            window->updateView();
        }

        //SCREEN_MANAGER会发送显示模式切换信号
        PlatformWindow *p =qobject_cast<PlatformWindow*>(window);
        p->destoryScreen();

        window->disconnect();
        SCREEN_MANAGER->removeBackgroundWindow(window);
        delete window;
    });
}

void DesktopWindowManager::initPeonyDbusService()
{
    m_peonyDbusService = new PeonyDbusService;
    m_peonyDbusService->DbusServerRegister();
}

void DesktopWindowManager::switchDisplayMode()
{
    int newMode = SCREEN_MANAGER->displayMode();
    qDebug()<<"switch display mode" <<newMode;

    if (newMode == ScreenManager::Extend) {
        multiscreenMode();
    } else {
        singleScreenMode();
    }
}

void DesktopWindowManager::startInitAnima()
{
       if(!hasUnlockAnima){
           Q_EMIT startInitAnimaRequest();
           hasUnlockAnima = true;
       }
}

bool DesktopWindowManager::getLockState()
{
    QDBusReply<bool> reply = m_dbusInterface->call("GetLockState");
    return reply.value();
}
