/*
 * 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 "shortcutline.h"

#include <QDebug>
#include <QKeySequence>
#include <unistd.h>
#include <QApplication>

#define SNULL  "NULL"
#define SCTRL  "Ctrl"
#define SALT   "Alt"
#define SSHIFT "Shift"

int allowKey[] = { // 0-9  -/+ A-Z ,/.
                Qt::Key_0,     Qt::Key_1,     Qt::Key_2,     Qt::Key_3,
                Qt::Key_4,     Qt::Key_5,     Qt::Key_6,     Qt::Key_7,
                Qt::Key_8,     Qt::Key_9,     Qt::Key_Minus, Qt::Key_Equal,
                Qt::Key_A,     Qt::Key_B,     Qt::Key_C,     Qt::Key_D,
                Qt::Key_E,     Qt::Key_F,     Qt::Key_G,     Qt::Key_H,
                Qt::Key_I,     Qt::Key_J,     Qt::Key_K,     Qt::Key_L,
                Qt::Key_M,     Qt::Key_N,     Qt::Key_O,     Qt::Key_P,
                Qt::Key_Q,     Qt::Key_R,     Qt::Key_S,     Qt::Key_T,
                Qt::Key_U,     Qt::Key_V,     Qt::Key_W,     Qt::Key_X,
                Qt::Key_Y,     Qt::Key_Z,     Qt::Key_Comma, Qt::Key_Period,
                Qt::Key_Print, Qt::Key_Escape, Qt::Key_Delete


};

int numKey[] = {
    Qt::Key_0,     Qt::Key_1,     Qt::Key_2,     Qt::Key_3,
    Qt::Key_4,     Qt::Key_5,     Qt::Key_6,     Qt::Key_7,
    Qt::Key_8,     Qt::Key_9,     Qt::Key_Minus, Qt::Key_Equal,
    Qt::Key_Period
};


ShortcutLine::ShortcutLine(QList<KeyEntry> *generalEntries,
                           QList<KeyEntry> *customEntries, QWidget *parent) :
                           QLineEdit(parent),
                           systemEntry(generalEntries),
                           customEntry(customEntries)
{
    initInterface();
    initInputKeyAndText(true);
}


ShortcutLine::~ShortcutLine()
{

}

void ShortcutLine::initInputKeyAndText(const bool &clearText)
{
    firstKey  = SNULL;
    secondKey = SNULL;
    thirdKey  = SNULL;
    forthKey  = SNULL;
    if (true == clearText) {
        this->setText("");
        shortCutObtainedFlag = false;
    }
}

void ShortcutLine::keyPressEvent(QKeyEvent *e)
{
    if (e->isAutoRepeat()) {  //一直按着导致触发的事件，不再处理
        return;
    }
    if (isShortCutObtained == true) {
        initInputKeyAndText(true);
        isShortCutObtained = false;
    }
    int keyValue = e->key();
    int keyCode = e->nativeVirtualKey();
//    qDebug()<<"0x"<<QString().sprintf("%04X",keyValue);
//    qDebug()<<"keyCode = = = = =  ="<<keyCode<<"    "<<keyValue;

    if (keyValue == Qt::Key_Meta && e->modifiers() != Qt::MetaModifier) { // bug#194489
        keyValue = Qt::Key_Alt;
        keyCode = 65513;
    }
    if (firstKey == SNULL) {
        firstKey = keyToString(keyValue);
        if (keyValue == Qt::Key_Control || keyValue == Qt::Key_Alt \
                || keyValue == Qt::Key_Shift || keyValue == Qt::Key_Meta || keyValue == Qt::Key_Print
                || keyValue == Qt::Key_Super_L) {
            if (keyValue == Qt::Key_Print) {
                this->setText(firstKey);
                shortCutObtained(true, 1);
            } else {
                this->setText(firstKey + QString("   "));
            }
        } else {                       //第一个键不是三个辅助键中的其中一个
            this->setText(firstKey);  //显示一下，增强用户交互性
            shortCutObtained(false);  //快捷键获取失败
            return;
        }
    } else if(secondKey == SNULL) {
        /*第二个键是辅助键中的另外一个*/
        if ((keyValue == Qt::Key_Control || keyValue == Qt::Key_Alt || \
             keyValue == Qt::Key_Shift || keyValue == Qt::Key_Meta) &&
                keyToString(keyValue) != firstKey) {
            secondKey = keyToString(keyValue);
            this->setText(firstKey + QString("   ") + secondKey + QString("   "));
        } else {  //第二个键是主键(最后一个键)
            if (lastKeyIsAvailable(keyValue, keyCode)) {   // 合法
                secondKey = keyToString(keyValue);
                shortCutObtained(true, 2);
            } else {                             //非法
                shortCutObtained(false);
                return;
            }
        }
    } else if (thirdKey == SNULL) {
        /*第三个键是辅助键中的另外一个*/
        if ((keyValue == Qt::Key_Control || keyValue == Qt::Key_Alt || \
             keyValue == Qt::Key_Shift || keyValue == Qt::Key_Meta) &&
                keyToString(keyValue) != firstKey) {
            thirdKey = keyToString(keyValue);
            this->setText(firstKey + QString("   ") + secondKey + QString("   ") + thirdKey + QString("   "));
        } else {  //第三个键是主键(最后一个键)
            if (lastKeyIsAvailable(keyValue, keyCode)) {   // 合法
                thirdKey = keyToString(keyValue);
                shortCutObtained(true, 3);
            } else {                             //非法
                shortCutObtained(false);
                return;
            }
        }

    } else if (forthKey == SNULL) { //第四个键只能是主键
        if (lastKeyIsAvailable(keyValue, keyCode)) {   // 合法
            forthKey = keyToString(keyValue);
            shortCutObtained(true, 4);
        } else {                             //非法
            shortCutObtained(false);
        }
    }

}

void ShortcutLine::keyReleaseEvent(QKeyEvent *e)
{
    if (e->isAutoRepeat()) {  //一直按着导致触发的事件
        return;
    }
    if (true == shortCutObtainedFlag) { //快捷键输入完毕
        initInputKeyAndText(false);
    } else {                           //快捷键输入放弃
        initInputKeyAndText(true);
    }
}

void ShortcutLine::focusInEvent(QFocusEvent *e)
{
    if (UkccCommon::isWayland() && mKglobalIfc->isValid()) mKglobalIfc->call("blockGlobalShortcuts", true);
    //establishGrab();
    this->grabKeyboard();
    QLineEdit::focusInEvent(e);
    initInputKeyAndText(false);
}

void ShortcutLine::focusOutEvent(QFocusEvent *e)
{
    if (UkccCommon::isWayland() && mKglobalIfc->isValid()) mKglobalIfc->call("blockGlobalShortcuts", false);
    //closeGrab();
    this->releaseKeyboard();
    QLineEdit::focusOutEvent(e);
}

void ShortcutLine::shortCutObtained(const bool &flag, const int &keyNum)
{
    isShortCutObtained = true;
    if (true == flag && (1 == keyNum || 2 == keyNum || 3 == keyNum || 4 == keyNum)) {
        shortCutObtainedFlag = true;
        if (1 == keyNum) {
            this->setText(firstKey);
            if (firstKey == "PrtSc") {
                firstKey = "Print";
            }
            seq = QKeySequence(firstKey);
        } else if (2 == keyNum) {
            this->setText(firstKey + QString("   ") + secondKey);
            if (secondKey == "PrtSc") {
                secondKey = "Print";
            }

            if (firstKey == "Win" || firstKey == "Start") {
                seq = QKeySequence("Meta" + QString("+") + secondKey);
            } else {
                seq = QKeySequence(firstKey + QString("+") + secondKey);
            }

        } else if (3 == keyNum) {
            this->setText(firstKey + QString("   ") + secondKey + QString("   ") + thirdKey);
            if (thirdKey == "PrtSc") {
                thirdKey = "Print";
            }
            if (firstKey == "Win") {
                seq = QKeySequence("Meta" + QString("+") + secondKey + QString("+") + thirdKey);
            } else if (secondKey == "Win") {
                seq = QKeySequence(firstKey + QString("+") + "Meta" + QString("+") + thirdKey);
            } else {
                seq = QKeySequence(firstKey + QString("+") + secondKey + QString("+") + thirdKey);
            }

        } else if (4 == keyNum){
            this->setText(firstKey + QString("   ") + secondKey + QString("   ") + thirdKey + QString("   ") + forthKey);
            if (forthKey == "PrtSc") {
                forthKey = "Print";
            }
            if (firstKey == "Win") {
                seq = QKeySequence("Meta" + QString("+") + secondKey + QString("+") + thirdKey + QString("+") + forthKey);
            } else if (secondKey == "Win") {
                seq = QKeySequence(firstKey + QString("+") + "Meta" + QString("+") + thirdKey + QString("+") + forthKey);
            } else if (thirdKey == "Win") {
                seq = QKeySequence(firstKey + QString("+") + secondKey + QString("+") + "Meta" + QString("+") + forthKey);
            } else {
                seq = QKeySequence(firstKey + QString("+") + secondKey + QString("+") + thirdKey + QString("+") + forthKey);
            }

        }

        if (conflictWithSystemShortcuts(seq) || conflictWithCustomShortcuts(seq)) { //快捷键冲突
            Q_EMIT shortCutAvailable(-2);
        } else if (conflictWithGlobalShortcuts(seq) || conflictWithStandardShortcuts(seq)) {
            shortCutObtainedFlag = false;
            initInputKeyAndText(true);
            Q_EMIT shortCutAvailable(-1);
        } else {
            Q_EMIT shortCutAvailable(0);
        }
    } else {  //快捷键无效
        conflictValue = conflictKey = firstKey;
        conflictValue = conflictValue.isEmpty() ? "Fn" : conflictValue;
        shortCutObtainedFlag = false;
        initInputKeyAndText(true);
        Q_EMIT shortCutAvailable(-1);
    }
}

bool ShortcutLine::lastKeyIsAvailable(const int &keyValue, const int &keyCode)
{

    for (u_int i = 0; i < sizeof(numKey) / sizeof(int); ++i) {
        if (keyValue == numKey[i] && keyValue != keyCode) {  //数字键盘上的
            return false;
        }
    }

    for (u_int i = 0; i < sizeof(allowKey) / sizeof(int); ++i) {
        if (keyValue == allowKey[i]) {
            return true;
        }
    }
    return false;
}

QKeySequence ShortcutLine::keySequence()
{
    return this->seq;
}

bool ShortcutLine::conflictWithGlobalShortcuts(const QKeySequence &keySequence)
{
    QHash<QKeySequence, QList<KGlobalShortcutInfo> > clashing;
    for (int i = 0; i < keySequence.count(); ++i) {
        QKeySequence keys(keySequence[i]);
        qDebug() << "全局快捷键冲突" << keySequence.count() << keySequence << keys;
        if (!KGlobalAccel::isGlobalShortcutAvailable(keySequence)) {
            clashing.insert(keySequence, KGlobalAccel::getGlobalShortcutsByKey(keys));
        }
    }

    if (clashing.isEmpty()) {
        return false;
    } else {
        qDebug() << "conflict With Global Shortcuts" << clashing[keySequence][0].friendlyName();;
        conflictValue = clashing[keySequence][0].friendlyName();
        conflictKey = keySequence.toString();
    }

    return true;
}

bool ShortcutLine::conflictWithStandardShortcuts(const QKeySequence &seq)
{
    KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(seq);
    if (ssc != KStandardShortcut::AccelNone) {
        qDebug() << "conflict With Standard Shortcuts" << seq;
        conflictValue = KStandardShortcut::label(ssc);
        conflictKey = seq.toString();
        return true;
    }
    return false;
}

bool ShortcutLine::conflictWithSystemShortcuts(const QKeySequence &seq)
{
    QString systemKeyStr = keyToLib(seq.toString());
    if (systemKeyStr.contains("Meta")) {
        systemKeyStr.replace("Meta", "Win");
    }
    if (systemKeyStr.contains("Start")) {
        systemKeyStr.replace("Start", "Win");
    }

    if (systemKeyStr.contains("PrtSc", Qt::CaseInsensitive)) {
        systemKeyStr.replace("PrtSc", "Print", Qt::CaseInsensitive);
    }
    for (KeyEntry ckeyEntry : *systemEntry) {
        QString ckeyString = ckeyEntry.valueStr;
        if (ckeyString.contains("Control", Qt::CaseInsensitive)) {
            ckeyString.replace("Control", "Ctrl", Qt::CaseInsensitive);
        }

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

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

        if (!systemKeyStr.compare(ckeyString, Qt::CaseInsensitive)) {
            qDebug() << "conflictWithSystemShortcuts" << seq;
            conflictValue.clear();
            conflictKey = ckeyEntry.keyStr;
            return true;
        }
    }

    return false;
}

bool ShortcutLine::conflictWithCustomShortcuts(const QKeySequence &seq)
{
    QString customKeyStr = keyToLib(seq.toString());
    if (customKeyStr.contains("Meta")) {
        customKeyStr.replace("Meta", "Win");
    }
    for (KeyEntry ckeyEntry : *customEntry) {
        QString ckeyString = ckeyEntry.bindingStr;
        if (ckeyString.contains("Control")) {
            ckeyString.replace("Control", "Ctrl");
        }
        if (ckeyString.contains("Meta")) {
            ckeyString.replace("Meta", "Win");
        }
        if (customKeyStr == ckeyString) {
            qDebug() << "conflictWithCustomShortcuts" << seq << ckeyEntry.keyStr;
            conflictValue.clear();
            conflictKey = ckeyEntry.actionStr;
            return true;
        }
    }
    return false;
}

QString ShortcutLine::keyToLib(QString key)
{
    if (key.contains("+")) {
        QStringList keys = key.split("+");
        if (keys.count() == 2) {
            QString lower = keys.at(1);
            QString keyToLib = "<" + keys.at(0) + ">" + lower.toLower();

            return keyToLib;
        } else if (keys.count() == 3) {
            QString lower = keys.at(2);
            QString keyToLib = "<" + keys.at(0) + ">" + "<" + keys.at(1) + ">" + lower.toLower();

            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();

            return keyToLib;
        }
    }
    return key;
}


void ShortcutLine::setKeySequence(QKeySequence setSeq){
    this->seq = setSeq;
}

void ShortcutLine::initInterface()
{
    mKglobalIfc = new QDBusInterface("org.kde.kglobalaccel",
                                     "/kglobalaccel",
                                     "org.kde.KGlobalAccel",
                                     QDBusConnection::sessionBus(),
                                     this);
}

QString ShortcutLine::keyToString(int keyValue)
{
    QString keyValue_QT_KEY;//表示意义

    //键盘上大部分键值对应的都是其表示的ASCII码值
    keyValue_QT_KEY = QString(keyValue);
    if (keyValue == 0) keyValue_QT_KEY = "Fn";

    //对于特殊意义的键值[无法用ASCII码展示]
    switch (keyValue)
    {
    case Qt::Key_Escape:
        keyValue_QT_KEY = QString("Esc");
        break;
    case Qt::Key_Tab:
        keyValue_QT_KEY = QString("Tab");
        break;
    case Qt::Key_CapsLock:
        keyValue_QT_KEY = QString("CapsLock");
        break;
    case Qt::Key_Shift:
        keyValue_QT_KEY = QString(SSHIFT);
        break;
    case Qt::Key_Control:
        keyValue_QT_KEY = QString(SCTRL);
        break;
    case Qt::Key_Alt:
        keyValue_QT_KEY = QString(SALT);
        break;
    case Qt::Key_Backspace:
        keyValue_QT_KEY = QString("Backspace");
        break;
    case Qt::Key_Meta:
        keyValue_QT_KEY = QString("Win");
        break;
    case Qt::Key_Return:
        keyValue_QT_KEY = QString("Enter(main)");
        break;
    case Qt::Key_Enter:
        keyValue_QT_KEY = QString("Enter(num)");
        break;
    case Qt::Key_Home:
        keyValue_QT_KEY = QString("Home");
        break;
    case Qt::Key_End:
        keyValue_QT_KEY = QString("End");
        break;
    case Qt::Key_PageUp:
        keyValue_QT_KEY = QString("PageUp");
        break;
    case Qt::Key_PageDown:
        keyValue_QT_KEY = QString("PageDown");
        break;
    case Qt::Key_Insert:
        keyValue_QT_KEY = QString("Insert");
        break;
    case Qt::Key_Up:
        keyValue_QT_KEY = QString::fromLocal8Bit("↑");
        break;
    case Qt::Key_Right:
        keyValue_QT_KEY = QString::fromLocal8Bit("→");
        break;
    case Qt::Key_Left:
        keyValue_QT_KEY = QString::fromLocal8Bit("←");
        break;
    case Qt::Key_Down:
        keyValue_QT_KEY = QString::fromLocal8Bit("↓");
        break;
    case Qt::Key_Delete:
        keyValue_QT_KEY = QString("Del");
        break;
    case Qt::Key_Space:
        keyValue_QT_KEY = QString("Space");
        break;
    case Qt::Key_F1:
        keyValue_QT_KEY = QString("F1");
        break;
    case Qt::Key_F2:
        keyValue_QT_KEY = QString("F2");
        break;
    case Qt::Key_F3:
        keyValue_QT_KEY = QString("F3");
        break;
    case Qt::Key_F4:
        keyValue_QT_KEY = QString("F4");
        break;
    case Qt::Key_F5:
        keyValue_QT_KEY = QString("F5");
        break;
    case Qt::Key_F6:
        keyValue_QT_KEY = QString("F6");
        break;
    case Qt::Key_F7:
        keyValue_QT_KEY = QString("F7");
        break;
    case Qt::Key_F8:
        keyValue_QT_KEY = QString("F8");
        break;
    case Qt::Key_F9:
        keyValue_QT_KEY = QString("F9");
        break;
    case Qt::Key_F10:
        keyValue_QT_KEY = QString("F10");
        break;
    case Qt::Key_F11:
        keyValue_QT_KEY = QString("F11");
        break;
    case Qt::Key_F12:
        keyValue_QT_KEY = QString("F12");
        break;
    case Qt::Key_NumLock:
        keyValue_QT_KEY = QString("NumLock");
        break;
    case Qt::Key_ScrollLock:
        keyValue_QT_KEY = QString("ScrollLock");
        break;
    case Qt::Key_Pause:
        keyValue_QT_KEY = QString("Pause");
        break;
    case Qt::Key_Print:
        keyValue_QT_KEY = QString("PrtSc");
        break;
    case Qt::Key_Super_L:
        keyValue_QT_KEY = QString("Win");
        break;
    case Qt::Key::Key_WakeUp:
        keyValue_QT_KEY = QString("Fn");
        break;
    default:
        keyValue_QT_KEY = QKeySequence(keyValue).toString();
        break;
    }

    return keyValue_QT_KEY;
}
