/*
 * 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: zy-yuan1 <zhangyuanyuan1@kylinos.cn>
 *
 */
#include "desktoplistmodel.h"

#include <algorithm>
#include <QMetaEnum>
#include <QGuiApplication>
#include <QScreen>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDebug>
#include "windowmanagerinterface.h"

DesktopListModel::DesktopListModel(QObject *parent) : QAbstractListModel(parent)
{
    connectSignals();
    init();
}

int DesktopListModel::rowCount(const QModelIndex &parent) const
{
    return m_desktopList.count();
}

QVariant DesktopListModel::data(const QModelIndex &index, int role) const
{
    int i = index.row();
    if (i < 0 || i >= m_desktopList.count()) {
        return QVariant();
    }

    Desktop desktop = m_desktopList.at(i);
    switch (static_cast<Role>(role)) {
    case Role::Id:
        return i + 1;
    case Role::Uuid:
            return desktop.uuid();
    case Role::CustomName:
        return desktop.customName();
    case Role::ItemType:
        return desktop.type() == Desktop::DesktopType::NewDesktop ? QString("newDesktop")
                                                                  : QString("workSpace");
    default:
        return {};
    }
}

bool DesktopListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid()) {
        return false;
    }

    if (role == Role::CustomName) {
        m_desktopList[index.row()].setCustomName(value.toString());
        Q_EMIT dataChanged(index, index, QVector<int>{Role::CustomName});
        WindowManagerInterface::getInstance()->setDesktopName(index.row() + 1, value.toString());
        WindowManagerInterface::getInstance()->saveDesktopNames(getDesktopNames());
        return true;
    } else if (role == Role::ItemType) {
        m_desktopList[index.row()].setDesktopType(Desktop::DesktopType(value.toInt()));
        Q_EMIT dataChanged(index, index, QVector<int>{Role::ItemType});
    }

    return false;
}

QHash<int, QByteArray> DesktopListModel::roleNames() const
{
    QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
    QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role"));

    for (int i = 0; i < e.keyCount(); ++i) {
        roles.insert(e.value(i), e.key(i));
    }
    return roles;
}

bool DesktopListModel::insertRows(int row, int count, const QModelIndex &parent)
{
    if (row < 0 || row > m_desktopList.count() || m_desktopList.count() > MAX_DESKTOP_COUNT) {
        return false;
    }

    beginInsertRows(parent, row, row);

    Desktop desktop;
    desktop.setDesktopType(Desktop::DesktopType::WorkSpace);
    m_desktopList.insert(m_desktopList.count() - 1, desktop);

    endInsertRows();

    return true;
}

bool DesktopListModel::removeRows(int row, int count, const QModelIndex &parent)
{
    if (!rowIsValid(row)) {
        return false;
    }

    beginRemoveRows(parent, row, row);
    m_desktopList.removeAt(row);
    endRemoveRows();
    return true;
}

bool DesktopListModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
                                const QModelIndex &destinationParent, int destinationChild)
{
    if (!rowIsValid(sourceRow) || !rowIsValid(destinationChild)) {
        return false;
    }
    // only support count == 1
    // 需要注意 beginMoveRows 单个节点移动时，上移差是1，下移差是2
    if (sourceRow < destinationChild) {
        // 右移/下移
        beginMoveRows(sourceParent,sourceRow, sourceRow, destinationParent, destinationChild + 1);
    } else {
        // 左移/上移
        beginMoveRows(sourceParent,sourceRow, sourceRow, destinationParent, destinationChild);
    }
    m_desktopList.move(sourceRow, destinationChild);
    endMoveRows();
    return true;
}

bool DesktopListModel::rowIsValid(int row) const
{
    return row >= 0 && row < m_desktopList.count();
}

void DesktopListModel::connectSignals()
{
    QObject::connect(WindowManagerInterface::getInstance(), &WindowManagerInterface::desktopAppended,
                     this, &DesktopListModel::onDesktopAppend);
    QObject::connect(WindowManagerInterface::getInstance(), &WindowManagerInterface::desktopRemoved,
                     this, &DesktopListModel::onDesktopRemoved);
    QObject::connect(WindowManagerInterface::getInstance(), &WindowManagerInterface::desktopMoved,
                     this, &DesktopListModel::onDesktopMoved);
}

void DesktopListModel::init()
{
    beginResetModel();
    m_desktopList.clear();

    const auto desktopCount = WindowManagerInterface::getInstance()->getNumberOfDesktops();
    QStringList desktopNameList = WindowManagerInterface::getInstance()->getDesktopNames();
    for (int desktopIndex = 1; desktopIndex <= desktopCount; desktopIndex++) {
        Desktop desktop;
        desktop.setDesktopType(Desktop::DesktopType::WorkSpace);
        if (desktopIndex < desktopNameList.size()) {
            desktop.setCustomName(desktopNameList.at(desktopIndex - 1));
        }
        m_desktopList.append(desktop);
    }

    // 在末尾添加空的Desktop,指代“newDesktop”
    if (desktopCount < MAX_DESKTOP_COUNT) {
        Desktop desktop;
        desktop.setDesktopType(Desktop::DesktopType::NewDesktop);
        m_desktopList.append(desktop);
    }

    endResetModel();
}

QStringList DesktopListModel::getDesktopNames()
{
    QStringList nameList;
    for (const auto &desktop : m_desktopList) {
        nameList.append(desktop.customName());
    }
    return nameList;
}

void DesktopListModel::onDesktopAppend(int desktopIndex)
{
    //当工作区数量达到最大时，再点击“新建工作区”不需要model新增item，只需将model最后一项的属性ItemType由“newDesktop”改为“workSpace”，并修改其他必须修改的属性
    //其他情况下则新增item
    if (desktopIndex == MAX_DESKTOP_COUNT) {
        setData(index(desktopIndex - 1, 0), Desktop::DesktopType::WorkSpace, Role::ItemType);
        setData(index(desktopIndex - 1, 0), "", Role::CustomName);
    } else {
        insertRows(desktopIndex - 1, 1, QModelIndex());
        emit desktopAppended(rowCount() + 1);
    }
    WindowManagerInterface::getInstance()->saveDesktopNames(getDesktopNames());
}

void DesktopListModel::onDesktopRemoved(int desktopIndex)
{
    //当工作区数量是最大时，且最后一项为“workSpace”，由于要播放删除动画，需要先添加“newDesktop”，再删除desktopIndex对应工作区
    if (rowCount() == MAX_DESKTOP_COUNT && index(MAX_DESKTOP_COUNT - 1, 0).data(Role::ItemType).toString() == QString("workSpace")) {
        insertRows(MAX_DESKTOP_COUNT, 1, QModelIndex());
        setData(index(MAX_DESKTOP_COUNT, 0), Desktop::DesktopType::NewDesktop, Role::ItemType);
        setData(index(MAX_DESKTOP_COUNT, 0), "", Role::CustomName);
    }
    removeRows(desktopIndex - 1, 1, QModelIndex());
    WindowManagerInterface::getInstance()->saveDesktopNames(getDesktopNames());
}

void DesktopListModel::onDesktopMoved(int srcDesktopIndex, int destDesktopIndex)
{
    moveRows(QModelIndex(), srcDesktopIndex - 1, 1, QModelIndex(), destDesktopIndex - 1);
    WindowManagerInterface::getInstance()->saveDesktopNames(getDesktopNames());
}
