/*
 *
 *  * Copyright (C) 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/>.
 *  *
 *  * Authors:  Zhang <zhangyuanyuan1@kylinos.cn>
 *
 */

#include "windowThumbnailView.h"

#include <QQmlContext>
#include <QQmlEngine>
#include <KWindowSystem>
#include <KWindowInfo>
#include <KWindowEffects>
#include <QApplication>
#include <QScreen>
#include <QX11Info>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QAction>
#include <QMenu>
#include <QDebug>
#include <QPainterPath>
#include "../panel/common/common.h"

#define NORMAL_WIDTH 272
#define NORMAL_HEIGHT 192
#define TITLE_HEIGHT 50
#define LIST_WIDTH 220
#define LIST_HEIGHT 40
#define LIST_SPACING 8
#define MARGIN 8

ThumbnailView::ThumbnailView(QWindow *parent)
    :QQuickView(parent)
{
    qRegisterMetaType<QVector<QSize> >("QVector<QSize>");

    setResizeMode(QQuickView::SizeViewToRootObject);
    setColor(Qt::transparent);
    setFlags(Qt::FramelessWindowHint | Qt::Popup);

    engine()->rootContext()->setContextProperty("wThumbnailView", this);
    engine()->rootContext()->setContextProperty("thumbnailModel", ThumbnailModel::instance());

    setSource(QUrl("qrc:/qml/thumbnailView.qml"));

    connect(ThumbnailModel::instance(), &ThumbnailModel::updateWinIdList, this, &ThumbnailView::setViewModel);
    //预览图监听主题圆角
    const QByteArray styleId(ORG_UKUI_STYLE);
    if (QGSettings::isSchemaInstalled(styleId)) {
        m_styleGsettings.reset(new QGSettings(styleId));
        if (m_styleGsettings->keys().contains(WINDOW_RADIUS)) {
            m_windowRadius = m_styleGsettings->get(WINDOW_RADIUS).toInt();
            connect(m_styleGsettings.get(), &QGSettings::changed, this, [=] (const QString &key) {
                if (key == WINDOW_RADIUS) {
                    int radius = m_styleGsettings->get(WINDOW_RADIUS).toInt();
                    setRadius(radius);
                }
            });
        }
    }
}

bool ThumbnailView::event(QEvent *event)
{
    switch (event->type()) {
    case QEvent::Expose: {
        if (isExposed()) {
            kdk::WindowManager::setSkipTaskBar(this, true);
            kdk::WindowManager::setSkipSwitcher(this, true);
            kdk::WindowManager::setGeometry(this, QRect(m_viewPoint, QSize(width(), height())));
        }
        break;
    }
    case QEvent::MouseButtonPress: {
            if (m_menuVisible) {
                setMenuVisible(false);
                return true;
            }
            break;
        }
    default:
        break;
    }
    return QQuickWindow::event(event);
}

bool ThumbnailView::viewVisible() const
{
    return m_viewShow;
}

void ThumbnailView::setViewVisible(const bool &viewVisible)
{
    m_viewShow = viewVisible;

    if (!m_menuVisible && !m_viewShow && !geometry().contains(QCursor::pos())) {
        KWindowEffects::enableBlurBehind(this->winId(), false, QRegion());
        hide();
        sendCloseSigToKWin();
        m_viewModel.clear();
        setViewModel(m_viewModel);  //clear qml listview.model
    } else {
        updataWindowRegion();
        KWindowEffects::enableBlurBehind(this->winId(), true, m_windowRegion);
        show();
    }
    Q_EMIT viewVisibleChanged(viewVisible);
}

bool ThumbnailView::menuVisible() const
{
    return m_menuVisible;
}

void ThumbnailView::setMenuVisible(const bool &menuVisible)
{
    m_menuVisible =  menuVisible;
}

QList<QVariant> ThumbnailView::viewModel() const
{
    return m_viewModel;
}

bool ThumbnailView::isShowByList()
{
    return m_isShowList;
}

void ThumbnailView::setShowByList(bool state)
{
    m_isShowList = state;
    Q_EMIT showByListChanged(m_isShowList);
}

bool ThumbnailView::isShowHorizontalView()
{
    return m_isHorizontal;
}

void ThumbnailView::setShowHorizontalView(bool state)
{
    m_isHorizontal = state;
    Q_EMIT showHorizontalViewChanged(m_isHorizontal);
}

int ThumbnailView::radius()
{
    return m_windowRadius;
}

void ThumbnailView::setRadius(int radius)
{
    m_windowRadius = m_isShowList ? (radius < 8 ? radius : 8) : radius;
    Q_EMIT radiusChanged(m_windowRadius);
}

QSize ThumbnailView::windowSize(const int &index)
{
    QVector<QSize> windowsSize = calculateWindowsSize(m_viewModel);
    updateViewMode();

    if (windowsSize.count() == 0) {
        return {QSize(1, 1)};
    }
    return windowsSize[index];
}

void ThumbnailView::updateMprisWindowSize(const int index, int width, int height)
{
    m_mprisWinSize = m_winSizeVector;
    if (index >= 0 && index < m_mprisWinSize.size()) {
        m_mprisWinSize.replace(index, QSize(width, height));
        m_mprisFlag = true;
    }
}

void ThumbnailView::sendSigToKwin(const QVariant &winId)
{
    QDBusMessage message = QDBusMessage::createSignal("/", "com.ukui.kwin", "panelUpdateLayer");
    QList<QVariant> args;
    quint32 intWid = winId.toUInt();
    args.append(intWid);
    message.setArguments(args);
    QDBusConnection::sessionBus().send(message);
}

void ThumbnailView::sendCloseSigToKWin()
{
    QDBusMessage message = QDBusMessage::createSignal("/", "com.ukui.kwin", "panelNotUpdateLayer");
    QList<QVariant> args;
    bool flag = true;
    args.append(flag);
    message.setArguments(args);
    QDBusConnection::sessionBus().send(message);
}

QVector<QSize> ThumbnailView::calculateWindowsSize(const QList<QVariant> &model)
{
    m_totalLenth = 0;
    QVector<QSize> windowsSize;

    for (const QVariant win : model) {
        KWindowInfo info(win.toUInt(), NET::WMGeometry | NET::WMName);
        int appwidth = info.geometry().width();
        int appheight =info.geometry().height();  //不包含标题-TITLE_HEIGHT
        if (appwidth <= 0 || appheight <= 0) {
            continue;
        }

        int tmpWidth = 0;
        int tmpHeight = 0;
        if (isShowHorizontalView()) {
            tmpWidth = (float)appwidth / (float)appheight * (NORMAL_HEIGHT - TITLE_HEIGHT);
            if (tmpWidth > NORMAL_WIDTH) {
                tmpWidth = NORMAL_WIDTH;
            }
            tmpHeight = NORMAL_HEIGHT;
            m_totalLenth += tmpWidth;
        } else {
            tmpWidth = NORMAL_WIDTH;
            tmpHeight = (float)appheight / (float)appwidth * NORMAL_WIDTH + TITLE_HEIGHT;
            if (tmpHeight > NORMAL_HEIGHT) {
                tmpHeight = NORMAL_HEIGHT;
            }
            m_totalLenth += tmpHeight;
        }
        windowsSize.append(QSize(tmpWidth, tmpHeight));
    }
    m_winSizeVector = windowsSize;
    return windowsSize;
}

void ThumbnailView::updateViewMode()
{
    QRect rect = QApplication::primaryScreen()->geometry();
    if (isShowHorizontalView()) {
        if (m_totalLenth > rect.width()) {
            //to do 窗口大小自适应
            setShowByList(true);
            m_totalLenth = m_viewModel.count() * (LIST_HEIGHT + LIST_SPACING) - LIST_SPACING;
            m_totalLenth = qMin(m_totalLenth, rect.height() - MARGIN * 2 - m_panelSize);
        } else {
            setShowByList(false);
        }
    } else {
        if (m_totalLenth > rect.height()) {
            //to do 窗口大小自适应
            setShowByList(true);
            m_totalLenth = m_viewModel.count() * (LIST_HEIGHT + LIST_SPACING) - LIST_SPACING;
            m_totalLenth = qMin(m_totalLenth, rect.height() - MARGIN * 2);
        } else {
            setShowByList(false);
        }
    }
}

void ThumbnailView::setViewModel(const QList<QVariant> &model)
{
    m_viewModel.clear();
    if (model.isEmpty()) {
        Q_EMIT viewModelChanged(m_viewModel);
        return;
    }
    m_viewModel = model;

    Q_EMIT viewModelChanged(m_viewModel);
}

void ThumbnailView::setViewPosition(PanelPositionType position, int panelSize, int x, int y)
{
    if (viewModel().isEmpty()) {
        return;
    }
    m_panelSize = panelSize;
    calculateWindowsSize(m_viewModel);
    updateViewMode();

    int margin = 8;
    QRect rect = QApplication::primaryScreen()->geometry();

    int viewWidth = m_isShowList ? LIST_WIDTH : m_totalLenth;
    int viewHeight;
    switch (position) {
    case PanelPositionType::Top:
        x = x - (float)viewWidth / 2;  //m_totalLenth == this->width()
        x = x < margin ? margin : x;
        y = y + margin + panelSize;
        break;
    case PanelPositionType::Bottom:
        viewHeight = m_isShowList ? m_totalLenth : NORMAL_HEIGHT;
        x = x - (float)viewWidth / 2;
        x = x < margin ? margin : x;
        y = rect.height() - margin - panelSize - viewHeight;
        break;
    case PanelPositionType::Left:
        x = x + margin + panelSize;
        y = y - (float)m_totalLenth / 2;  //this->height()返回的不是view整体高度，所以用m_totalLenth
        y = y < margin ? margin : y;
        break;
    case PanelPositionType::Right:
        viewWidth = m_isShowList ? LIST_WIDTH : NORMAL_WIDTH;
        x = rect.width() - margin - panelSize - viewWidth;
        y = y - (float)m_totalLenth / 2;
        y = y < margin ? margin : y;
        break;
    default:
        break;
    }

    m_viewPoint = QPoint(x, y);
}

void ThumbnailView::updataWindowRegion()
{
    m_windowRegion = QRegion();
    if (viewModel().isEmpty()) {
        return;
    }

    if (isShowByList()) {
        int radius = 6;
        int nextP = 0;
        int spacing = 8;
        QRegion windowRegion;
        for (int index = 0; index < viewModel().count(); ++index) {
            QPainterPath path;
            path.addRoundedRect(1, 1, LIST_WIDTH - 2, LIST_HEIGHT - 2, radius, radius);
            QRegion childRegion = QRegion(path.toFillPolygon().toPolygon());
            childRegion.translate(0, nextP);
            nextP = nextP + LIST_HEIGHT + spacing;
            windowRegion = windowRegion.united(childRegion);
        }
        m_windowRegion = windowRegion;
    } else {
        int radius = 11;
        int nextP = 0;
        int spacing = 8;
        QRegion windowRegion;
        QSize win;
        if (m_mprisFlag) {
            m_winSizeVector = m_mprisWinSize;
            m_mprisFlag = false;
            m_mprisWinSize.clear();
        }
        for (int index = 0; index < m_winSizeVector.count(); ++index) {
            win = m_winSizeVector.at(index);
            QPainterPath path;
            path.addRoundedRect(1, 1, win.width() - 2, win.height() - 2, radius, radius);
            QRegion childRegion = QRegion(path.toFillPolygon().toPolygon());
            if (isShowHorizontalView()) {
                childRegion.translate(nextP, 0);
                nextP = nextP + win.width() + spacing;
            } else {
                childRegion.translate(0, nextP);
                nextP = nextP + win.height() + spacing;
            }
            windowRegion = windowRegion.united(childRegion);
        }
        m_windowRegion = windowRegion;
    }
}

void ThumbnailView::openMenu(const QVariant &winId)
{
    QMenu *menu = new QMenu();
    menu->setAttribute(Qt::WA_DeleteOnClose);
    QList<QAction*> actionList;
    kdk::WindowInfo info = kdk::WindowManager::getwindowInfo(winId);

    //关闭窗口
    QAction *closeAction = new QAction(QIcon::fromTheme("window-close-symbolic"), tr("Close"), menu);
    closeAction->setEnabled(true);
    connect(closeAction, &QAction::triggered, this, [=] {
        kdk::WindowManager::closeWindow(winId);
        ThumbnailModel::instance()->onWindowRemoved(winId);
    });

    //恢复窗口
    QAction *restoreAction = new QAction(QIcon::fromTheme("window-restore-symbolic"), tr("Restore"));
    restoreAction->setEnabled(info.isMaximized() || info.isMinimized());
    connect(restoreAction, &QAction::triggered, this, [=] {
        KWindowSystem::clearState(winId.toUInt(), NET::Max);
        kdk::WindowManager::activateWindow(winId);
    });

    //最大化窗口
    QAction *maximizeAction = new QAction(QIcon::fromTheme("window-maximize-symbolic"), tr("Maximize"));
    maximizeAction->setEnabled(info.isMaximizable() && !info.isMaximized());
    connect(maximizeAction, &QAction::triggered, this, [=] {
        //kdk::WindowManager::maximizeWindow(winId); //不生效
        KWindowSystem::setState(winId.toUInt(), NET::Max);
        kdk::WindowManager::activateWindow(winId);
    });

    //最小化窗口
    QAction *minimizeAction = new QAction(QIcon::fromTheme("window-minimize-symbolic"), tr("Minimize"));
    minimizeAction->setEnabled(!info.isMinimized());
    connect(minimizeAction, &QAction::triggered, this, [=] {
        kdk::WindowManager::minimizeWindow(winId);
    });

    //置顶窗口
    QAction *keepAboveAction = new QAction(QIcon::fromTheme("ukui-fixed-symbolic"), tr("Keep above"));
    keepAboveAction->setEnabled(!info.isKeepAbove());
    connect(keepAboveAction, &QAction::triggered, this, [=] {
        if (!info.isActive()) {
            kdk::WindowManager::activateWindow(winId);
        }
        kdk::WindowManager::keepWindowAbove(winId);
    });

    //取消置顶窗口
    QAction *UnsetKeepAboveAction = new QAction(QIcon::fromTheme("ukui-unfixed-symbolic"), tr("Unset keep above"));
    UnsetKeepAboveAction->setEnabled(info.isKeepAbove());
    connect(UnsetKeepAboveAction, &QAction::triggered, this, [=] {
        KWindowSystem::clearState(winId.toUInt(), NET::KeepAbove);
    });

    actionList << closeAction
               << restoreAction
               << maximizeAction
               << minimizeAction
               << keepAboveAction
               << UnsetKeepAboveAction;

    menu->addActions(actionList);
    menu->popup(QCursor::pos());
    setMenuVisible(true);

    connect(menu, &QMenu::aboutToHide, this, [=]() {
        menu->close();
        setMenuVisible(false);
        setViewVisible(geometry().contains(QCursor::pos()));
    });
}
