// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "desktopappworker.h"
#include "global/builtinsearch.h"
#include "global/searchhelper.h"

#include <QLoggingCategory>

Q_DECLARE_LOGGING_CATEGORY(logDaemon)

using namespace GrandSearch;

DesktopAppWorker::DesktopAppWorker(const QString &name, QObject *parent)
    : ProxyWorker(name, parent)
{
    qCDebug(logDaemon) << "DesktopAppWorker constructor - Name:" << name;
}

void DesktopAppWorker::setContext(const QString &context)
{
    if (context.isEmpty())
        qCWarning(logDaemon) << "Search key is empty";
    m_context = buildKeyword(context);
    qCDebug(logDaemon) << "Built search keyword:" << m_context;
}

bool DesktopAppWorker::isAsync() const
{
    // 同步搜索，需分配线程资源
    return false;
}

bool DesktopAppWorker::working(void *context)
{
    // 准备状态切运行中，否则直接返回
    if (!m_status.testAndSetRelease(Ready, Runing))
        return false;

    Q_UNUSED(context);
    if (m_context.isEmpty() || m_indexTable.isEmpty()) {
        qCDebug(logDaemon) << "Search context or index table is empty, completing search";
        m_status.storeRelease(Completed);
        return true;
    }

    // 计时
    QElapsedTimer time;
    time.start();
    int lastEmit = 0;

    // 遍历查找
    QHash<MatchedItem *, bool> found;
    for (auto iter = m_indexTable.begin(); iter != m_indexTable.end(); ++iter) {
        // 中断
        if (m_status.loadAcquire() != Runing)
            return false;

        // 匹配
        QRegularExpression regExp(m_context, QRegularExpression::CaseInsensitiveOption);
        if (iter.key().contains(regExp)) {
            // 遍历该关键词匹配的项目检查是否已经被添加
            for (const QSharedPointer<MatchedItem> &item : iter.value()) {
                // 检查是否已经添加过
                if (found.contains(item.data()))
                    continue;

                // 中断
                if (m_status.loadAcquire() != Runing)
                    return false;

                // 加入搜索结果中
                {
                    QMutexLocker lk(&m_mtx);
                    m_items.append(*item);
                    found.insert(item.data(), true);

                    // 数据从无到有发送通知
                    // 50ms推送一次
                    int cur = time.elapsed();
                    if ((cur - lastEmit) > 50) {
                        lastEmit = cur;
                        qCDebug(logDaemon) << "New results found - Time elapsed:" << cur << "ms";

                        // 发送信号前必须解锁
                        lk.unlock();
                        emit unearthed(this);
                    }
                }
            }
        }
    }

    m_status.storeRelease(Completed);

    int leave = 0;
    {
        QMutexLocker lk(&m_mtx);
        leave = m_items.size();
    }

    qCInfo(logDaemon) << "Search completed - Found items:" << found.size()
                      << "Total time:" << time.elapsed() << "ms"
                      << "Current items:" << leave;

    // 还有数据再发一次
    if (leave > 0)
        emit unearthed(this);

    return true;
}

void DesktopAppWorker::terminate()
{
    m_status.storeRelease(Terminated);
}

ProxyWorker::Status DesktopAppWorker::status()
{
    return static_cast<ProxyWorker::Status>(m_status.loadAcquire());
}

bool DesktopAppWorker::hasItem() const
{
    QMutexLocker lk(&m_mtx);
    return !m_items.isEmpty();
}

MatchedItemMap DesktopAppWorker::takeAll()
{
    QMutexLocker lk(&m_mtx);
    MatchedItems items = std::move(m_items);

    Q_ASSERT(m_items.isEmpty());
    lk.unlock();

    // 添加分组
    MatchedItemMap ret;
    ret.insert(group(), items);

    return ret;
}

void DesktopAppWorker::setIndexTable(const QHash<QString, QList<QSharedPointer<MatchedItem>>> &table)
{
    qCDebug(logDaemon) << "Setting index table - Entry count:" << table.size();
    // 搜索中不允许设置索引表, 已有数据后不允许更新
    if (m_status.loadAcquire() == Runing || !m_indexTable.isEmpty())
        return;

    m_indexTable = table;
}

QString DesktopAppWorker::group() const
{
    return GRANDSEARCH_GROUP_APP;
}

QString DesktopAppWorker::buildKeyword(const QString &keyword)
{
    QJsonParseError error;
    QJsonDocument doc = QJsonDocument::fromJson(keyword.toLocal8Bit(), &error);
    if (error.error != QJsonParseError::NoError || doc.isEmpty())
        return searchHelper->tropeInputSymbol(keyword);

    // 应用搜索类目，只需要获取关键字信息
    QStringList keywordList;
    QJsonObject obj = doc.object();
    QJsonArray arr = obj[JSON_KEYWORD_ATTRIBUTE].toArray();
    for (int i = 0; i < arr.count(); ++i) {
        const QString &key = arr[i].toString();
        if (key.isEmpty())
            continue;
        keywordList.append(searchHelper->tropeInputSymbol(key));
    }

    // 搜索所有应用
    if (keywordList.isEmpty())
        return ".*";

    return keywordList.join('|');
}
