/*
 * Copyright (C) 2025, 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: Wenjie Xiang <xiangwenjie@kylinos.cn>
 *
 */

#include "background-manager.h"
#include <QVariant>
#include <QDBusInterface>
#include <QDBusReply>
#include <QFile>
#include <QProcess>
#include <QDebug>
#include <QGSettings/QGSettings>
#include <QPixmap>
#include <QApplication>
#include <QScreen>
#include <QImageReader>
#include <gio/gio.h>

static BackgroundManager *global_instance = nullptr;

BackgroundManager::BackgroundManager(QObject *parent)
    : QObject{parent}
{
    initGSettings();
    initBackground();
}

BackgroundManager *BackgroundManager::globalInstance()
{
    if (!global_instance) {
        global_instance = new BackgroundManager;
    }
    return global_instance;
}

QString BackgroundManager::getBackImagePath() const
{
    return m_backImagePath;
}

QString BackgroundManager::getFrontImagePath() const
{
    return m_frontImagePath;
}

QString BackgroundManager::getBackgroundOption() const
{
    return m_backgroundOption;
}

void BackgroundManager::setBackgroundOption(const QString &backgroundOption)
{
    if (m_backgroundOption != backgroundOption) {
        m_backgroundOption = backgroundOption;
        Q_EMIT backgroundOptionChanged();
    }
}

QString BackgroundManager::getAccountBackground() const
{
    if (!m_shouldSyncAccountBackground) {
        if (m_backgroundSettings) {
            return m_backgroundSettings->get(PICTUREFILENAME).toString();
        }
        return "";
    }

    uid_t uid = getuid();
    QDBusInterface iface(ACCOUNT_SETTINGS, ACCOUNT_PATH,
                         ACCOUNT_SETTINGS, QDBusConnection::systemBus());

    QDBusReply<QDBusObjectPath> userPath = iface.call("FindUserById", (qint64)uid);
    if(!userPath.isValid())
        qWarning() << "Get UserPath error:" << userPath.error();
    else {
        QDBusInterface userIface(ACCOUNT_SETTINGS, userPath.value().path(),
                                 "org.freedesktop.DBus.Properties", QDBusConnection::systemBus());
        QDBusReply<QDBusVariant> backgroundReply = userIface.call("Get", "org.freedesktop.Accounts.User", "BackgroundFile");
        if(backgroundReply.isValid())
            return  backgroundReply.value().variant().toString();
    }
    return "";
}

void BackgroundManager::setAccountBackground(const QString &backgroundPath)
{
    if (!m_shouldSyncAccountBackground) {
        return;
    }

    QDBusInterface * interface = new QDBusInterface(ACCOUNT_SETTINGS,
                                                    ACCOUNT_PATH,
                                                    ACCOUNT_SETTINGS,
                                                    QDBusConnection::systemBus());

    if (!interface->isValid()){
        qCritical() << "Create /org/freedesktop/Accounts Client Interface Failed " << QDBusConnection::systemBus().lastError();
        return;
    }

    QDBusReply<QDBusObjectPath> reply = interface->call("FindUserByName", g_get_user_name());
    QString userPath;
    if (reply.isValid()){
        userPath = reply.value().path();
    } else {
        qCritical() << "Call 'GetComputerInfo' Failed!" << reply.error().message();
        return;
    }

    QDBusInterface *useriFace = new QDBusInterface(ACCOUNT_SETTINGS,
                                                   userPath,
                                                   "org.freedesktop.Accounts.User",
                                                   QDBusConnection::systemBus());

    if (!useriFace->isValid()){
        qCritical() << QString("Create %1 Client Interface Failed").arg(userPath) << QDBusConnection::systemBus().lastError();
        return;
    }

    QDBusMessage msg = useriFace->call("SetBackgroundFile", backgroundPath);
    qDebug() << "setAccountBackground path:" << backgroundPath;
    if (!msg.errorMessage().isEmpty()) {
        qDebug() << "update user background file error: " << msg.errorMessage();
    } else {
        Q_EMIT accountBackgroundChanged();
    }

    //saveBlurBackground
    QProcess p;
    p.startDetached("/usr/bin/save-blurBackground");
}

bool BackgroundManager::getUsePureColor() const
{
    return m_usePureColor;
}

void BackgroundManager::setUsePureColor(const bool usePureColor)
{
    if (m_usePureColor != usePureColor) {
        m_usePureColor = usePureColor;
        Q_EMIT usePureColorChanged();
    }
}

QColor BackgroundManager::getColor() const
{
    return m_color;
}

void BackgroundManager::setColor(const QColor &color)
{
    if (m_color != color) {
        m_color = color;
        Q_EMIT colorChanged();
    }
}

QRect BackgroundManager::calculateSpannedSourceRect(const QRect &screenGeometry, const QString &imagePath) const
{
    QImageReader reader(imagePath);
    QRegion region;
    for (auto screen : qApp->screens()) {
        region += screen->geometry();
    }
    QRect virtualGeometry = region.boundingRect().translated(0, 0);
    qreal pixWidth = reader.size().width();
    qreal pixHeight = reader.size().height();

    QSize sourceSize = reader.size();
    sourceSize.setWidth(screenGeometry.width() * 1.0 / virtualGeometry.width() * pixWidth);
    sourceSize.setHeight(screenGeometry.height() * 1.0 / virtualGeometry.height() * pixHeight);

    qint32 offsetX = 0;
    qint32 offsetY = 0;
    if (screenGeometry.x() > 0) {
        offsetX = (screenGeometry.x() * 1.0 / virtualGeometry.width() * pixWidth);
    }

    if (screenGeometry.y() > 0) {
        offsetY = (screenGeometry.y() * 1.0 / virtualGeometry.height() * pixHeight);
    }

    QPoint offsetPoint(0, 0);
    offsetPoint += QPoint(offsetX, offsetY);

    return QRect(offsetPoint, sourceSize);
}

void BackgroundManager::initGSettings()
{
    if (QGSettings::isSchemaInstalled(BACKGROUND_SETTINGS)) {
        m_backgroundSettings = new QGSettings(BACKGROUND_SETTINGS, QByteArray(), this);
        m_backgroundOption = m_backgroundSettings->get(PICTUREOPTIONS).toString();

        g_autoptr (GSettings) settings = g_settings_new_with_path(BACKGROUND_SETTINGS, BACKGROUND_PATH);
        if (settings) {
            bool writable = g_settings_is_writable(settings, PICTURE_FILENAME);
            m_shouldSyncAccountBackground = writable;
        }
    } else if (QGSettings::isSchemaInstalled(BACKGROUND_MATE_SETTINGS)){
        m_backgroundSettings = new QGSettings(BACKGROUND_MATE_SETTINGS, QByteArray(), this);
        m_backgroundOption = m_backgroundSettings->get(PICTUREOPTIONS).toString();

        g_autoptr (GSettings) settings = g_settings_new_with_path(BACKGROUND_MATE_SETTINGS, BACKGROUND_MATE_PATH);
        if (settings) {
            bool writable = g_settings_is_writable(settings, PICTURE_FILENAME);
            m_shouldSyncAccountBackground = writable;
        }
    } else {
        m_backgroundOption = "scaled";
    }

    if (m_backgroundSettings) {
        connect(m_backgroundSettings, &QGSettings::changed, this, [=](const QString &key){
            if (key == PICTUREFILENAME) {
                m_shouldSyncAccountBackground = true;
                setAccountBackground(m_backgroundSettings->get(PICTUREFILENAME).toString());
            }
            if (key == PICTUREFILENAME || key == PRIMARYCOLOR || key == PICTUREOPTIONS) {
                switchBackground();
            }
        });
    }
}

void BackgroundManager::initBackground()
{
    QString defaultBg;
    auto accountBack = getAccountBackground();
    if (accountBack != "" && QFile::exists(accountBack))
        defaultBg = accountBack;

    if (!QFile::exists(defaultBg))
    {
       qWarning() << "default bg and account bg not exist";
       switchBackground();
       return;
    }

    m_frontImagePath = defaultBg;
    m_backImagePath = m_frontImagePath;

    if (defaultBg != accountBack) {
        setAccountBackground(defaultBg);
    }
}

void BackgroundManager::switchBackground()
{
    if (!m_backgroundSettings)
        return;

    setBackgroundOption(m_backgroundSettings->get(PICTUREOPTIONS).toString());

    auto path = m_backgroundSettings->get(PICTUREFILENAME).toString();
    QString localPath = path;
    if (!QFile::exists(path))
        path = getAccountBackground();

    // try fix #124971
    if (!localPath.isEmpty() && (path.isEmpty() || !QFile::exists(path))) {
        path = DEFAULTBACKGROUND;
    }

    if (path.isEmpty()) {
        setUsePureColor(true);
        setColor(QColor(m_backgroundSettings->get(PRIMARYCOLOR).toString()));
        m_backImagePath = "";
        m_frontImagePath = "";
    } else {
        setUsePureColor(false);
        setColor(QColor(m_backgroundSettings->get(PRIMARYCOLOR).toString()));

        if (m_backImagePath.isEmpty() && m_frontImagePath.isEmpty()) {
            m_backImagePath = path;
            m_frontImagePath = path;
        }

        m_backImagePath = m_frontImagePath;
        m_frontImagePath = path;
        Q_EMIT backgroundImageChanged();

        qDebug() << __func__ << __LINE__ << m_backImagePath << m_frontImagePath;

        if (path != getAccountBackground()) {
            setAccountBackground(path);
        }
    }
}

