/*
 * Copyright (C) 2023, 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, 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 <http://www.gnu.org/licenses/>.
 *
**/
#include "noticeinterface.h"
#include "universalinterface.h"
#include <QDebug>
#include <QStandardPaths>
#include <QSettings>
#include <KApplicationTrader>
#include <KService>
#include <KSharedConfig>
#include <KConfigGroup>
#include <QFileInfo>

#define NOTICE_SCHEMA           "org.ukui.control-center.notice"
#define ENABLE_NOTICE_KEY       "enable-notice"

#define NOTICE_ORIGIN_SCHEMA    "org.ukui.control-center.noticeorigin"
#define NOTICE_ORIGIN_PATH      "/org/ukui/control-center/noticeorigin/"
#define MAX_SHORTCUTS           1000
#define MESSAGES_KEY "messages"
#define VOICE_KEY  "voice"
#define SHOW_SCREENLOCK_KEY "show-on-screenlock"
#define SHOW_DETAIL_KEY "show-detail"
#define STYLE_KEY "notification-style"

#define NOTIFICATION_DEMO_SCHEMA "org.ukui.notification.demo"
#define AUTOSTART_KEY "autostartdisturb"
#define PROJECTIONSCREEN_KEY "projectionscreendisturb"
#define FULLSCREEN_KEY "fullscreendisturb"
#define ALARMCLOCK_KEY "alarmclockdisturb"
#define TIMESTART_KEY "timestart"
#define TIMEEND_KEY "timeend"

#define AUTOSTARTDIR      "/etc/xdg/autostart/"

NoticeInterface::NoticeInterface()
{
    mUkccNoticeGsettings = UniversalInterface::self()->ukccNoticeGsettings();
    mUkuiNoticeGsettings = UniversalInterface::self()->ukuiNoticeGsettings();
    mStyleGsettings = UniversalInterface::self()->ukuiStyleGsettings();

    mGsettingMap[ENABLE_NOTICE_KEY] = QString("enableNotice");
    mGsettingMap[SHOW_SCREENLOCK_KEY] = QString("showOnScreenlock");
    mGsettingMap[SHOW_DETAIL_KEY] = QString("showDetail");
    mGsettingMap[STYLE_KEY] = QString("notificationStyle");

    mWhiteList.append("kylin-screenshot.desktop");
    mWhiteList.append("peony.desktop");
    mWhiteList.append("kylin-device-daemon.desktop");
    mWhiteList.append("ukui-power-manager.desktop");
    mWhiteList.append("kylin-system-update.desktop");
    mWhiteList.append("ukui-bluetooth.desktop");

    if (mUkccNoticeGsettings)
        connect(mUkccNoticeGsettings, &QGSettings::changed, [=](const QString &key){
            emit changed(key);
        });
    if (mUkuiNoticeGsettings)
        connect(mUkuiNoticeGsettings, &QGSettings::changed, [=](const QString &key){
            emit changed(key);
        });
    if (mStyleGsettings)
        connect(mStyleGsettings, &QGSettings::changed, [=](const QString &key){
            emit changed(key);
        });



}

bool NoticeInterface::getAutostartDisturb()
{
    if (mUkuiNoticeGsettings->keys().contains(AUTOSTART_KEY)) {
        return mUkuiNoticeGsettings->get(AUTOSTART_KEY).toBool();
    } else {
        qCritical() << "ukuiNoticeGsettings not contains the key: " << AUTOSTART_KEY;
    }
    return false;
}

bool NoticeInterface::getProjectionsScreenDisturb()
{
    if (mUkuiNoticeGsettings->keys().contains(PROJECTIONSCREEN_KEY)) {
        return mUkuiNoticeGsettings->get(PROJECTIONSCREEN_KEY).toBool();
    } else {
        qCritical() << "ukuiNoticeGsettings not contains the key: " << PROJECTIONSCREEN_KEY;
    }
    return false;
}

bool NoticeInterface::getFullScreenDisturb()
{
    if (mUkuiNoticeGsettings->keys().contains(FULLSCREEN_KEY)) {
        return mUkuiNoticeGsettings->get(FULLSCREEN_KEY).toBool();
    } else {
        qCritical() << "ukuiNoticeGsettings not contains the key: " << FULLSCREEN_KEY;
    }
    return false;
}

bool NoticeInterface::getAlarmClockDisturb()
{
    if (mUkuiNoticeGsettings->keys().contains(ALARMCLOCK_KEY)) {
        return mUkuiNoticeGsettings->get(ALARMCLOCK_KEY).toBool();
    } else {
        qCritical() << "ukuiNoticeGsettings not contains the key: " << ALARMCLOCK_KEY;
    }
    return false;
}

QString NoticeInterface::getTimeStart()
{
    if (mUkuiNoticeGsettings->keys().contains(TIMESTART_KEY)) {
        return mUkuiNoticeGsettings->get(TIMESTART_KEY).toString();
    } else {
        qCritical() << "ukuiNoticeGsettings not contains the key: " << TIMESTART_KEY;
    }
    return QString();
}

QString NoticeInterface::getTimeEnd()
{
    if (mUkuiNoticeGsettings->keys().contains(TIMEEND_KEY)) {
        return mUkuiNoticeGsettings->get(TIMEEND_KEY).toString();
    } else {
        qCritical() << "ukuiNoticeGsettings not contains the key: " << TIMEEND_KEY;
    }
    return QString();
}

bool NoticeInterface::getNoticeEnable()
{
    if (mUkccNoticeGsettings->keys().contains(mGsettingMap[ENABLE_NOTICE_KEY])) {
        return mUkccNoticeGsettings->get(ENABLE_NOTICE_KEY).toBool();
    } else {
        qCritical() << "mUkccNoticeGsettings not contains the key: " << mUkccNoticeGsettings;
    }
    return false;
}

QList <QVariant> NoticeInterface::getNoticeAppList()
{
    qDBusRegisterMetaType<NoticeApp>();
    QList <QVariant> list;
    for (QString desktopName : mWhiteList) {
        QFileInfo file(AUTOSTARTDIR + desktopName);
        if (file.exists()) {
            QString mainSection = QStringLiteral("Desktop Entry");
            KSharedConfig::Ptr sharedConfig = KSharedConfig::openConfig(AUTOSTARTDIR + desktopName, KConfig::NoGlobals);
            KConfigGroup cfg(sharedConfig, mainSection);
            QString name = cfg.readEntry("Name");
            QString icon = cfg.readEntry("Icon");
            QString fileName = desktopName.left(desktopName.indexOf(QLatin1Char('.')));

            if (mApplist.contains(name))
                continue;
            mApplist.append(name);
            NoticeApp app;
            app.desktop = fileName;
            app.name = name;
            app.icon = icon;
            getAppInfo(app, fileName);
            list.append(QVariant::fromValue(app));
        }
    }

    KApplicationTrader::query([=, &list](const KService::Ptr &service) {
        if (service->exec().isEmpty() || service->noDisplay())
            return false;

        QString appname, desktopEntryName, icon;
        appname = service->name();
        desktopEntryName = service->desktopEntryName();
        icon = service->icon();

        if (mApplist.contains(appname))
            return false;

        mApplist.append(appname);
        NoticeApp app;
        app.desktop = desktopEntryName;
        app.name = appname;
        app.icon = icon;
        getAppInfo(app, desktopEntryName);
        list.append(QVariant::fromValue(app));
        return true;
    });
    mApplist.clear();
    return list;
}

void NoticeInterface::getAppInfo(NoticeApp &app, const QString &desktop)
{
    const QByteArray id(NOTICE_ORIGIN_SCHEMA);
    QString path = QString("%1%2%3").arg(NOTICE_ORIGIN_PATH).arg(desktop).arg("/");
    QGSettings *settings = new QGSettings(id, path.toUtf8().data(), this);

    app.status = settings->get(MESSAGES_KEY).toBool();

    app.voice = settings->get(VOICE_KEY).toBool();
    app.detail = settings->get(SHOW_DETAIL_KEY).toBool();
    app.show = settings->get(SHOW_SCREENLOCK_KEY).toBool();
    QString style = settings->get(STYLE_KEY).toString();
    app.mode = 0;
    if (style == "mutative") {
        app.mode = 0;
    } else if (style == "always") {
        app.mode = 1;
    } else if (style == "none") {
        app.mode = 2;
    }
    mNoticeAppsMap[desktop] = settings;
}

void NoticeInterface::setNoticeDisturb(const QString &key, bool value)
{
    if (mUkuiNoticeGsettings->keys().contains(key)) {
        return mUkuiNoticeGsettings->set(key, value);
    } else {
        qCritical() << "ukuiNoticeGsettings not contains the key: " << key;
    }
}

void NoticeInterface::setNoticeDisturb(const QString &key, const QString &value)
{
    if (mUkuiNoticeGsettings->keys().contains(key)) {
        return mUkuiNoticeGsettings->set(key, value);
    } else {
        qCritical() << "ukuiNoticeGsettings not contains the key: " << key;
    }
}

void NoticeInterface::setUkccNotice(const QString &key, bool value)
{
    if (mUkccNoticeGsettings->keys().contains(mGsettingMap[key]) || mUkccNoticeGsettings->keys().contains(key)) {
        return mUkccNoticeGsettings->set(key, value);
    } else {
        qCritical() << "mUkccNoticeGsettings not contains the key: " << key;
    }
}

void NoticeInterface::setAppInfo(const QString &desktop, const QString &key, bool value)
{
    auto iter = mNoticeAppsMap.find(desktop);
    if (iter != mNoticeAppsMap.end()) {
        QGSettings *settings = iter.value();
        if (settings)
            settings->set(key, value);
    }
}

void NoticeInterface::setAppInfo(const QString &desktop, const QString &key, const QString &value)
{
    auto iter = mNoticeAppsMap.find(desktop);
    if (iter != mNoticeAppsMap.end()) {
        QGSettings *settings = iter.value();
        if (settings)
            settings->set(key, value);
    }
}

const QDBusArgument &operator<<(QDBusArgument &argument, const NoticeApp &app)
{
    argument.beginStructure();
    argument << app.desktop;
    argument << app.icon;
    argument << app.name;
    argument << app.status;
    argument << app.voice;
    argument << app.detail;
    argument << app.show;
    argument << app.mode;
    argument.endStructure();
    return argument;
}

const QDBusArgument &operator>>(const QDBusArgument &argument, NoticeApp &app)
{
    argument.beginStructure();
    argument >> app.desktop;
    argument >> app.icon;
    argument >> app.name;
    argument >> app.status;
    argument >> app.voice;
    argument >> app.detail;
    argument >> app.show;
    argument >> app.mode;
    argument.endStructure();
    return argument;
}
