/*
 * UKUI Framework
 *
 * Copyright (C) 2024, KylinSoft Co., Ltd.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Authors: Yue Lan <lanyue@kylinos.cn>
 *
 */

#include "theme.h"

#include <QProcess>
#include <QStringList>

#include <QGSettings/QGSettings>

#undef signals
#include <gio/gio.h>

#define QT_SETTINGS_SCHEMA_ID "org.ukui.style"
#define GTK_SETTINGS_SCHEMA_ID "org.mate.interface"
#define GLOBAL_SETTINGS_SCHEMA_ID "org.ukui.globaltheme.settings"

// 自动主题设置
#define AUTO_SCHEMA               "org.ukui.SettingsDaemon.plugins.color"
#define COLOR_KEY_AUTO_THEME      "theme-schedule-automatic"

#define CURSOR_THEME_SCHEMA "org.ukui.peripherals-mouse"

#define KEY_SOUNDS_SCHEMA   "org.ukui.sound"

const QStringList g_themes = {"Light-Seeking", "HeYin", "Classic"};

const QStringList g_iconThemes = {"ukui-icon-theme-default", "ukui-icon-theme-fashion", "ukui-icon-theme-classical"};

const QStringList g_cursorThemes = {"dark-sense", "DMZ-White", "blue-crystal"};

const QStringList g_soundThemes = {"Light-Seeking", "HeYin", "Classic"};

static bool g_selfAdaptedMode = false;

bool g_themeServiceAvailable = true;

// 临时切换实现，需要将GlobalTheme的机制从控制面板中迁移到后端服务，通过后端服务进行切换
void unstableSwitchGlobalTheme(int themeIndex) {
    g_autoptr (GSettings) scopedQtSettings = g_settings_new(QT_SETTINGS_SCHEMA_ID);
    g_autoptr (GSettings) scopedGtkSettings = g_settings_new(GTK_SETTINGS_SCHEMA_ID);
    g_autoptr (GSettings) scopedBgSettings = g_settings_new("org.mate.background");
    g_autoptr (GSettings) scopedCursorSettings = g_settings_new(CURSOR_THEME_SCHEMA);
    g_autoptr (GSettings) scopedUKUIIfaceSettings = g_settings_new("org.ukui.interface");

    bool shouldChange = false;
    QString wallpaperPath;
    QString iconThemeName;
    QString qtColor;
    QString gtkColor;
    QString qtWidgetTheme;
    QString qtStyleName;
    QString gtkStyleName;
    QString cursorTheme;
    int windowRadius = 0;

    switch (themeIndex) {
    case 0: {
        shouldChange = true;
        wallpaperPath = "/usr/share/backgrounds/1-openkylin.jpg";
        iconThemeName = "ukui-icon-theme-default";
        qtColor = "daybreakBlue";
        gtkColor = "#3790FA";
        qtStyleName = "ukui-light";
        qtWidgetTheme = "default";
        gtkStyleName = "ukui-white";
        cursorTheme = "dark-sense";
        windowRadius = 12;
        break;
    }
    case 1: {
        shouldChange = true;
        wallpaperPath = "/usr/share/backgrounds/heyin.png";
        iconThemeName = "ukui-icon-theme-fashion";
        qtColor = "jamPurple";
        gtkColor = "#6A287E";
        qtStyleName = "ukui-dark";
        qtWidgetTheme = "fashion";
        gtkStyleName = "ukui-black";
        cursorTheme = "DMZ-White";
        windowRadius = 12;
        break;
    }
    case 2: {
        shouldChange = true;
        wallpaperPath = "/usr/share/backgrounds/water.jpg";
        iconThemeName = "ukui-icon-theme-classical";
        qtColor = "daybreakBlue";
        gtkColor = "#3790FA";
        qtStyleName = "ukui-light";
        qtWidgetTheme = "classical";
        gtkStyleName = "ukui-white";
        cursorTheme = "dark-sense";
        windowRadius = 0;
        break;
    }
    default:
        return;
    }

    g_settings_set_string(scopedQtSettings, "widget-theme-name", qtWidgetTheme.toLocal8Bit().constData());
    g_settings_set_string(scopedQtSettings, "style-name", qtStyleName.toLocal8Bit().constData());
    g_settings_set_string(scopedQtSettings, "icon-theme-name", iconThemeName.toLocal8Bit().constData());
    g_settings_set_string(scopedQtSettings, "theme-color", qtColor.toLocal8Bit().constData());
    g_settings_set_int(scopedQtSettings, "window-radius", windowRadius);

    g_settings_set_string(scopedGtkSettings, "gtk-theme", gtkStyleName.toLocal8Bit().constData());
    g_settings_set_string(scopedGtkSettings, "icon-theme", iconThemeName.toLocal8Bit().constData());

    g_settings_set_string(scopedUKUIIfaceSettings, "primary-color", gtkColor.toLocal8Bit().constData());

    g_settings_set_string(scopedBgSettings, "picture-filename", wallpaperPath.toLocal8Bit().constData());

    g_settings_set_string(scopedCursorSettings, "cursor-theme", cursorTheme.toLocal8Bit().constData());
}

Theme::Theme(QObject *parent):
    QObject(parent)
{
    if (!QGSettings::isSchemaInstalled(GLOBAL_SETTINGS_SCHEMA_ID) || !QGSettings::isSchemaInstalled(QT_SETTINGS_SCHEMA_ID)
            || !QGSettings::isSchemaInstalled(AUTO_SCHEMA) || !QGSettings::isSchemaInstalled(GTK_SETTINGS_SCHEMA_ID) ||
            !QGSettings::isSchemaInstalled(CURSOR_THEME_SCHEMA) || !QGSettings::isSchemaInstalled(KEY_SOUNDS_SCHEMA)) {
        g_themeServiceAvailable = false;
        return;
    }

    auto globalThemeSettings = new QGSettings(GLOBAL_SETTINGS_SCHEMA_ID);
    globalThemeSettings->setParent(this);
    GlobalThemeName = globalThemeSettings->get("globalThemeName").toString();
    UserCustomized = globalThemeSettings->get("isModified").toBool();
    connect(globalThemeSettings, &QGSettings::changed, this, [=](const QString &key){
        if (key == "globalThemeName") {
            GlobalThemeName = globalThemeSettings->get("globalThemeName").toString();
            emit GlobalThemeNameChanged();
        } else if (key == "isModified") {
            UserCustomized = globalThemeSettings->get("isModified").toBool();
            emit UserCustomizedChanged();
        }
    });

    auto qtSettings = new QGSettings(QT_SETTINGS_SCHEMA_ID);
    qtSettings->setParent(this);
    QtStyleName = qtSettings->get("widgetThemeName").toString();
    QString qtColorSchemeName = qtSettings->get("styleName").toString();

    auto selfAdaptedSettings = new QGSettings(AUTO_SCHEMA);
    g_selfAdaptedMode = selfAdaptedSettings->get(COLOR_KEY_AUTO_THEME).toBool();
    // 判断自适应模式
    if (g_selfAdaptedMode) {
        ThemeMode = SelfAdapted;
    } else {
        if (qtColorSchemeName == "ukui-dark") {
            ThemeMode = Dark;
        } else if (qtColorSchemeName == "ukui-default") {
            ThemeMode = Mixed;
        } else {
            ThemeMode = Light;
        }
    }

    IconThemeName = qtSettings->get("iconThemeName").toString();
    WindowRadius = qtSettings->get("windowRadius").toInt();

    connect(qtSettings, &QGSettings::changed, this, [=](const QString &key){
        if (key == "widgetThemeName") {
            QtStyleName = qtSettings->get("widgetThemeName").toString();
            emit QtStyleNameChanged();
        } else if (key == "styleName") {
            // 判断自适应模式
            if (g_selfAdaptedMode) {
                if (ThemeMode != SelfAdapted) {
                    ThemeMode = SelfAdapted;
                    emit ThemeModeChanged();
                }
                return;
            }

            if (qtColorSchemeName == "ukui-dark") {
                ThemeMode = Dark;
            } else if (qtColorSchemeName == "ukui-default") {
                ThemeMode = Mixed;
            } else {
                ThemeMode = Light;
            }

            emit ThemeModeChanged();
        } else if (key == "iconThemeName") {
            IconThemeName = qtSettings->get("iconThemeName").toString();
            emit IconThemeNameChanged();
        } else if (key == "windowRadius") {
            WindowRadius = qtSettings->get("windowRadius").toInt();
            emit WindowRadiusChanged();
        }
    });

    auto gtkSettings = new QGSettings(GTK_SETTINGS_SCHEMA_ID);
    gtkSettings->setParent(this);
    GtkStyleName = gtkSettings->get("gtkTheme").toString();
    connect(gtkSettings, &QGSettings::changed, this, [=](const QString &key){
        if (key == "gtkTheme") {
            GtkStyleName = gtkSettings->get("gtkTheme").toString();
            emit GtkStyleNameChanged();
        }
    });

    auto cursorSettings = new QGSettings(CURSOR_THEME_SCHEMA);
    cursorSettings->setParent(this);
    CursorThemeName = cursorSettings->get("cursorTheme").toString();
    connect(cursorSettings, &QGSettings::changed, this, [=](const QString &key){
        if (key == "cursorTheme") {
            CursorThemeName = cursorSettings->get("cursorTheme").toString();
            emit CursorThemeNameChanged();
        }
    });

    auto soundSettings = new QGSettings(KEY_SOUNDS_SCHEMA);
    SoundThemeName = soundSettings->get("themeName").toString();
    connect(soundSettings, &QGSettings::changed, this, [=](const QString &key){
        if (key == "themeName") {
            SoundThemeName = soundSettings->get("themeName").toString();
            emit SoundThemeNameChanged();
        }
    });
}

Theme::~Theme()
{

}

bool Theme::IsUserCustomized()
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
    }
    return UserCustomized;
}

QStringList Theme::GetStyleThemeList()
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
    }
    // fixme: 从后台获取全局主题
    return g_themes;
}

bool Theme::SetStyleTheme(const QString &name)
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
        return false;
    }

    if (g_themes.contains(name)) {
        // fixme: 后台响应全局主题切换
        unstableSwitchGlobalTheme(g_themes.indexOf(name));
        g_autoptr (GSettings) settings = g_settings_new(GLOBAL_SETTINGS_SCHEMA_ID);
        bool successed = g_settings_set_string(settings, "global-theme-name", name.toLocal8Bit().constData());
        if (successed) {
            setUserModified(false);
        }
        return successed;
    } else {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidArgs, tr("Invalid theme name: %1, avaliable names are %2").arg(name).arg(g_themes.join(" ")));
        }
        return false;
    }
    return false;
}

bool Theme::SetThemeMode(int themeMode)
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
        return false;
    }

    bool successed = false;
    // 如果不是自适应模式，则需要重新设置当前主题自动切换功能
    if (themeMode != 3) {
        g_autoptr (GSettings) scopedSettings = g_settings_new(AUTO_SCHEMA);
        g_settings_set_boolean(scopedSettings, COLOR_KEY_AUTO_THEME, false);
        g_selfAdaptedMode = false;
    } else {
        g_selfAdaptedMode = true;
    }

    switch (ThemeModeEnum(themeMode)) {
    case Light: {
        g_autoptr (GSettings) qtSettings = g_settings_new(QT_SETTINGS_SCHEMA_ID);
        g_autoptr (GSettings) gtkSettings = g_settings_new(GTK_SETTINGS_SCHEMA_ID);
        if (g_settings_set_string(qtSettings, "style-name", "ukui-light")) {
            // fixme: gtk主题需要适配widget theme name
            successed = g_settings_set_string(gtkSettings, "gtk-theme", "ukui-white");
        }
        break;
    }
    case Dark: {
        g_autoptr (GSettings) qtSettings = g_settings_new(QT_SETTINGS_SCHEMA_ID);
        g_autoptr (GSettings) gtkSettings = g_settings_new(GTK_SETTINGS_SCHEMA_ID);
        if (g_settings_set_string(qtSettings, "style-name", "ukui-dark")) {
            // fixme: gtk主题需要适配widget theme name
            successed = g_settings_set_string(gtkSettings, "gtk-theme", "ukui-black");
        }
        break;
    }
    case Mixed: {
        g_autoptr (GSettings) qtSettings = g_settings_new(QT_SETTINGS_SCHEMA_ID);
        g_autoptr (GSettings) gtkSettings = g_settings_new(GTK_SETTINGS_SCHEMA_ID);
        if (g_settings_set_string(qtSettings, "style-name", "ukui-default")) {
            // fixme: gtk主题需要适配widget theme name
            successed = g_settings_set_string(gtkSettings, "gtk-theme", "ukui-white");
        }
        break;
    }
    case SelfAdapted: {
        g_autoptr (GSettings) scopedSettings = g_settings_new(AUTO_SCHEMA);
        if (g_settings_set_boolean(scopedSettings, COLOR_KEY_AUTO_THEME, true)) {
            return true;
        }
        break;
    }
    default:
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidArgs, tr("Invalid Mode: %1").arg(themeMode));
        }
        return false;
    }

    if (successed) {
        setUserModified(true);
    }
    return successed;
}

bool Theme::SetAccentColor(const QString &hexColor)
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
        return false;
    }

    // fixme: 当前强调色配置为字符类型，需要调整
    if (calledFromDBus()) {
        sendErrorReply(QDBusError::NotSupported, tr("Not supported yet."));
    }

    return false;
}

QStringList Theme::GetIconThemeList()
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
    }
    // fixme: 从后台获取图标主题
    return g_iconThemes;
}

bool Theme::SetIconTheme(const QString &iconThemeName)
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
        return false;
    }

    if (g_iconThemes.contains(iconThemeName)) {
        bool successed = false;
        g_autoptr (GSettings) qtSettings = g_settings_new(QT_SETTINGS_SCHEMA_ID);
        successed = g_settings_set_string(qtSettings, "icon-theme-name", iconThemeName.toLocal8Bit().constData());
        if (successed) {
            g_autoptr (GSettings) gtkSettings = g_settings_new(GTK_SETTINGS_SCHEMA_ID);
            successed = g_settings_set_string(gtkSettings, "icon-theme", iconThemeName.toLocal8Bit().constData());
            setUserModified(true);
        }
        return successed;
    } else {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidArgs, tr("Invalid theme name: %1, avaliable names are %2").arg(iconThemeName).arg(g_iconThemes.join(" ")));
        }
        return false;
    }
    return false;
}

QStringList Theme::GetCursorThemeList()
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
    }
    // fixme: 从后台获取支持的光标主题
    return g_cursorThemes;
}

bool Theme::SetCursorTheme(const QString &cursorThemeName)
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
        return false;
    }

    if (g_cursorThemes.contains(cursorThemeName)) {
        bool successed = false;
        g_autoptr (GSettings) scopedSettings = g_settings_new(CURSOR_THEME_SCHEMA);
        successed = g_settings_set_string(scopedSettings, "cursor-theme", cursorThemeName.toLocal8Bit().constData());
        return successed;
    } else {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidArgs, tr("Invalid theme name: %1, avaliable names are %2").arg(cursorThemeName).arg(g_cursorThemes.join(" ")));
        }
        return false;
    }
    return false;
}

QStringList Theme::GetSoundThemeList()
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
    }
    // fixme: 从后台获取支持的声音主题
    return g_soundThemes;
}

bool Theme::SetSoundTheme(const QString &soundThemeName)
{
    if (!g_themeServiceAvailable) {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidService, tr("Theme service is not available"));
        }
        return false;
    }

    if (g_soundThemes.contains(soundThemeName)) {
        bool successed = false;
        g_autoptr (GSettings) scopedSettings = g_settings_new(KEY_SOUNDS_SCHEMA);
        successed = g_settings_set_string(scopedSettings, "theme-name", soundThemeName.toLocal8Bit().constData());
        return successed;
    } else {
        if (calledFromDBus()) {
            sendErrorReply(QDBusError::InvalidArgs, tr("Invalid theme name: %1, avaliable names are %2").arg(soundThemeName).arg(g_soundThemes.join(" ")));
        }
        return false;
    }
    return false;
}

/**
 * @brief setUserModified
 * 更新主题自定义状态，在用户设置全局主题后设置为false，
 * 设置其它主题配置是设置为true
 * @param modified
 */
void Theme::setUserModified(bool modified)
{
    g_autoptr (GSettings) settings = g_settings_new(GLOBAL_SETTINGS_SCHEMA_ID);
    g_settings_set_boolean(settings, "is-modified", modified);
}

int Theme::getThemeMode() const
{
    return ThemeMode;
}

bool Theme::SetWindowRadius(int newWindowRadius)
{
    if (WindowRadius == newWindowRadius)
        return false;
    WindowRadius = newWindowRadius;
    emit WindowRadiusChanged();

    return true;
}

int Theme::getWindowRadius() const
{
    return WindowRadius;
}

const QString &Theme::getGlobalThemeName() const
{
    return GlobalThemeName;
}

const QString &Theme::getSoundThemeName() const
{
    return SoundThemeName;
}

const QString &Theme::getCursorThemeName() const
{
    return CursorThemeName;
}

const QString &Theme::getIconThemeName() const
{
    return IconThemeName;
}

const QString &Theme::getGtkStyleName() const
{
    return GtkStyleName;
}

const QString &Theme::getQtStyleName() const
{
    return QtStyleName;
}
