//
// Created by Mr.V on 2024/4/15.
//

#include "ReadComThread.h"

ReadComThread::ReadComThread(MQueue<TrainInfo> *queueTrainInfo,
                             QObject *parent) :
        QThread(parent)
{
    // 配置文件读取
    QString errorMessage = "";
    if (!ConfigUtil::readBaseConfig(g_config_path, errorMessage, this->baseConfig_))
    {
        this->ErrorSignals(errorMessage);
    }
    if (!ConfigUtil::readSocketServerConfig(g_config_path, errorMessage, this->socketServerConfig_))
    {
        this->ErrorSignals(errorMessage);
    }

    this->queueTrainInfo_ = queueTrainInfo;

    this->logRfidRecvName = "./Logs/rfid_data.txt";
    this->recvLog.setFileName(logRfidRecvName);
    this->recvLog.open(QIODevice::ReadWrite | QIODevice::Append);

    timer_.setSingleShot(true);
    connect(&timer_, &QTimer::timeout, this, &ReadComThread::tryToReconnect);

    connect(this, &ReadComThread::getRfid_signals, this, &ReadComThread::getQueueDataThread);
    connect(this, &ReadComThread::IdentifyType, this, &ReadComThread::IdentifyTypeUpdate);
    connect(this, &ReadComThread::upRfid_signals, this, &ReadComThread::upRfid);

    if (this->baseConfig_.useSocketServer)
    {
        // 创建线程
        QThread *threadWork = new QThread;   // 任务线程

        this->tcpClient = new TcpClient(this);
        this->tcpClient->moveToThread(threadWork);

        connect(this, &ReadComThread::connect_socket, this->tcpClient, &TcpClient::connectToServer);

        // 子线程反馈 socket 到主线程
        connect(this->tcpClient, &TcpClient::socketComplete, this, [=](QTcpSocket* tcp, QString ip, quint16 port){
            this->tcp_ = tcp;
        });

        // 接收子线程的消息提示
        connect(this->tcpClient, &TcpClient::sendTcpInfoSignals, this, [=](const QString& info){
            this->InfoSignals(info);
            this->statusInfoUpdate(info);
            if (info == "链接服务端超时"
                || info == "未连接Socket服务器"
                || info == "链接服务端失败"
                || info == "服务端断开连接"
                || info == "链接服务端失败")
            {
                this->videoHasTrain = false;
            }
        });
        // 接收子线程的TCP消息内容展示
        connect(this->tcpClient, &TcpClient::getTcpInfoSignals, this, [=](const QString& info){
            this->InfoSignals(info);
        });

        emit connect_socket(this->socketServerConfig_.server_ip, this->socketServerConfig_.server_port);

        connect(this->tcpClient, &TcpClient::comeTrainSignals, this, [=](bool type) {
            if (type)
            {
                this->InfoSignals("视频车号识别-来车了");
                this->videoHasTrain = true;
                emit IdentifyType();
            }
            else
            {
                this->InfoSignals("视频车号识别-火车离开了");
                this->videoHasTrain = false;
                if (this->needIdentify)
                    emit IdentifyType();
            }
        });
        connect(this->tcpClient, &TcpClient::getDirectionSignals, this, [=](int direction) {
            if (this->iDirection == 0)
            {
                this->InfoSignals("获得方向判定：" + QString(direction > -1 ? (direction > 0 ? "需识别" : "待定") : "无需上传"));
                this->iDirection = direction;
            }
        });
    }
}

void ReadComThread::run()
{
    while (true)
    {
        QThread::msleep(10 * 1000);
//        if (this->auto_reconnect_serial_)
//        {
//            QThread::msleep(10 * 1000);
//            if ((this->serial_ == nullptr || !this->serial_->isOpen()))
//            {
//                this->openCom(true, 0);
//            }
//
//        }


    }
}

void ReadComThread::openCom(const bool &type, const int &msec)
{
    QThread::msleep(msec);
    if (type)
    {
        this->auto_reconnect_serial_ = true;
        this->serial_ = new QSerialPort(this);
        if (this->comDetect_->openCom(this->serial_, this->baseConfig_.comName, this->baseConfig_.baud))
        {
            QString logs;
            logs.append("串口打开成功\n");
            logs.append("串口号:" + this->baseConfig_.comName + "\n");
            logs.append("波特率:" + QString::number(this->baseConfig_.baud) + "\n");
            if (this->baseConfig_.havaMagnetSteel)
                logs.append("磁钢顺序:" + this->baseConfig_.magnetSteelOrder);
            this->isOpenCom = true;
            this->InfoSignals(logs);

            connect(this->serial_,&QSerialPort::readyRead,this,&ReadComThread::readCom); //
            connect(this->serial_, &QSerialPort::errorOccurred, this, &ReadComThread::serialPort_error);

            emit this->openComSuccess(true);
            this->timer_.stop();
            this->initParam();
        }
        else
        {
            this->comDetect_->closeCom(this->serial_);
            this->isOpenCom = false;
            this->ErrorSignals("串口打开失败！");
            emit this->openComSuccess(false);
            this->timer_.start(10*1000);
        }
    }
    else
    {
        this->auto_reconnect_serial_ = false;

        if (this->comDetect_->closeCom(this->serial_))
        {
            this->initParam();
            this->InfoSignals("串口已关闭！");
            this->closeComSuccess(true);
        }
        else
        {
            emit this->closeComSuccess(false);
        }
    }
}

void ReadComThread::tryToReconnect()
{
    if (this->auto_reconnect_serial_)
        this->openCom(true);
}

void ReadComThread::readCom()
{
    try {
        if (this->serial_->waitForReadyRead(10)) {
            QByteArray data = this->serial_->readAll(); // 读取数据

            if(!data.isEmpty())
            {
                this->queue_.push(QString(data));
                emit getRfid_signals();
            }
            data.clear();
        }
        else
        {
            this->ErrorSignals("等待读取超时");
        }
    } catch(...) {
        this->ErrorSignals("读取串口数据异常！");
    }
}

void ReadComThread::readTestInfo(const QString &filePath) {
    // 创建一个QFile对象并打开要读取的文本文件
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        this->ErrorSignals("无法打开测试文件:" + filePath);
        return;
    }
    this->InfoSignals("开始读取测试数据...");
    // 将文件内容读入到QStringList中
    QTextStream in(&file);
    while(!in.atEnd()) {
        QString line = in.readLine();
        this->queue_.push(line);
        emit getRfid_signals();
    }
    // 关闭文件
    file.close();
    this->InfoSignals("---读取测试数据完毕---");
}

/**
 * @brief:从queue中获取数据
 * @param:空
 * @return:空
 */
void ReadComThread::getQueueDataThread()
{
    if (this->queue_.isEmpty()) return;

    QString strRfidInfo = this->queue_.getTop();
    this->queue_.pop();
    this->saveRfidLog(strRfidInfo);
    this->tmpRfid.append(strRfidInfo);
    if (this->tmpRfid.right(1) != "&")
    {
        return;
    }

    this->tmpRfid = this->tmpRfid.replace("&", "");
    QStringList rfidSubList = this->tmpRfid.split("@");
    this->tmpRfid = "";

    auto split_lambda = [=](QString strFirst, QString strOther){
        if (strFirst == "0")
        {
            this->rfidHasTrain = false;
            this->InfoSignals("RFID-火车离开了");
            // 关功放信号
            // 需要结合 “来车状态”广播消息，确认是否真的车离开了;  TODO:没有真离开，则向串口发送 @on& 避免关功放
            emit IdentifyType();
        }
        else
        {
            // TODO: 此处按照4个磁钢来配置，可能存在一定限制; 另外逻辑需要优化
            if (strOther == "000000"  // 第一次检测来车时（前提是上一次功放已关闭）
                && (strFirst == this->baseConfig_.magnetSteelOrder.left(1)
                    || strFirst == this->baseConfig_.magnetSteelOrder.mid(1, 1)))
            {
                this->rfidHasTrain = true;
                this->InfoSignals("RFID-来车了");
                emit IdentifyType();
            }
        }
    };

    for (const auto & i : rfidSubList)
    {
        if (i.size() == 26 || i.size() == 24)
        {
            // 车厢信息
            QString strTrainInfo = i.mid(1,13);

            if (vecTrain.empty() || !vecTrain.contains(strTrainInfo))
            {

                // 判断是否是车头  车头的第14位是 K或H (慎重)
                if (!i.mid(14, 1).contains(QRegExp("^[0-9]+$"))) continue;
                // 过滤掉车号中有非数字的
                if (!strTrainInfo.right(7).contains(QRegExp("^[0-9]+$"))) continue;
                // 因信号不稳定 增加一行过滤  出现读到的编号数据里 空格替代了实际字符的情况
                if (strTrainInfo.right(7).simplified().size() < 7) continue;

                vecTrain.append(strTrainInfo);
                int train_order = vecTrain.size();
//                this->resultTableModel_->setItem(train_order - 1, 0, new QStandardItem(strTrainInfo));
                this->addResultSignals(strTrainInfo);
                this->InfoSignals(QString("第%1节 %2").arg(train_order).arg(strTrainInfo));
                // 将接收到的RFID使用@符拼接起来，以待后续上传web
                this->rfidSourceInfo.append("@" + i);
                QString rfidinfo = this->rfidSourceInfo;
                this->rfidSourceInfo = "";
                emit upRfid_signals(rfidinfo);

            }
        }
        else if (i.size() == 7)
        {
            // 不使用磁钢的情况下 过滤掉磁钢信号
            if (!this->baseConfig_.havaMagnetSteel) continue;
            // 磁钢信号
            QString strFirst = i.left(1);
            QString strOther = i.mid(1, i.size() - 1);
            // 将接收到的RFID使用@符拼接起来，以待后续上传web
            this->rfidSourceInfo.append("@" + i);
            split_lambda(strFirst, strOther);
        }
        else if (i.size() == 14) // 特殊情况，来车信号少了&符
        {
            // 不使用磁钢的情况下 过滤掉磁钢信号
            if (!this->baseConfig_.havaMagnetSteel) continue;
            for (int j = 0; j < i.size(); j+=7) {
                QString rfid_cg = i.mid(j, 7);
                QString strFirst = rfid_cg.left(1);
                QString strOther = rfid_cg.mid(1, 6);
                // 将接收到的RFID使用@符拼接起来，以待后续上传web
                this->rfidSourceInfo.append("@" + rfid_cg);
                split_lambda(strFirst, strOther);
            }
        }
    }
}

void ReadComThread::IdentifyTypeUpdate() {
    if (this->rfidHasTrain || this->videoHasTrain)
    {
        if (!this->needIdentify)
        {
            this->InfoSignals("来车了");
            this->mkRfidLog();
            this->vecTrain.clear();
            this->needIdentify = true;
            this->trainTime = this->getSystemTime();
            emit this->comeTrain(false);

        }
    }
    else if (!this->rfidHasTrain && !this->videoHasTrain)
    {
        this->initParam();
        this->InfoSignals("火车离开了");
    }
}

void ReadComThread::upRfid(const QString &rfidInfo)
{
    if (!this->baseConfig_.upResult) return;
    if (!this->needIdentify && this->baseConfig_.havaMagnetSteel)
    {
        emit upResultType(false);
        return;
    }else{
        emit upResultType(true);
    }

    QString info = this->vecTrain.back();
    int order = this->vecTrain.size();

    if (order > this->socketServerConfig_.delayed_upload && this->iDirection == 0)
    {
        this->iDirection = 1;
    }
    if (!this->baseConfig_.useSocketServer) this->iDirection = 1;

    TrainInfo trainInfo;
    trainInfo.carriageType = info.left(6).simplified().toStdString();
    trainInfo.carriageNum = info.mid(6, info.size() - 6).simplified().toStdString();
    trainInfo.strOrder = QString::number(order).toStdString();
    trainInfo.trainTime = this->trainTime.toStdString();
    trainInfo.collectTime = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz").toStdString();
    trainInfo.strRfidInfo = rfidInfo.toStdString();

    this->queueTmpTrainInfo_.push(trainInfo);

    if (this->iDirection < 0)
    {
        this->queueTmpTrainInfo_.clear();
        return;
    }
    if (this->iDirection > 0)
    {
        while (!this->queueTmpTrainInfo_.isEmpty())
        {
            this->queueTrainInfo_->push(this->queueTmpTrainInfo_.pop());
        }
    }
}

bool ReadComThread::mkRfidLog()
{
    try {
        QFileInfo fileInfo(this->logRfidRecvName);
        if(fileInfo.isFile())
        {
            return true;
        }
        this->recvLog.setFileName(this->logRfidRecvName);
        if (!this->recvLog.isOpen())
        {
            this->recvLog.open(QIODevice::ReadWrite | QIODevice::Append);
        }
        return true;
    } catch (const std::exception &e) {
        this->ErrorSignals("创建、打开RFID原始数据日志文件失败,详细：");
        this->ErrorSignals(e.what());
    }
    return false;
}

void ReadComThread::saveRfidLog(const QString &value)
{
    try {
        QString logValue = value + "\n";
        if (!this->recvLog.isOpen())
        {
            this->recvLog.open(QIODevice::ReadWrite | QIODevice::Append);
        }
        this->recvLog.write(logValue.toUtf8());
        this->recvLog.flush();
    } catch (const std::exception &e) {
        this->ErrorSignals("保存RFID原数据失败");
    }
}

bool ReadComThread::rnameRfidLog()
{
    try {
        this->recvLog.close();
        QFile logFile(this->logRfidRecvName);
        QFileInfo fileInfo(logFile);
        if (fileInfo.size() == 0) return false;

        QString newLogFileName = fileInfo.path() + "/";
        newLogFileName.append(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss"));
        newLogFileName.append(".txt");

        if (!logFile.rename(newLogFileName))
        {
            this->ErrorSignals("读取RFID原始数据，存储重命名失败！");
            return false;
        }

        this->deleteOldFiles(fileInfo.path(), 3);
    } catch (const std::exception &e) {
        this->ErrorSignals("读取RFID原始数据，存储重命名失败！");
        return false;
    }
    return true;
}

void ReadComThread::deleteOldFiles(const QString &path, int days)
{
    QDir dir(path);
            foreach(QFileInfo fi, dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot))
        {
            if((fi.lastModified().addDays(days) < QDateTime::currentDateTime()))
            {
                QFile::remove(fi.absoluteFilePath());
            }
        }
}

/**
 * 串口异常退出的槽函数
 * @param error
 */
void ReadComThread::serialPort_error(QSerialPort::SerialPortError error)
{
    if (error != QSerialPort::NoError)
    {
        //关闭串口
        this->serial_->close();
        this->ErrorSignals("串口异常退出，疑似接口松动;10秒后自动尝试重连...");
        emit this->openComSuccess(false);
        this->timer_.start(10*1000);
    }
}

void ReadComThread::initParam() {
    if (this->recvLog.isOpen()) this->recvLog.close();
    this->rfidSourceInfo = "";
    this->vecTrain.clear();
    this->rnameRfidLog();
    this->queue_.clear();
    this->trainTime = "";
    this->needIdentify = false;
    this->rfidHasTrain = false;
    this->videoHasTrain = false;
    this->iDirection = 0;
}

/**
 * @brief:获取系统时间
 * @param：
 * @return:空
 */
QString ReadComThread::getSystemTime()
{
    return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
}

