/*
 *
 *  * 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: Nicole <buxiaoqing@kylinos.cn>
 *
 */

#include "ukuitaskgroup.h"
#include "../panel/common/common.h"
#include <QDBusVariant>
#include <QToolTip>
#include "../panel/xdg-desktop-file/xdgdesktopfile.h"


#define SMALL_PANEL_SIZE   46
#define MIDDLE_PANEL_SIZE  70
#define LARGE_PANEL_SIZE   92

UKUITaskGroup::UKUITaskGroup(const QString &groupName, const QString & desktopFileName, QWidget *parent) :
    QWidget(parent),
    m_timer(new QTimer(this)),
    m_groupName(groupName),
    m_desktopFileName(desktopFileName),
    m_parent(parent)
{
    this->setObjectName("UKUITaskGroup-" + desktopFileName);
    m_layout.reset(new UKUi::GridLayout(this));
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    realign();

    QDBusConnection::sessionBus().connect(QString("org.ukui.panel.daemon"), QString("/panel/cornermark"),
                                          "org.ukui.panel.daemon", "appsCornerMarkChanged",
                                          this, SLOT(appsCornerMarkChangedSlot(QString, int)));
    const QByteArray id(PANEL_SETTINGS);
    if (QGSettings::isSchemaInstalled(id)) {
        m_gsettings.reset(new QGSettings(id));
        m_gsettingKeys = m_gsettings->keys();
        if (m_gsettingKeys.contains(GROUPING_ENABLE))
            m_isGrouping = m_gsettings->get(GROUPING_ENABLE).toBool();
        if (m_gsettingKeys.contains(PANEL_POSITION_KEY))
            m_panelPosition = m_gsettings->get(PANEL_POSITION_KEY).toInt();
        if (m_gsettingKeys.contains(PANEL_SIZE_KEY))
            m_panelSize = m_gsettings->get(PANEL_SIZE_KEY).toInt();
        if (m_gsettingKeys.contains(TASKBAR_BTN_SPAN))
            m_buttonsSpan = m_gsettings->get((TASKBAR_BTN_SPAN)).toInt();
        connect(m_gsettings.get(), &QGSettings::changed, this, [&] (const QString &key) {
            if (key == TASKBAR_BTN_SPAN) {
                m_buttonsSpan = m_gsettings->get((TASKBAR_BTN_SPAN)).toInt();
                changeButtonsSize();
            }
            if (key == GROUPING_ENABLE) {
                m_isGrouping = m_gsettings->get(GROUPING_ENABLE).toBool();
                onExpandModeChanged(m_isGrouping);
            }
            if (key == PANEL_POSITION_KEY) {
                m_panelPosition = m_gsettings->get(PANEL_POSITION_KEY).toInt();
                for (WindowId window : qAsConst(m_currentDesktopWindowIdList)) {
                    setButtonsStyle(m_buttonsMap.value(window));
                }
                changeButtonsSize();
            }
            if (key == PANEL_SIZE_KEY) {
                m_panelSize = m_gsettings->get(PANEL_SIZE_KEY).toInt();
                changeButtonsSize();
                setCornerMarkSize();
            }
        });
    }

    m_timer->setTimerType(Qt::PreciseTimer);
    connect(m_timer, &QTimer::timeout, this, &UKUITaskGroup::timeToEmit);
}

UKUITaskGroup::~UKUITaskGroup()
{
    if (m_gsettings) {
        m_gsettings.reset(nullptr);
    }
}

QString UKUITaskGroup::getGroupName()
{
    return m_groupName;
}

QString UKUITaskGroup::getDesktopFileName()
{
    return m_desktopFileName;
}

void UKUITaskGroup::setDesktopFileName(QString desktopFileName)
{
    m_desktopFileName = desktopFileName;
}

QMap<WindowId, std::shared_ptr<UKUITaskButton> > UKUITaskGroup::getButtonsInfo()
{
    return m_buttonsMap;
}

bool UKUITaskGroup::isPinned()
{
    return m_isPinned;
}

void UKUITaskGroup::addWindow(WindowId window)
{
    if (m_groupName.isEmpty()) {
        m_groupName = kdk::WindowManager::getWindowGroup(window);
    }
    if (m_buttonsMap.contains(window)) {
        return;
    }
    std::shared_ptr<UKUITaskButton> btn(new UKUITaskButton(window, ""));
    connect(btn.get(), &UKUITaskButton::pinToTaskbar, this, [&](){ emit pinToTaskbarSignal(m_desktopFileName);});
    connect(btn.get(), &UKUITaskButton::unPinFromTaskbar, this, [&](){ emit unpinFromTaskbarSignal(m_desktopFileName);});
    connect(btn.get(), &UKUITaskButton::closeGroup, this, &UKUITaskGroup::closeAllWindowInGroup);
    connect(btn.get(), &UKUITaskButton::enterButton, this, [=](QList<WindowId> winIdList, QString groupName, int x, int y){
        emit enterGroup(winIdList, groupName, x, y);
    });
    connect(btn.get(), &UKUITaskButton::leaveButton, this, [=](QList<WindowId> winIdList, QString groupName, int x, int y){
        emit leaveGroup(groupName);
    });

    btn->setDesktopFileName(m_desktopFileName);
    qDebug() << btn->desktopFileName();
    btn->onButtonsStatusChanged(m_isPinned);
    m_buttonsMap.insert(window, btn);
    if (btn->isOnCurrentDesktop()) {
        m_currentDesktopWindowIdList.append(window);
        this->setVisible(true);
    }
    m_layout->addWidget(btn.get());
    realign();
    changeButtonsCount();
    refreshButtonsVisible();
    setButtonsStyle(btn);
    changeButtonsSize();
}

void UKUITaskGroup::removeWindow(WindowId window)
{
    if (m_buttonsMap.keys().contains(window)) {
        std::shared_ptr<UKUITaskButton> btn = m_buttonsMap.value(window);
        m_layout->removeWidget(btn.get());
        m_buttonsMap.remove(window);
        if (m_currentDesktopWindowIdList.contains(window))
            m_currentDesktopWindowIdList.removeAll(window);
        btn.reset();
    }
    changeButtonsCount();
    calculGroupSize();
    //固定且所有打开窗口已关闭，则显示出固定按钮
    if (isOnlyPinned()) {
        m_buttonsMap.begin().value()->setVisible(true);
    } else if (m_currentDesktopWindowIdList.isEmpty()) {
        this->setVisible(false);
    }
}

void UKUITaskGroup::changeButtonsCount()
{
    for (WindowId window : qAsConst(m_currentDesktopWindowIdList)) {
        m_buttonsMap.value(window)->onButtonsCountChanged(m_currentDesktopWindowIdList.size());
    }
    if (isOnlyPinned()) {
        m_buttonsMap.begin().value()->onButtonsCountChanged(0);
    }
}

bool UKUITaskGroup::isHorizontalPanel()
{
    return m_panelPosition == PanelPosition::Bottom || m_panelPosition == PanelPosition::Top;
}

bool UKUITaskGroup::isOnlyPinned()
{
    return m_isPinned && m_currentDesktopWindowIdList.isEmpty();
}

void UKUITaskGroup::refreshButtonsVisible()
{
    //只固定无打开，保持不变；
    if (isOnlyPinned()) {
        return;
    }

    if (m_isGrouping) {
        //合并且打开，仅显示当前桌面的Leader Window;
        for (QMap<WindowId, std::shared_ptr<UKUITaskButton>>::const_iterator i = m_buttonsMap.begin(); i != m_buttonsMap.end(); i++) {
            std::shared_ptr<UKUITaskButton> btn = i.value();
            btn->setVisible(/*btn->isLeaderWindow() &&*/ btn->isOnCurrentDesktop());
        }
    } else {
        //展开固定且打开，隐藏固定按钮，其余全显示
        if (m_isPinned) {
            m_buttonsMap.begin().value()->setVisible(false);
        }
        for (WindowId window : qAsConst(m_currentDesktopWindowIdList)) {
            m_buttonsMap.value(window)->setVisible(true);
        }
    }
}

void UKUITaskGroup::setButtonsStyle(std::shared_ptr<UKUITaskButton> btn)
{
    if (m_isGrouping) {
        btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
    } else {
        btn->setVisible(btn->isOnCurrentDesktop());
        if (isHorizontalPanel()) {
            btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
            btn->updateCaption();
        } else {
            btn->setToolButtonStyle(Qt::ToolButtonIconOnly);
        }
    }
    btn->repaint();
}

void UKUITaskGroup::pinToTaskbar(QString desktopFileName)
{
    if (m_isPinned) {
        qDebug() << "This App has pinned on taskbar!";
        return;
    }

    std::shared_ptr<UKUITaskButton> pinBtn(new UKUITaskButton(0, desktopFileName));
    connect(pinBtn.get(), &UKUITaskButton::pinToTaskbar, this, [&](){ emit pinToTaskbarSignal(m_desktopFileName);});
    connect(pinBtn.get(), &UKUITaskButton::unPinFromTaskbar, this, [&](){ emit unpinFromTaskbarSignal(m_desktopFileName);});
    connect(pinBtn.get(), &UKUITaskButton::clicked, this, [&](){
        if (pinBtn)
            pinBtn->execAction();
    });

    //固定按钮始终存储在QMap中的第一位
    m_buttonsMap.insert(m_buttonsMap.begin(), 0, pinBtn);
    m_layout->addWidget(pinBtn.get());
    pinBtn->setToolButtonStyle(Qt::ToolButtonIconOnly);
    pinBtn->setVisible(m_currentDesktopWindowIdList.size() == 0);
    pinBtn->quickLaunchAction();
    m_isPinned = true;
    changeButtonsStatus();
    onCurrentDesktopChanged();
}

void UKUITaskGroup::unpinFromTaskbar(QString desktopFileName)
{
    if (!m_isPinned) {
        qDebug() << "This App has NOT pinned!";
        return;
    }
    if (m_buttonsMap.begin().value()->desktopFileName() == desktopFileName && m_buttonsMap.keys().contains(0)) {
        std::shared_ptr<UKUITaskButton> pinBtn = m_buttonsMap.value(0);
        m_layout->removeWidget(pinBtn.get());
        m_buttonsMap.remove(0);
        m_isPinned = false;
    }
    changeButtonsStatus();
    onCurrentDesktopChanged();
}

void UKUITaskGroup::closeAllWindowInGroup()
{
    for (WindowId window : qAsConst(m_currentDesktopWindowIdList)) {
        kdk::WindowManager::closeWindow(window);
    }
}

void UKUITaskGroup::appsCornerMarkChangedSlot(QString arg, int value)
{
    //将软件商店角标数据信息写入ini配置文件，便于重启后立即显示
    QString filename = QDir::homePath() + "/.config/ukui/panel.conf";
    QSettings *cornerSettings = new QSettings(filename, QSettings::IniFormat);
    cornerSettings->setIniCodec("utf-8");
    if (arg.contains("kylin-software-center")) {
        qDebug() << "[Panel] write kylin-software-center corner mark data";
        cornerSettings->beginGroup("CornerMark");
        cornerSettings->setValue("desktop", arg);
        cornerSettings->setValue("value", value);
        cornerSettings->endGroup();
    }
    cornerSettings->sync();

    if(m_desktopFileName == arg) {
        if(!m_isHaveCornerMark) {
            m_badge = new KBadge(this);
        }
        m_badge->setColor(Qt::red);
        m_badge->setValue(value);
        m_badge->setVisible(true);
        m_isHaveCornerMark = true;
        setCornerMarkSize();
    }
}

void UKUITaskGroup::setCornerMarkSize()
{
    if (!m_isHaveCornerMark || m_badge == nullptr) {
        return;
    }

    if(m_panelSize <= SMALL_PANEL_SIZE) {
        m_badge->setFontSize(8);
        m_badge->repaint();
        m_badge->move((m_panelSize - m_badge->width() - 4), 4);

    } else if (SMALL_PANEL_SIZE < m_panelSize && m_panelSize <= MIDDLE_PANEL_SIZE) {
        m_badge->setFontSize(12);
        m_badge->repaint();
        m_badge->move((m_panelSize - m_badge->width() - 4), 4);

    } else if (MIDDLE_PANEL_SIZE < m_panelSize && m_panelSize <= LARGE_PANEL_SIZE) {
        m_badge->setFontSize(16);
        m_badge->repaint();
        m_badge->move((m_panelSize - m_badge->width() - 4), 4);
    }
    m_badge->raise();
}

void UKUITaskGroup::changeButtonsStatus()
{
    for (QMap<WindowId, std::shared_ptr<UKUITaskButton>>::const_iterator i = m_buttonsMap.begin(); i != m_buttonsMap.end(); i++) {
        std::shared_ptr<UKUITaskButton> btn = i.value();
        btn->onButtonsStatusChanged(m_isPinned);
    }
}

void UKUITaskGroup::onExpandModeChanged(bool isGrouping)
{
    calculGroupSize();
    realign();
    refreshButtonsVisible();
    for (WindowId window : qAsConst(m_currentDesktopWindowIdList)) {
        setButtonsStyle(m_buttonsMap.value(window));
    }
    changeButtonsSize();
}

void UKUITaskGroup::calculGroupSize()
{
    int openWindowsCount = m_currentDesktopWindowIdList.size();
    if (m_isGrouping) {
        this->setFixedSize(m_panelSize, m_panelSize);
    } else {
        if (isHorizontalPanel()) {
            if (m_isPinned && openWindowsCount == 0) {
                this->setFixedSize(m_panelSize, m_panelSize);
            } else {
                this->setFixedSize(m_panelSize * m_buttonsSpan * openWindowsCount, m_panelSize);
            }
        } else {
            if (m_isPinned && openWindowsCount == 0) {
                this->setFixedSize(m_panelSize, m_panelSize);
            } else {
                this->setFixedSize(m_panelSize, m_panelSize * openWindowsCount);
            }
        }
    }
}

void UKUITaskGroup::changeButtonsSize()
{
    if (m_isPinned) {
        m_buttonsMap.begin().value()->updateIcon();
        m_buttonsMap.begin().value()->setFixedSize(m_panelSize, m_panelSize);
    }

    for (WindowId window : qAsConst(m_currentDesktopWindowIdList)) {
        m_buttonsMap.value(window)->updateIcon();
        if (m_isGrouping) {
            m_buttonsMap.value(window)->setFixedSize(m_panelSize, m_panelSize);
        } else {
            if (isHorizontalPanel()) {
                m_buttonsMap.value(window)->setFixedSize(m_panelSize * m_buttonsSpan, m_panelSize);
                m_buttonsMap.value(window)->updateCaption();
            } else {
                m_buttonsMap.value(window)->setFixedSize(m_panelSize, m_panelSize);
            }
        }
    }

    int size = m_layout->count();
    for (int j = 0; j < size; ++j) {
        UKUITaskButton *btn = qobject_cast<UKUITaskButton*>(m_layout->itemAt(j)->widget());
    }
}

void UKUITaskGroup::realign()
{
    calculGroupSize();
    QSize btnSize = QSize(m_panelSize, m_panelSize);
    if (isHorizontalPanel()) {
        m_layout->setRowCount(1);
        m_layout->setColumnCount(0);

    } else {
        m_layout->setRowCount(0);
        m_layout->setColumnCount(1);

    }
    m_layout->setCellMinimumSize(btnSize);
    if (m_isGrouping) {
        m_layout->setCellMaximumSize(btnSize);
    } else {
        m_layout->setCellMaximumSize(btnSize * m_buttonsSpan);
    }
    for (WindowId window : qAsConst(m_currentDesktopWindowIdList)) {
        m_buttonsMap.value(window)->refreshIconGeometry();
    }

}

bool UKUITaskGroup::isHaveCornerMark()
{
    return m_isHaveCornerMark;
}

void UKUITaskGroup::setHaveCornerMark(bool isHave)
{
    m_isHaveCornerMark = isHave;
}

KBadge *UKUITaskGroup::getKbadge()
{
    return m_badge;
}

void UKUITaskGroup::newKbage()
{
    if (m_badge) {
        return;
    }
    m_badge = new KBadge(this);
}


void UKUITaskGroup::enterEvent(QEvent *e)
{
    m_taskGroupEvent = ENTEREVENT;

    if (m_currentDesktopWindowIdList.isEmpty() && !m_desktopFileName.isEmpty()) {
        XdgDesktopFile xdg;
        if (xdg.load(m_desktopFileName)) {
            QString nameStr(xdg.localizedValue("Name").toString());
            this->setToolTip(nameStr);
        }
    }

    if (m_isGrouping && !m_currentDesktopWindowIdList.isEmpty()) {
        qDebug() << "all windows' id in this group is :" << m_currentDesktopWindowIdList;
        this->setToolTip("");
        m_event = e;
        if (m_timer->isActive()) {
            m_timer->stop();
        }
        m_timer->start(400);
    } else {
        e->setAccepted(false);
        return;
    }
}

void UKUITaskGroup::leaveEvent(QEvent *e)
{
    m_taskGroupEvent = LEAVEEVENT;
    if (m_isGrouping && !m_currentDesktopWindowIdList.isEmpty()) {
        m_event = e;
        if (m_timer->isActive()) {
            m_timer->stop();
        }
        m_timer->start(400);
    } else {
        e->setAccepted(false);
        return;
    }
}

void UKUITaskGroup::timeToEmit()
{
    if (m_timer->isActive()) {
        m_timer->stop();
    }
    QPoint abs = mapToGlobal(QPoint(0, 0));

    switch (m_taskGroupEvent) {
    case ENTEREVENT:
        if (isHorizontalPanel()) {
            int groupCenterPositionX = abs.x() + this->width() / 2;
            emit enterGroup(m_currentDesktopWindowIdList, m_groupName, groupCenterPositionX, 0);
        } else {
            int groupCenterPositionY = abs.y() + this->height() / 2;
            emit enterGroup(m_currentDesktopWindowIdList, m_groupName, 0, groupCenterPositionY);
        }
        QWidget::enterEvent(m_event);
        break;

    case LEAVEEVENT:
        if (isHorizontalPanel()) {
            int groupCenterPositionX = abs.x() + this->width() / 2;
            emit leaveGroup(m_groupName);
        } else {
            int groupCenterPositionY = abs.y() + this->height() / 2;
            emit leaveGroup(m_groupName);
        }
        QWidget::leaveEvent(m_event);
        break;

    case OTHEREVENT:
    default:
        break;
    }
}

void UKUITaskGroup::onCurrentDesktopChanged()
{
    qDebug() << __func__ << kdk::WindowManager::currentDesktop();
    m_currentDesktopWindowIdList.clear();
    for (QMap<WindowId, std::shared_ptr<UKUITaskButton>>::const_iterator i = m_buttonsMap.begin(); i != m_buttonsMap.end(); i++) {
        if (i.value()->isOnCurrentDesktop()) {
            m_currentDesktopWindowIdList.append(i.key());
            i.value()->setVisible(true);
        } else {
            i.value()->setVisible(false);
        }
    }

    //非固定，当前桌面未打开，但是其他桌面有打开，需要隐藏当前桌面的group
    if (m_currentDesktopWindowIdList.isEmpty() && !m_isPinned) {
        this->setVisible(false);
    }
    //固定，当前桌面未打开，但是其他桌面有打开，需要只显示固定按钮
    if (m_currentDesktopWindowIdList.isEmpty() && m_isPinned) {
        this->setVisible(true);
        for (QMap<WindowId, std::shared_ptr<UKUITaskButton>>::const_iterator i = m_buttonsMap.begin(); i != m_buttonsMap.end(); i++) {
            if (i.key() == 0) {
                i.value()->setVisible(true);
            } else {
                i.value()->setVisible(false);
            }
        }
    }
    //固定，当前桌面有打开，需要隐藏固定按钮，只显示打开的按钮和样式
    if (!m_currentDesktopWindowIdList.isEmpty() && m_isPinned) {
        this->setVisible(true);
        m_buttonsMap.value(0)->setVisible(false);
    }
    //非固定，当前桌面打开，需要显示出group，否则button无法显示
    if(!m_currentDesktopWindowIdList.isEmpty() && !m_isPinned) {
        this->setVisible(true);
    }
    changeButtonsSize();
    setCornerMarkSize();
    for (WindowId window : qAsConst(m_currentDesktopWindowIdList)) {
        setButtonsStyle(m_buttonsMap.value(window));
    }
    realign();
}

void UKUITaskGroup::mousePressEvent(QMouseEvent *event)
{
    event->setAccepted(false);
}

