/*
 * Copyright (C) 2020, 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 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, see <https://www.gnu.org/licenses/>.
 */
#include "systemchat.h"


#include <list>
#include <algorithm>
#include <iostream>

bool is_reply_end = true;
static QString imageUrl = "";

static SystemChat *g_nlpChat = nullptr;

//应答消息格式
//{
//    "message_type": "chat",
//    "content": [
//        [
//            {"type": "text", "text": { "sentence_id": 1, "is_end": true, "result": "" }},
//            {"type": "file_data", "file_data": "image/png;base64,base64_image"}
//        ]
//    ],
//    "error_code": 1,
//    "error_message": "error message",
//    "model":"AI助手"
//}

//回调函数处理chat返回的消息
static void chatCallback(const std::string text_data)
{
    SystemChat *systemChat = SystemChat::getPtr();

    // 每当收到新消息后，需要重新启动定时器。由于定时器是在主线程创建的，启动定时器也需要在主线程执行，这里发信号给主线程处理。
    Q_EMIT systemChat->sigRestartTimeoutTimer();

    const char *result = text_data.data();


    //处理JSON数据
    QJsonParseError jsonError;
    QJsonDocument doc = QJsonDocument::fromJson(result, &jsonError);
    if(jsonError.error != QJsonParseError::NoError)
    {
        KyInfo() << "JSON parsing error: " << jsonError.errorString();
        return;
    }

    QJsonObject obj = doc.object();

    int errorCode = obj.value("error_code").toInt();
    QString errorMsg = obj.value("error_message").toString();
    QString messageType = obj.value("message_type").toString();
    QString model = obj.value("model").toString();
    QString feedbackURL = "";
    if(obj.contains("feedback_url")) {
        feedbackURL = obj.value("feedback_url").toString();
    }

    if(model == ""){
        KyInfo() << "model is empty";
        model = "ai";
    }

    QJsonArray contentArray = obj["content"].toArray();
    QJsonArray infoSourceArray;
    QMap<int, QString> toolMap;
    QMap<int, QString> infoSourceMap;
    QString fileData = "";
    QString schedule_date = "";


    if(messageType == "tool_call"){

        // 遍历content数组,取出tool_id tool_name
        for(const QJsonValue &value: contentArray)
        {
            QJsonObject itemObject = value.toObject();

            if(itemObject["type"].toString() == "text")
            {
                systemChat->text = itemObject["text"].toString();
            }
            // 检查是否包含tool_id和tool_name
            if(itemObject.contains("tool_id") && itemObject.contains("tool_name"))
            {
                int toolId = itemObject["tool_id"].toInt();
                QString toolName = itemObject["tool_name"].toString();
                toolMap.insert(toolId, toolName);

                //21 新建日程   22 生成待办
                if (toolId == 21 || toolId == 22){
                    QJsonObject para = itemObject["tool_description"].toObject();
                    schedule_date = para["parameter"].toString();
                }
            }
        }

        for(auto i = toolMap.begin(); i != toolMap.end(); ++i)
        {
            KyInfo() << "toolid:" << i.key() << "toolName:" << i.value();
        }

        QString fileType = obj.value("file_type").toString();
        if(fileType == "image")
        {
            //错误处理
            if(errorCode != 0)
            {
                Q_EMIT systemChat->toolReply(errorMsg, toolMap, imageUrl, errorCode, model, feedbackURL);
            }
            else
            {
                Q_EMIT systemChat->toolReply(systemChat->text, toolMap, imageUrl, errorCode, model, feedbackURL);
                KyInfo() << "________________emit toolReply success";
            }
            imageUrl = "";
        }
        else if (fileType == "document") {

        }
        else if (fileType == "schedule") {
            if (toolMap.contains(21)) {
                if (errorCode != 0) {
                    Q_EMIT systemChat->reply("", errorMsg, true, true, 1, errorCode, model, feedbackURL);

                    return;
                }
                Q_EMIT systemChat->sigAddScheduleDate(21, schedule_date, "", model, feedbackURL);
            }
            else if (toolMap.contains(22)) {
                if(errorCode != 0)
                {
                    Q_EMIT systemChat->reply("", errorMsg, true, true, 1, errorCode, model, feedbackURL);

                    return;
                }
                Q_EMIT systemChat->sigAddScheduleDate(22, schedule_date, systemChat->text, model, feedbackURL);
            }
        }
    }
    else if(messageType == "image_generation")
    {
        QJsonValue itemObject = contentArray.at(0);
        QString status = itemObject["status"].toString();
        if(status == "generating")
        {
            //向前端发送生成中
            Q_EMIT systemChat->sigGeneratingPic(model, feedbackURL);

        }
        else if(status == "generated")
        {
            if(itemObject["type"].toString() == "file_data")
            {
                fileData =itemObject["file_data"].toString();
            }
            //向前端发送生成的图片
            if(fileData != "")
            {
                Q_EMIT systemChat->sigPictureUpdate(fileData, errorMsg);
            }
        }
        else if(status == "canceled")
        {
            Q_EMIT systemChat->sigPictureUpdate("", "");

        }
        else  //status == failed
        {
            if(errorCode != 0)
            {
                //21 未配置模型错误，未配置模型时，不显示生成失败图片
                if(errorCode != 21){
                    //文生图失败时，向前端发送生成失败的图片
                    Q_EMIT systemChat->sigPictureUpdate(fileData, "failed");
                }
                Q_EMIT systemChat->reply("", errorMsg, true, true, 1, errorCode, model, feedbackURL);


            }
        }
    }
    else // messageType == "chat"
    {
        // 遍历content数组
        for(const QJsonValue &value: contentArray)
        {
            // 假设每个元素都是一个对象
            QJsonObject itemObject = value.toObject();

            KyInfo() << "文本类回答-chat-" << itemObject;
            // 检查type是否为"text"
            if(itemObject["type"].toString() == "text")
            {
                // 获取text子对象
                QJsonObject textObject = itemObject["text"].toObject();
                systemChat->is_end = textObject["is_end"].toBool();
                systemChat->result_msg = textObject["result"].toString();
                systemChat->result_index = obj.value("sentence_id").toInt();
                systemChat->reasoningContentMsg = textObject["reasoning_content"].toString();
                if(systemChat->reasoningContentMsg == ""){
                    systemChat->is_reasoning_end = true;
                }else{
                    systemChat->is_reasoning_end = false;
                }
//                if(systemChat->is_end){
//                    systemChat->is_reasoning_end = true;
//                }
                //systemChat->reasoningContentMsg = textObject["result"].toString();
            }

            if(itemObject["type"].toString() == "file_data")
            {
                fileData = itemObject["file_data"].toString();
            }
        }

        // 检查是否包含information_sources字段
        if(!obj.contains("information_sources"))
        {
//            KyInfo() << "No information sources";
        }
        else if(obj.value("information_sources").isNull())
        {
            KyInfo() << "information_sources is null";
        }
        else if (systemChat->is_end == true)
        {
            infoSourceArray = obj["information_sources"].toArray();
        }

        // 遍历information-sources数组
        for(const QJsonValue &value: infoSourceArray)
        {
            QJsonObject itemObject = value.toObject();
            // 检查是否包含text和tool_id
            if(itemObject.contains("text") && itemObject.contains("tool_id"))
            {
                int toolId = itemObject["tool_id"].toInt();
                QString sourceName = itemObject["text"].toString();
                infoSourceMap.insert(toolId, sourceName);
            }

        }
        for(auto i = infoSourceMap.begin(); i != infoSourceMap.end(); ++i)
        {
            KyInfo() << "information-sources: "
                     << "toolid:" << i.key() << "sourceName:" << i.value();
        }

        is_reply_end = systemChat->is_end;

        QJsonValue itemObject = contentArray.at(0);
        QString status = itemObject["status"].toString();
        switch(systemChat->target)
        {
        case TartgetType::RECVMSG:
            //错误处理
            if(errorCode != 0)
            {
                Q_EMIT systemChat->reply("", errorMsg, true, true, 1, errorCode, model, feedbackURL);
                break;
            }
            if(status != ""){
                if(status == "generating")
                {
                    //向前端发送生成中
                    Q_EMIT systemChat->sigGeneratingPic(model, feedbackURL);

                }
                else if(status == "generated")
                {
                    if(itemObject["type"].toString() == "file_data")
                    {
                        fileData =itemObject["file_data"].toString();
                    }
                    //向前端发送生成的图片
                    if(fileData != "")
                    {
                        Q_EMIT systemChat->pictureOperateUpdate(fileData, errorMsg);
                    }
                }
                else  //status == failed
                {
                    if(errorCode != 0)
                    {
                        Q_EMIT systemChat->reply("", errorMsg, true, true, 1, errorCode, model, feedbackURL);
                    }
                }
            }else{
                Q_EMIT systemChat->reply(systemChat->reasoningContentMsg, systemChat->result_msg, systemChat->is_reasoning_end, systemChat->is_end, systemChat->result_index, errorCode, model, feedbackURL);
            }

            if(obj.contains("information_sources") && !obj.value("information_sources").isNull() && systemChat->is_end == true)
            {
                Q_EMIT systemChat->sigInfoSourceUpdate(infoSourceMap);
            }
            break;
        case TartgetType::SUMMARY:
            Q_EMIT systemChat->sigGenerateSummary(systemChat->result_msg, systemChat->is_end, systemChat->result_index, errorCode, model);
            break;
        }
    }
}

void SystemChat::run()
{
    QEventLoop loop;

    initSystemChat();

    loop.exec();

    return;
}

SystemChat::SystemChat(QObject *parent)
    : QThread (parent)
{
    ChatDatabase *database = ChatDatabase::getChatDatabase();
    int history_id_database = database->selectMaxHistoryId();
    int history_id_settings  = g_configManager->getConfigValue("history-id").toInt();
    m_sessionID = history_id_database >= history_id_settings ? history_id_database : history_id_settings;

    m_features = new Features(0);

    mTimeoutTimer = new QTimer(this);
    if (mTimeoutTimer == nullptr) {
        KyInfo() << "Cannot create timer!";
    } else {
        mTimeoutTimer->setInterval(TIMEOUT_TIMER_INTERVAL); // 设置15秒间隔
        mTimeoutTimer->setSingleShot(false); // 设置为持续触发
        QObject::connect(mTimeoutTimer, &QTimer::timeout, [=](){
            KyInfo() << "Timeout timer triggered at " << QTime::currentTime();
            Q_EMIT this->sigChatTimeout();
        });

    }

    start();
}

SystemChat::~SystemChat()
{
}

void SystemChat::setActor(int actorId)
{
    m_osassistant->setPromptId(actorId);
}

std::vector<Prompt> SystemChat::getPrompts()
{
    m_promptList.clear();
//    m_promptList = m_osassistant->prompts();
//    for (const auto& prompt : m_promptList) {
//        KyInfo() << "========================";
//        KyInfo() << "Prompt ID:" << prompt.id;
//        KyInfo() << "Prompt Name:" << QString::fromStdString(prompt.name);
//        KyInfo() << "Prompt Content:" << QString::fromStdString(prompt.content);
//        KyInfo() << "Prompt promptType:" << prompt.type;
//        KyInfo() << "Prompt isActive:" << prompt.active;
//        KyInfo() << "========================";
//    }

    return m_promptList;
}

bool SystemChat::setCommandOrder(QList<CommandTemplateItem> commandTemplateList)
{
    std::list<int> promptIdList;

    for (const auto& item : commandTemplateList) {
        promptIdList.push_back(item.actorId);  // 按新顺序添加 promptId
    }

    KyInfo() << "promptIdList:" << promptIdList;

    // 确保列表不为空
    if (promptIdList.empty()) {
        KyInfo() << "错误：promptIdList 为空，无法更新顺序";
        return false;
    }

    // 检查是否包含负数等无效ID
    bool hasInvalidId = std::any_of(promptIdList.begin(), promptIdList.end(), [](int id) { return id <= 0; });
    if (hasInvalidId) {
        KyInfo() << "错误：promptIdList 包含无效 ID，无法更新顺序";
        return false;
    }

    bool ret = m_osassistant->setPromptOrder(promptIdList);  // 更新顺序到 SDK

//    if (ret) {
//        // 更新后的 prompts 列表日志输出
//        std::vector<Prompt> prompts = getPrompts();

//        KyInfo() << "========================";
//        for (const auto& prompt : prompts) {
//            KyInfo() << "Prompt ID:" << prompt.id;
//        }
//        KyInfo() << "========================";
//    }

    return ret;
}


int SystemChat::createCommand(QString name, QString content)
{
    KyInfo() << "create prompt" << "name" << name << "content" << content;
    int id = m_osassistant->createPrompt(name.toStdString(), content.toStdString());
    return id;
}

bool SystemChat::updateCommand(int actorId, QString name, QString content)
{
    bool ret = m_osassistant->updatePrompt(actorId, name.toStdString(), content.toStdString());
    return ret;
}

bool SystemChat::deleteCommand(int actorId)
{
    bool ret = m_osassistant->deletePrompt(actorId);
    return ret;
}

void SystemChat::stopChat()
{
    m_osassistant->stopChat();
}

void SystemChat::clearContext()
{
    m_osassistant->clearContext();
}

SystemChat *SystemChat::getPtr()
{
    if (g_nlpChat == nullptr) {
        g_nlpChat = new SystemChat;
    }

    return g_nlpChat;
}

void SystemChat::initSystemChat()
{
    m_osassistant = new kyai::assistant::OsAssistant();
    m_osassistant->setChatAsyncCallback(chatCallback);

    KyInfo() << "*** start initSystemChat m_osassistant->init()";

    Error error = m_osassistant->init();
    //SDK ErrorCode 内部定义冲突
    if(error.code != kyai::assistant::ErrorCode::Ok)
    {
        KyInfo() << "init systemChat failed";
        // 初始化失败不发送信号
        // Q_EMIT sigInitCompleted();
        return;
    }

    KyInfo() << "*** end initSystemChat m_osassistant->init()";

    m_promptList = m_osassistant->prompts();

    *m_features = m_osassistant->supportedFeatures();

    Q_EMIT sigInitCompleted();

    //TEMP-多线程初始化调用测试
//    Error error;
//    QFuture<Error> future =  QtConcurrent::run([&](){
//        QCoreApplication::processEvents();
//        m_osassistant = new kyai::assistant::OsAssistant();
//        m_osassistant->setChatAsyncCallback(chatCallback);
//        return m_osassistant->init();
//    });
//    QFutureWatcher<Error>* watcher = new QFutureWatcher<Error>(this);
//    connect(watcher, &QFutureWatcher<Error>::finished, this, [&]() {
//              error = future.result();
//           });
//   //SDK ErrorCode 内部定义冲突
//   if(error.code != kyai::assistant::ErrorCode::Ok)
//   {
//       KyInfo() << "init systemChat failed";
//       return;
//   }



}

void SystemChat::initWithHistory(std::vector<Message> messages)
{
    Error error = m_osassistant->initWithChatHistory(messages);
    if(error.code != kyai::assistant::ErrorCode::Ok){
        KyInfo() << "init with history failed.";
    }
}

void SystemChat::sendMessage(QString text, QString filePath)
{

    // 创建顶层对象
    QJsonObject jsonObject;
    // 设置message_type字段
    jsonObject["message_type"] = "chat";
    // 创建content数组
    QJsonArray contentArray;

    // 文本消息
    if(text.length() > 0)
    {
        QJsonObject textMessage;
        textMessage["type"] = "text";
        textMessage["text"] = text;
        contentArray.append(textMessage);
    }

    if(filePath.length() > 0)
    {
        //添加文件路径
        FileType type = judgeFileType(filePath);
        if(type == IMAGE)
        {
            // 图片URL消息
            QJsonObject imageUrlMessage;
            imageUrlMessage["type"] = "image_url";
            imageUrlMessage["image_url"] = filePath;
            contentArray.append(imageUrlMessage);
            imageUrl = filePath;
        }
        else if(type == DOCUMENT)
        {
            // 文档URL消息
            QJsonObject documentUrlMessage;
            documentUrlMessage["type"] = "document_url";
            documentUrlMessage["document_url"] = filePath;
            contentArray.append(documentUrlMessage);
        }
        else
        {
        }
    }
    // 将content数组设置到顶层对象
    jsonObject["content"] = contentArray;
    QJsonDocument jsonDoc(jsonObject);
    m_osassistant->chatAsync(jsonDoc.toJson().toStdString());

    // 发送新的消息后，需要开启超时定时器，超时的时候告诉前端UI“网络不稳定，仍在尝试”
    startTimeoutTimer();
}

void SystemChat::sendToolMessage(int toolId, FileType fileType, QString para)
{
    QJsonObject jsonObject;
    // message_type字段
    jsonObject["message_type"] = "tool_call";
    // file_type字段
    QString type = "";
    switch (fileType){
        case IMAGE:
            type = "image";
            break;
        case DOCUMENT:
            type = "document";
            break;
        case SOURCE:
            type = "source_url";
            break;
        case SCHEDULE:
            type = "schedule";
            break;
    }
    jsonObject["file_type"] = type;

    // content数组
    QJsonArray contentArray;

    // 调用的工具
    QJsonObject textMessage;
    textMessage["type"] = "tool_id";
    textMessage["tool_id"] = toolId;
    contentArray.append(textMessage);

    if(fileType == IMAGE) {
        // 图片URL
        QJsonObject imageUrlMessage;
        imageUrlMessage["type"] = "image_url";
        imageUrlMessage["image_url"] = para;
        contentArray.append(imageUrlMessage);
    }
    else if(fileType == DOCUMENT) {
        // 文件URL
        QJsonObject imageUrlMessage;
        imageUrlMessage["type"] = "image_url";
        imageUrlMessage["image_url"] = para;
        contentArray.append(imageUrlMessage);
    }
    else if (fileType == SCHEDULE) {
        QJsonObject scheduleMessage;
        scheduleMessage["type"] = "schedule_date";
        scheduleMessage["schedule_date"] = para;
        contentArray.append(scheduleMessage);
    }
    else {
        QJsonObject imageUrlMessage;
        contentArray.append(imageUrlMessage);
    }

    jsonObject["content"] = contentArray;
    QJsonDocument jsonDoc(jsonObject);
    m_osassistant->chatAsync(jsonDoc.toJson().toStdString());

    // 发送新的消息后，需要开启超时定时器，超时的时候告诉前端UI“网络不稳定，仍在尝试”
    startTimeoutTimer();
}

void SystemChat::setSession(int sessionID)
{
    m_sessionID = sessionID;
}

int SystemChat::getSession()
{
    return m_sessionID;
}

bool SystemChat::getFeatureStatus(Features::Feature feature)
{
    KyInfo() << "init getFeatureStatus";

    bool isSupported = m_features->containsFeature(feature);

    KyInfo() << "end getFeatureStatus";

    return isSupported;
}

QList<ModelItem *> SystemChat::getNLPModelList()
{
    std::string t1 = m_osassistant->getConfiguredNlpModelInfos();
    const char *ret = t1.data();
    if (ret == ""){
        KyInfo() << "getNLPModelList Failed！";
        return {};
    }

    QList<ModelItem *> result;
    // 解析JSON文档
    QJsonParseError parseError;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(ret, &parseError);

    if (parseError.error != QJsonParseError::NoError) {
        qDebug() << "JSON解析错误:" << parseError.errorString();
        qDebug() << "origin text: " << ret;
        return result;
    }

    // 获取根对象
    if (!jsonDoc.isObject()) {
        qDebug() << "JSON文档不是一个对象";
        return result;
    }

    QJsonObject rootObj = jsonDoc.object();

    // 提取模型信息数组
    if (!rootObj.contains("currentModel")) {
        qDebug() << "缺少currentModel字段";
        return result;
    }
    ModelItem *first = new ModelItem;
    first->m_modelName = tr("Intelligent Mode");
    first->m_description = tr("Intelligently invoke the system's default model configuration");
    first->m_isChecked = false;
    QString curModelName = rootObj["currentModel"].toString();
    qDebug() << "************* cur model:" << curModelName;
    if(curModelName == ""){
       first->m_isChecked = true;
    }
    result.append(first);


    // 提取模型信息数组
    if (!rootObj.contains("modelInfos") || !rootObj["modelInfos"].isArray()) {
        qDebug() << "缺少modelInfos数组或格式错误";
        return result;
    }

    QJsonArray modelArray = rootObj["modelInfos"].toArray();

    // 遍历模型信息数组
    for (const auto& modelValue : modelArray) {
        if (!modelValue.isObject()) {
            qDebug() << "模型信息不是一个对象";
            continue;
        }

        QJsonObject modelObj = modelValue.toObject();

        // 提取每个模型的信息
        ModelItem *info = new ModelItem;

        if (modelObj.contains("modelName") && modelObj["modelName"].isString()) {
            info->m_modelName = modelObj["modelName"].toString();
            KyInfo() << "info->m_modelName " << info->m_modelName;
        }

        if (modelObj.contains("deployType") && modelObj["deployType"].isDouble()) {
            int num = modelObj["deployType"].toInt();
            switch (num) {
            case 0:
                info->m_description = tr("Local Model");
                break;
            case 1:
                info->m_description = tr("Cloud Model");
                break;
            case 2:
                info->m_description = tr("Optional Model");
                break;
            default:
                break;
            }
        }
        if (info->m_modelName == curModelName){
            info->m_isChecked = true;
        }

        result.append(info);
    }

    return result;
}

QString SystemChat::getNLPCurModel()
{
    QString res = g_configManager->getConfigValue("cur-nlp-model").toString();
    if (res == ""){
        res = tr("Intelligent Mode");
    }

    return res;
}

void SystemChat::setNLPModel(QString name)
{
    m_osassistant->setCurrentNlpModel(name.toStdString());
    Q_EMIT sigNLPModelChanged();
}

FileType SystemChat::judgeFileType(QString filePath)
{
    //当前支持的图片格式
    QStringList fileFormats = { "txt","pdf","docx","pptx" };
    QStringList imageFormats = { "bmp", "jpe", "jpeg", "jpg", "pbm", "pgm", "png",  "ppm", "tiff", "webp" };
    //判断是否为支持的图片格式
    if(imageFormats.contains(QFileInfo(filePath).suffix().toLower()))
    {
        return IMAGE;
    }
    else if(fileFormats.contains(QFileInfo(filePath).suffix().toLower()))
    {
        return DOCUMENT;
    }
    else
    {
        return ERROR;
    }
}

bool SystemChat::isModelExisted(QString modeName)
{
    std::string t1 = m_osassistant->getConfiguredNlpModelInfos();
    const char *ret = t1.data();
    if (t1.empty()){
        KyInfo() << "getNLPModelList Failed！";
        return false;
    }

    // 解析JSON文档
    QJsonParseError parseError;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(ret, &parseError);

    if (parseError.error != QJsonParseError::NoError) {
        KyInfo() << "JSON解析错误:" << parseError.errorString();
        KyInfo() << "origin text: " << ret;
        return false;
    }

    // 获取根对象
    if (!jsonDoc.isObject()) {
        KyInfo() << "JSON文档不是一个对象";
        return false;
    }

    QJsonObject rootObj = jsonDoc.object();

    // 提取模型信息数组
    if (!rootObj.contains("currentModel")) {
        KyInfo() << "缺少currentModel字段";
        return false;
    }


    // 提取模型信息数组
    if (!rootObj.contains("modelInfos") || !rootObj["modelInfos"].isArray()) {
        KyInfo() << "缺少modelInfos数组或格式错误";
        return false;
    }

    QJsonArray modelArray = rootObj["modelInfos"].toArray();

    // 遍历模型信息数组
    bool isExisted = false;
    for (const auto& modelValue : modelArray) {
        if (!modelValue.isObject()) {
            KyInfo() << "模型信息不是一个对象";
            continue;
        }

        QJsonObject modelObj = modelValue.toObject();

        // 提取每个模型的信息
        if (modelObj.contains("modelName") && modelObj["modelName"].isString()) {
            if(modelObj["modelName"].toString() == modeName) {
                isExisted = true;
                break;
            }
        }
    }

    return isExisted;
}

void SystemChat::startTimeoutTimer()
{
    if(mTimeoutTimer == nullptr) {
        KyInfo() << "Chat timeout timer is not inited.";
        return;
    }

    KyInfo() << "Chat timeout timer is started.";

    mTimeoutTimer->start();

    return;
}

void SystemChat::restartTimeoutTimer()
{
    if(mTimeoutTimer == nullptr) {
        KyInfo() << "Chat timeout timer is not inited.";
        return;
    }

    mTimeoutTimer->stop();

    mTimeoutTimer->start();

    return;
}

void SystemChat::stopTimeoutTimer()
{
    if(mTimeoutTimer == nullptr) {
        KyInfo() << "Chat timeout timer is not inited.";
        return;
    }

    KyInfo() << "Chat timeout timer is stopped.";

    mTimeoutTimer->stop();

    return;
}
