/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * Copyright (C) 2019 Tianjin KYLIN Information Technology 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 2 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */
#include "shortcut.h"
#include "realizeshortcutwheel.h"
#include "ukcccommon.h"
using namespace ukcc;
#include <QDebug>

/* qt会将glib里的signals成员识别为宏，所以取消该宏
 * 后面如果用到signals时，使用Q_SIGNALS代替即可
 **/
#ifdef signals
#undef signals
#endif

#include <glib.h>
#include <glib/gi18n.h>

Shortcut::Shortcut() : mFirstLoad(true)
{
    pluginName = tr("Shortcut");
    pluginType = DEVICES;
    qRegisterMetaType<QStringPair>("QStringPair");
    qDBusRegisterMetaType<QStringPair>();
    qRegisterMetaType<QStringPairList>("QStringPairList");
    qDBusRegisterMetaType<QStringPairList>();

    qRegisterMetaType<KeyEntry>("KeyEntry");
    qDBusRegisterMetaType<KeyEntry>();
    qRegisterMetaType<KeyEntryList>("KeyEntryList");
    qDBusRegisterMetaType<KeyEntryList>();
}

Shortcut::~Shortcut()
{
    shortcutUi->deleteLater();
    shortcutUi = nullptr;
}

QString Shortcut::plugini18nName()
{
    return pluginName;
}

int Shortcut::pluginTypes()
{
    return pluginType;
}

QWidget *Shortcut::pluginUi()
{
    if (mFirstLoad) {
        mFirstLoad = false;
        shortcutUi = new ShortcutUi;
        shortcutInterface = new QDBusInterface("org.ukui.ukcc.session",
                                                "/Shortcut",
                                                "org.ukui.ukcc.session.Shortcut",
                                                QDBusConnection::sessionBus(), this);
        if (shortcutInterface->isValid()) { // 判断服务是否存在
            QDBusMessage message = shortcutInterface->call("ping"); // 判断dbus路径是否存在
            if (message.type() == QDBusMessage::ErrorMessage && message.errorMessage().contains("No such object path", Qt::CaseInsensitive)) {
                qWarning()<<shortcutInterface<<":"<<message.errorMessage();
            } else {
                QDBusConnection::sessionBus().connect("org.ukui.ukcc.session",
                                                      "/Shortcut",
                                                      "org.ukui.ukcc.session.Shortcut",
                                                      "changed",
                                                      this,
                                                      SLOT(dataChanged(QString)));
                initContent();
                connectUiSignals();
                connectToServer();
            }
        } else {
            qCritical() << "org.ukui.ukcc.session.Wallpaper DBus error:" << shortcutInterface->lastError();
        }
    }
    return shortcutUi;
}

const QString Shortcut::name() const
{
    return QStringLiteral("Shortcut");
}

bool Shortcut::isShowOnHomePage() const
{
    return false;
}

QIcon Shortcut::icon() const
{
    return QIcon::fromTheme("ukui-shortcut-key-symbolic");
}

bool Shortcut::isEnable() const
{
    return true;
}

void Shortcut::connectToServer()
{
    QThread *NetThread = new QThread;
    MThread *NetWorker = new MThread;
    NetWorker->moveToThread(NetThread);
    connect(NetThread, &QThread::started, NetWorker, &MThread::run);
    connect(NetWorker, &MThread::keychangedsignal,this,&Shortcut::shortcutChangedSlot);
    connect(NetThread, &QThread::finished, NetWorker, &MThread::deleteLater);
    NetThread->start();
}

void Shortcut::initContent()
{
    initShortEntry();
    initSystem();
    initWindow();
    initCustom();
}

void Shortcut::initShortEntry()
{
    QDBusReply<KeyEntryList> listEntrySystem = shortcutInterface->call("getSystemShortcutEntry");
    QDBusReply<KeyEntryList> listEntryCustom = shortcutInterface->call("getCustomShortcutEntry");
    QDBusReply<KeyEntryList> listEntryWindow = shortcutInterface->call("getWindowShortcutEntry");

    if (listEntrySystem.isValid()) {
        systemEntryList = listEntrySystem.value();
    }
    m_windowEntryList.clear();
    if (listEntryWindow.isValid()) {
        m_windowEntryList = listEntryWindow.value();
        if (m_windowEntryList.empty()) {
            qDebug() << Q_FUNC_INFO << "window shortcut is empty";
            m_windowShortcutValid = false;
        }
    } else {
        m_windowShortcutValid = false;
        qDebug() << Q_FUNC_INFO << "window shortcut invalid";
    }

    if (listEntryCustom.isValid()) {
        customEntryList = listEntryCustom.value();
    }

    shortcutUi->loadDefaultShortcut();
}

QMap<QString, QString> Shortcut::MergerOfTheSamekind(QMap<QString, QString> desktopMap) {
    QMap<QString, QString>::iterator it = desktopMap.begin();
    for (; it != desktopMap.end(); it++) {
        QString name = it.key().at(it.key().size() - 1);
        QString name_modification = it.key().left(it.key().length() - 1);
        if (name == '2') {
            desktopMap[name_modification] = desktopMap[name_modification]+" or " + it.value();
            qDebug() << Q_FUNC_INFO << it.key() << name << name_modification << desktopMap[name_modification];
            desktopMap.erase(it);
            it = desktopMap.begin()+1;// 除之后要将指针指向后面一个
        }
    }
    return desktopMap;
}

void Shortcut::initSystem()
{
    QDBusReply<QStringPairList> list = shortcutInterface->call("getSystemShortcut");
    QMap<QString, QString> desktopMap;
    for (int i = 0; i < systemEntryList.count(); i++) {
        if ((systemEntryList[i].gsSchema == KEYBINDINGS_DESKTOP_SCHEMA) &&
                !systemEntryList[i].keyStr.contains("ukui-window-switch")) {
            desktopMap.insert(systemEntryList[i].keyStr, systemEntryList[i].valueStr);
        }
    }
    desktopMap = MergerOfTheSamekind(desktopMap);
    QList<QString> generalSortKeyList = {"area-screenshot", "screenshot", "window-screenshot", "kylin-display-switch",
                                     "logout", "nm-connection-editor", "peony-qt", "screensaver", "terminal", "ukui-control-center", "ukui-search", "ukui-sidebar",
                                     "ukui-system-monitor", "ukui-window-switch",
                                     "kylin-service-support"};
    foreach (const auto &key, generalSortKeyList) {
        QMap<QString, QString>::iterator iter = desktopMap.find(key);
        if (iter != desktopMap.end()) {
            for (QStringPair s : list.value()) {
                if (s.mediaKey != key) {
                    continue;
                }
                QString name = s.name;
                shortcutUi->addSystemShortcut(name, iter.value(), iter.key(), &systemEntryList, &m_windowEntryList, &customEntryList);
                break;
            }
            desktopMap.erase(iter);
        }
    }
}

void Shortcut::initWindow()
{
    if (!m_windowShortcutValid) {
        shortcutUi->setWindowValid(false);
        shortcutUi->setWindowHide();
        shortcutUi->addDefaultBtn();
        return;
    }
    QMap<QString, QString> windowMap;
    for (int i = 0; i < m_windowEntryList.count(); i++) {
        windowMap.insert(m_windowEntryList[i].keyStr, m_windowEntryList[i].valueStr);
        qDebug() << Q_FUNC_INFO << m_windowEntryList[i].keyStr << m_windowEntryList[i].valueStr;
    }
    windowMap = MergerOfTheSamekind(windowMap);
    for (const auto& entries : m_windowEntryList) {
        QString key = entries.keyStr;
        if (!windowMap.keys().contains(key)) {
            continue;
        }
        QString name = entries.nameStr;
        shortcutUi->addWindowShortcut(name, windowMap[key], key, &systemEntryList, &m_windowEntryList, &customEntryList);
    }
    shortcutUi->addDefaultBtn();
}

void Shortcut::initCustom()
{
    for (KeyEntry s : customEntryList) {
        shortcutUi->addCustomShortcut(s, &systemEntryList, &m_windowEntryList, &customEntryList);
    }
}

void Shortcut::connectUiSignals()
{
    connect(shortcutUi, &ShortcutUi::deleteShortcut, this, [=](QString path) {
        deleteCustomShortcut(path);
        shortcutInterface->call("updateShortcut");
    });

    connect(shortcutUi, &ShortcutUi::toCreateShortcut, this, [=](QString path, QString name, QString exec, QString key, bool buildFlag, bool convertFlag, DoubleClickShortCut *shortCut){
        createNewShortcut(path, name, exec, key, buildFlag, convertFlag, shortCut);
        shortcutInterface->call("updateShortcut");
    });

    connect(shortcutUi, &ShortcutUi::updateSystemShortcut, this, [=](const QString &key, const QString &value){
        shortcutInterface->call("setSystemShortcut", key, value);
        shortcutInterface->call("updateShortcut");
    });

    connect(shortcutUi, &ShortcutUi::addButtonClicked, this, [=]{
        AddShortcutDialog *addDialog = new AddShortcutDialog(systemEntryList, m_windowEntryList, customEntryList);
        addDialog->setTitleText(QObject::tr("Customize Shortcut"));

        connect(addDialog, &AddShortcutDialog::shortcutInfoSignal,
                [=](QString path, QString name, QString exec, QString key){
            createNewShortcut(path, name, exec, key, true, true);
            shortcutInterface->call("updateShortcut");
        });
        UkccCommon::buriedSettings(this->name(), QString("AddBtn"), QString("clicked"));
        addDialog->exec();
    });
}

void Shortcut::deleteCustomShortcut(QString path)
{
    if (path.isEmpty()) {
        return;
    }
    QProcess p(0);
    QStringList args;

    char *fullpath = path.toLatin1().data();
    QString cmd = "dconf";

    args.append("reset");
    args.append("-f");
    args.append(fullpath);
    p.execute(cmd, args);// command是要执行的命令,args是参数
    qDebug()<<"wait for finish";
    p.waitForFinished(-1);
    qDebug()<<QString::fromLocal8Bit(p.readAllStandardError());
}

void Shortcut::createNewShortcut(QString path, QString name, QString exec, QString key, bool buildFlag, bool convertFlag, DoubleClickShortCut *shortCut)
{
    if (key.contains("Meta")) {
        key.replace("Meta", "Win");
    }
    if (key.contains("Print")) {
        key.replace("Print", "PrtSc");
    }
    if (key.contains("Prtsc")) {
        key.replace("Prtsc", "PrtSc");
    }
    qDebug() << "createNewShortcut" << path << name << exec << key;
    QString availablepath;
    if (path.isEmpty()) {
        availablepath = findFreePath(); // 创建快捷键

        // 更新数据
        KeyEntry nKeyentry;
        nKeyentry.gsPath = availablepath;
        nKeyentry.nameStr = name;
        if (convertFlag == true) {
            nKeyentry.bindingStr = keyToLib(key);
        } else {
            nKeyentry.bindingStr = key;
        }
        nKeyentry.actionStr = exec;

        customEntryList.append(nKeyentry);
        if (true == buildFlag) {
            shortcutUi->addCustomShortcut(nKeyentry, &systemEntryList, &m_windowEntryList, &customEntryList);
        }
    } else {
        availablepath = path; // 更新快捷键

        if (convertFlag) {
            UkccCommon::buriedSettings(name, exec, QString("settings"), key);
        }
        // 更新数据
        for (int i = 0; i < customEntryList.count(); i++) {
            if (customEntryList[i].gsPath == availablepath) {
                customEntryList[i].nameStr = name;
                customEntryList[i].actionStr = exec;
                if (convertFlag == true) {
                    customEntryList[i].bindingStr = keyToLib(key);
                } else {
                    customEntryList[i].bindingStr = key;
                }

                if (shortCut) {
                    shortCut->setProperty("shortKey", customEntryList[i].bindingStr);
                    qDebug() << Q_FUNC_INFO << "custom update property shortKey to" << customEntryList[i].bindingStr << exec;
                } else {
                    qDebug() << Q_FUNC_INFO << "custom update shortCut is null" << customEntryList[i].bindingStr;
                }
                break;
            }
        }
    }

    const QByteArray id(KEYBINDINGS_CUSTOM_SCHEMA);
    const QByteArray idd(availablepath.toLatin1().data());
    QGSettings *settings = new QGSettings(id, idd, this);
    if (convertFlag == true) {
         settings->set(BINDING_KEY, keyToLib(key));
    } else {
         settings->set(BINDING_KEY, key);
    }

    settings->set(NAME_KEY, name);
    settings->set(ACTION_KEY, exec);

    delete settings;
    settings = nullptr;
}

QString Shortcut::keyToLib(QString key)
{
    if (key.contains("Meta")) {
        key.replace("Meta", "Win");
    }

    if (key.contains("Start")) {
        key.replace("Start", "Win");
    }

    if (key.contains("Print", Qt::CaseInsensitive)) {
        key.replace("Print", "PrtSc", Qt::CaseInsensitive);
    }

    if (key.contains("↑")) {
        key.replace("↑", "Up");
    }
    if (key.contains("→")) {
        key.replace("→", "Right");
    }
    if (key.contains("←")) {
        key.replace("←", "Left");
    }
    if (key.contains("↓")) {
        key.replace("↓", "Down");
    }

    if (key.contains("+")) {
        QStringList keys = key.split("+");
        if (keys.count() == 2) {
            QString lower = keys.at(1);
            QString keyToLib = "<" + keys.at(0) + ">" + lower.toLower();
            qDebug() << "count = 2,keyToLib = " << keyToLib;
            return keyToLib;
        } else if (keys.count() == 3) {
            QString lower = keys.at(2);
            QString keyToLib = "<" + keys.at(0) + ">" + "<" + keys.at(1) + ">" + lower.toLower();
            qDebug() << "count = 3,keyToLib = " << keyToLib;
            return keyToLib;
        } else if (keys.count() == 4) {
            QString lower = keys.at(3);
            QString keyToLib = "<" + keys.at(0) + ">" + "<" + keys.at(1) + ">" + "<" + keys.at(2) + ">" + lower.toLower();
            qDebug() << "count = 4,keyToLib = " << keyToLib;
            return keyToLib;
        }
    }
    qDebug() << "count = 1,keyToLib = " << key;
    return key;
}

void Shortcut::shortcutChangedSlot()
{
    qInfo() << "receive cloud service signal";
    shortcutUi->clearCustomShorcutFrame();
    initCustom();
}

