/*
 * Copyright (C) 2026, 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: iaom <zhangpengfei@kylinos.cn>
 */

#include "ukui-workspace-manager.h"

#include "wayland-pointer_p.h"
#include "wayland-kywc-workspace-v1-client-protocol.h"
#include <QDebug>

namespace UkuiQuick::WaylandClient {

class UkuiWorkspaceManager::Private
{
public:
    Private(UkuiWorkspaceManager *q);

    WaylandPointer<kywc_workspace_manager_v1, kywc_workspace_manager_v1_destroy> manager;
    EventQueue *queue = nullptr;
    UkuiWorkspaceManager *q;
    QList<UkuiWorkspace *> workspaces;
    void init_listener();
    void desktopRemoved(UkuiWorkspace *ws);

    static struct kywc_workspace_manager_v1_listener s_managerListener;
    static void workspaceCallback(void *data, kywc_workspace_manager_v1 *manager, kywc_workspace_v1 *workspace, const char *uuid);
    static void doneCallback(void *data, kywc_workspace_manager_v1 *manager);
    static void finishedCallback(void *data, kywc_workspace_manager_v1 *manager);
};

kywc_workspace_manager_v1_listener UkuiWorkspaceManager::Private::s_managerListener = {
    .workspace = workspaceCallback,
    .done = doneCallback,
    .finished = finishedCallback
};

class UkuiWorkspace::Private
{
public:
    Private(UkuiWorkspace *q);

    WaylandPointer<kywc_workspace_v1, kywc_workspace_v1_destroy> workspace;
    UkuiWorkspace *q;
    QString m_name;
    uint32_t m_position = 0;
    bool m_activated = false;
    QString m_uuid;
    void init_listener(kywc_workspace_v1 *workspace);

    static struct kywc_workspace_v1_listener s_workspaceListener;
    static void nameCallback(void *data, kywc_workspace_v1 *workspace, const char *name);
    static void positionCallback(void *data, kywc_workspace_v1 *workspace, uint32_t position);
    static void activatedCallback(void *data, kywc_workspace_v1 *workspace);
    static void deactivatedCallback(void *data, kywc_workspace_v1 *workspace);
    static void removedCallback(void *data, kywc_workspace_v1 *workspace);
};

kywc_workspace_v1_listener UkuiWorkspace::Private::s_workspaceListener = {
    .name = nameCallback,
    .position = positionCallback,
    .activated = activatedCallback,
    .deactivated = deactivatedCallback,
    .removed = removedCallback
};

UkuiWorkspaceManager::Private::Private(UkuiWorkspaceManager *q) : q(q)
{
}

UkuiWorkspaceManager::UkuiWorkspaceManager(QObject *parent) : QObject(parent), d(new Private(this))
{
}

UkuiWorkspaceManager::~UkuiWorkspaceManager()
{
    release();
}

bool UkuiWorkspaceManager::isValid() const
{
    return d->manager.isValid();
}

void UkuiWorkspaceManager::release()
{
    if (!d->manager) {
        return;
    }
    Q_EMIT interfaceAboutToBeReleased();
    d->manager.release();
}

void UkuiWorkspaceManager::destroy()
{
    if (!d->manager) {
        return;
    }
    Q_EMIT interfaceAboutToBeDestroyed();
    d->manager.destroy();
}

void UkuiWorkspaceManager::setup(kywc_workspace_manager_v1 *manager)
{
    Q_ASSERT(!d->manager);
    Q_ASSERT(manager);
    d->manager.setup(manager);
    d->init_listener();
}

void UkuiWorkspaceManager::setEventQueue(EventQueue *queue)
{
    d->queue = queue;
}

EventQueue *UkuiWorkspaceManager::eventQueue() const
{
    return d->queue;
}

void UkuiWorkspaceManager::createWorkspace(const QString &name, uint32_t position)
{
    if (!d->manager) {
        return;
    }
    kywc_workspace_manager_v1_create_workspace(d->manager,
                                               name.isNull() ? nullptr : qPrintable(name),
                                               position);
}

void UkuiWorkspaceManager::stop()
{
    if (!d->manager) {
        return;
    }
    kywc_workspace_manager_v1_stop(d->manager);
}

QList<UkuiWorkspace*> UkuiWorkspaceManager::workspaces() const
{
    return d->workspaces;
}

void UkuiWorkspaceManager::Private::init_listener()
{
    kywc_workspace_manager_v1_add_listener(manager, &s_managerListener, this);
}

void UkuiWorkspaceManager::Private::desktopRemoved(UkuiWorkspace* ws)
{
    workspaces.removeAll(ws);
}

void UkuiWorkspaceManager::Private::workspaceCallback(void *data, kywc_workspace_manager_v1 *manager, kywc_workspace_v1 *workspace, const char *uuid)
{
    auto p = reinterpret_cast<UkuiWorkspaceManager::Private *>(data);
    if (p->manager != manager) {
        return;
    }

    auto ws = new UkuiWorkspace(p->q);
    ws->d->m_uuid = QString::fromUtf8(uuid);
    p->workspaces.append(ws);
    connect(ws, &UkuiWorkspace::removed, p->q, [p, ws]() {
        p->desktopRemoved(ws);
    });
    ws->setup(workspace);

    Q_EMIT p->q->workspaceCreated(ws, ws->d->m_uuid);
}

void UkuiWorkspaceManager::Private::doneCallback(void *data, kywc_workspace_manager_v1 *manager)
{
    Q_UNUSED(manager)
    auto p = reinterpret_cast<UkuiWorkspaceManager::Private *>(data);
    Q_EMIT p->q->done();
}

void UkuiWorkspaceManager::Private::finishedCallback(void *data, kywc_workspace_manager_v1 *manager)
{
    auto p = reinterpret_cast<UkuiWorkspaceManager::Private *>(data);
    if (p->manager != manager) {
        return;
    }
    Q_EMIT p->q->removed();
}

UkuiWorkspace::Private::Private(UkuiWorkspace *q) : q(q)
{
}

UkuiWorkspace::UkuiWorkspace(QObject *parent) : QObject(parent), d(new Private(this))
{
}

UkuiWorkspace::~UkuiWorkspace()
{
    release();
}

void UkuiWorkspace::setup(kywc_workspace_v1 *workspace)
{
    Q_ASSERT(!d->workspace);
    Q_ASSERT(workspace);
    d->workspace.setup(workspace);
    d->init_listener(workspace);
}

void UkuiWorkspace::release()
{
    d->workspace.release();
}

void UkuiWorkspace::destroy()
{
    d->workspace.destroy();
}

bool UkuiWorkspace::isValid() const
{
    return d->workspace.isValid();
}

QString UkuiWorkspace::name() const
{
    return d->m_name;
}

uint32_t UkuiWorkspace::position() const
{
    return d->m_position;
}

bool UkuiWorkspace::isActivated() const
{
    return d->m_activated;
}

QString UkuiWorkspace::uuid() const
{
    return d->m_uuid;
}

void UkuiWorkspace::setPosition(uint32_t position)
{
    if (!d->workspace) {
        return;
    }
    kywc_workspace_v1_set_position(d->workspace, position);
}

void UkuiWorkspace::activate()
{
    if (!d->workspace) {
        return;
    }
    kywc_workspace_v1_activate(d->workspace);
}

void UkuiWorkspace::remove()
{
    if (!d->workspace) {
        return;
    }
    kywc_workspace_v1_remove(d->workspace);
}

void UkuiWorkspace::setName(const QString &name)
{
    if (!d->workspace) {
        return;
    }
    kywc_workspace_v1_set_name(d->workspace,
                               name.isNull() ? nullptr : qPrintable(name));
}

void UkuiWorkspace::Private::init_listener(kywc_workspace_v1 *workspace)
{
    kywc_workspace_v1_add_listener(workspace, &s_workspaceListener, this);
}

void UkuiWorkspace::Private::nameCallback(void *data, kywc_workspace_v1 *workspace, const char *name)
{
    auto p = reinterpret_cast<UkuiWorkspace::Private *>(data);
    if (p->workspace != workspace) {
        return;
    }
    p->m_name = QString::fromUtf8(name);
    Q_EMIT p->q->nameChanged(p->m_name);
}

void UkuiWorkspace::Private::positionCallback(void *data, kywc_workspace_v1 *workspace, uint32_t position)
{
    auto p = reinterpret_cast<UkuiWorkspace::Private *>(data);
    if (p->workspace != workspace) {
        return;
    }
    p->m_position = position;
    Q_EMIT p->q->positionChanged(p->m_position);
}

void UkuiWorkspace::Private::activatedCallback(void *data, kywc_workspace_v1 *workspace)
{
    auto p = reinterpret_cast<UkuiWorkspace::Private *>(data);
    if (p->workspace != workspace) {
        return;
    }
    p->m_activated = true;
    Q_EMIT p->q->activated();
}

void UkuiWorkspace::Private::deactivatedCallback(void *data, kywc_workspace_v1 *workspace)
{
    auto p = reinterpret_cast<UkuiWorkspace::Private *>(data);
    if (p->workspace != workspace) {
        return;
    }
    p->m_activated = false;
    Q_EMIT p->q->deactivated();
}

void UkuiWorkspace::Private::removedCallback(void *data, kywc_workspace_v1 *workspace)
{
    auto p = reinterpret_cast<UkuiWorkspace::Private *>(data);
    if (p->workspace != workspace) {
        return;
    }
    Q_EMIT p->q->removed();
}

}