/*
    SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "osdmanager.h"
#include "kscreen_daemon_debug.h"
#include "osd.h"

#include <KScreen/Config>
#include <KScreen/EDID>
#include <KScreen/GetConfigOperation>
#include <KScreen/Output>

#include <QDBusConnection>

#include <QQmlEngine>

namespace KScreen
{
class OsdActionImpl : public OsdAction
{
    Q_OBJECT
public:
    OsdActionImpl(QObject *parent = nullptr)
        : OsdAction(parent)
    {
    }

    void setOsd(Osd *osd)
    {
        connect(osd, &Osd::osdActionSelected, this, [this](Action action) {
            Q_EMIT selected(action);
            deleteLater();
        });
    }
};

OsdManager::OsdManager(QObject *parent)
    : QObject(parent)
    , m_cleanupTimer(new QTimer(this))
{
    qmlRegisterSingletonType<KScreen::OsdAction>("org.kde.KScreen", 1, 0, "OsdAction", [](QQmlEngine *, QJSEngine *) -> QObject * {
        return new KScreen::OsdAction();
    });

    // free up memory when the osd hasn't been used for more than 1 minute
    m_cleanupTimer->setInterval(60000);
    m_cleanupTimer->setSingleShot(true);
    connect(m_cleanupTimer, &QTimer::timeout, this, [this]() {
        hideOsd();
    });
    QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kscreen.osdService"));
    if (!QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/kscreen/osdService"), this, QDBusConnection::ExportAllSlots)) {
        qCWarning(KSCREEN_KDED) << "Failed to registerObject";
    }
}

void OsdManager::hideOsd()
{
    qDeleteAll(m_osds);
    m_osds.clear();
}

OsdManager::~OsdManager()
{
}

OsdAction *OsdManager::showActionSelector()
{
    hideOsd();

    OsdActionImpl *action = new OsdActionImpl(this);
    connect(action, &OsdActionImpl::selected, this, [this]() {
        for (auto osd : qAsConst(m_osds)) {
            osd->hideOsd();
        }
    });
    connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, [this, action](const KScreen::ConfigOperation *op) {
        if (op->hasError()) {
            qCWarning(KSCREEN_KDED) << op->errorString();
            return;
        }

        // Show selector on all enabled screens
        const auto outputs = op->config()->outputs();
        KScreen::OutputPtr osdOutput;
        for (const auto &output : outputs) {
            if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) {
                continue;
            }

            // Prefer laptop screen
            if (output->type() == KScreen::Output::Panel) {
                osdOutput = output;
                break;
            }

            // Fallback to primary
            if (output->isPrimary()) {
                osdOutput = output;
                break;
            }
        }
        // no laptop or primary screen, just take the first usable one
        if (!osdOutput) {
            for (const auto &output : outputs) {
                if (output->isConnected() && output->isEnabled() && output->currentMode()) {
                    osdOutput = output;
                    break;
                }
            }
        }

        if (!osdOutput) {
            // huh!?
            return;
        }

        KScreen::Osd *osd = nullptr;
        if (m_osds.contains(osdOutput->name())) {
            osd = m_osds.value(osdOutput->name());
        } else {
            osd = new KScreen::Osd(osdOutput, this);
            m_osds.insert(osdOutput->name(), osd);
        }
        action->setOsd(osd);
        osd->showActionSelector();
        m_cleanupTimer->start();
    });

    return action;
}

}

#include "osdmanager.moc"
