#include "CounterDevice.h"

#include <iostream>
#include <QDateTime>
#include <QDebug>

CounterDevice::CounterDevice(QObject *parent) : QObject(parent)
{
    connect(&this->serialUtil, &QSerialPortUtil::dataRecieved,
                this, &CounterDevice::dataReceivedHandler);

    kafkaUtil.setBrokers(SettingConfig::getInstance().KAFKA_BROKERS);
    kafkaUtil.setTopic(SettingConfig::getInstance().KAFKA_DATA_TOPIC);
    kafkaUtil.createProducer();
}

CounterDevice::~CounterDevice()
{
    disconnect(&this->serialUtil, &QSerialPortUtil::dataRecieved,
                this, &CounterDevice::dataReceivedHandler);
}

void CounterDevice::setComName(QString comName)
{
    this->comName = comName;
}
void CounterDevice::setBaudRate(int baudRate)
{
    this->baudRate = baudRate;
}
QString CounterDevice::getDevCode()
{
    return this->devCode;
}
void CounterDevice::setDevCode(QString devCode)
{
    this->devCode = devCode;
}
void CounterDevice::setDeviceId(QString deviceId)
{
    this->deviceId = deviceId;
}
QString CounterDevice::getDeviceName()
{
    return deviceName;
}
void CounterDevice::setDeviceName(QString deviceName)
{
    this->deviceName = deviceName;
}

int CounterDevice::getFormColIndex()
{
    return this->formColIndex;
}
void CounterDevice::setFormColIndex(int idx)
{
    this->formColIndex = idx;
}

int CounterDevice::getIsUse()
{
    return this->isUse;
}
void CounterDevice::setIsUse(int isUse)
{
    this->isUse = isUse;
}

bool CounterDevice::isSerialOpen()
{
    return this->serialUtil.isOpen();
}

void CounterDevice::initSerialPort()
{
    int master = SettingConfig::getInstance().MASTER;
    QStringList comNameList = this->comName.split(",");
    if (comNameList.isEmpty() == true)
    {
        return;
    }

    // 如果是主程序则打开主串口 备程序则打开备串口
    if (master == 1)
    {
        this->serialUtil.openSerialPort(comNameList.at(0), this->baudRate);
    } else
    {
        this->serialUtil.openSerialPort(comNameList.at(1), this->baudRate);
    }
}


void CounterDevice::dataReceivedHandler(QByteArray data)
{
    this->dataBuff.append(data);

    QDateTime now = QDateTime::currentDateTime();

    CounterDataDto * counterData = new CounterDataDto(this);
    if (CounterProtocolXH::checkFrame(this->dataBuff) == true)
    {
        counterData->rawFrame = this->dataBuff;

        // ★解析成数据对象
        bool parse = CounterProtocolXH::parseMessureData(this->dataBuff, counterData);

        // 解析成功
        if (parse == true)
        {
            this->dataBuff.clear();
            counterData->devCode = this->devCode;
            counterData->timestamp = now.toString("yyyy-MM-dd HH:mm:ss.zzz");
            counterData->milisecond = now.toMSecsSinceEpoch();
            this->afterFramePhase(counterData);
        }

        // 到达拟合计算时间点时 触发
        if (now.time().second() == SettingConfig::getInstance().RESIDUAL_CALC_SEC)
        {
            this->calcResidualClockData(now.currentSecsSinceEpoch() - SettingConfig::getInstance().RESIDUAL_CALC_SEC);
        }
    } else if (this->dataBuff.size() > COUNTER_FRAME_MIN_LENGTH)
    {
        std::cout << QString("clear buffer: %1").arg(this->dataBuff.size()).toStdString() << std::endl;
        this->dataBuff.clear();
    }

    // 在此处释放内存，不影响后续显示
    // 不在此处释放内存则会导致内存持续增加
    // 具体原因不明
    delete counterData;
}


void CounterDevice::afterFramePhase(CounterDataDto * counterData)
{
    // 0. 输出到日志文件中
    QString date = counterData->timestamp.mid(0, 10);

    // 1. 原始字节数组数据
    QString filename = "raw_" + devCode + ".log";
    QString content = counterData->timestamp + " " + counterData->rawFrame;
    QLogUtil::writeRawDataLogByDate(date, filename, content);

    QJsonArray messageArray;
    QString msgLogFilename = "msg_" + devCode + ".log";

    CounterChannel * refChanPtr = ConstCache::getInstance().channelMap.value(QString("%1-%2").arg(deviceId).arg(counterData->channelRefId));
    this->refChannelNo = counterData->channelRefId;
    double refDelay = 0.0;
    if (refChanPtr != nullptr && refChanPtr->getDelays().isEmpty() == false) {
        refDelay = refChanPtr->getDelays().toDouble();
    }

    // 2. 各个通道的clock diff数据
    for (int i = 1; i <= counterData->channelActiveArray.size(); i++)
    {
        if (counterData->channelActiveArray.at(i-1).toUInt() == 1)
        {
            if (counterData->channelRefId != i)
            {
                QString chFilename("%1_CH_%2.log");
                chFilename = chFilename.arg(devCode);
                if (i < 10)
                {
                    chFilename = chFilename.arg(QString("0%1").arg(i));
                } else
                {
                    chFilename = chFilename.arg(i);
                }
                QString channelDataStr = QString("%1 [%2] %3").arg(counterData->timestamp).arg(counterData->frameId).arg(counterData->channelDataArray.at(i-1));

                // 写通道数据日志
                QLogUtil::writeChannelDataLogByDate(date, chFilename, channelDataStr);
            }

            // 加到hisDataList中 用于绘制图表
            CounterChannel * channelPtr = ConstCache::getInstance().channelMap.value(QString("%1-%2").arg(deviceId).arg(i));
            if (channelPtr != nullptr && channelPtr->isActive() == true)
            {
                // 通道时延值 并计算 减去时延值的测量值
                double delay = channelPtr->getDelays().isEmpty() == false ? channelPtr->getDelays().toDouble() : 0.0;
                double valueMinusDelay = counterData->channelDataArray.at(i-1).toDouble() - (delay * 1E-9) + (refDelay * 1E-9);
                channelPtr->appendHisData(QString::number(counterData->milisecond), QString::number(valueMinusDelay, 'f', 12));

                QJsonObject jsonObj = counterData->toJSON(i - 1);
                jsonObj.insert("clientId", SettingConfig::getInstance().CLIENT_ID);
                jsonObj.insert("master", SettingConfig::getInstance().MASTER);
                jsonObj.insert("deviceId", deviceId);
                messageArray.append(jsonObj);
            }
        }
    }

    // 3. 输出到中间件，执行后续处理过程
    QJsonObject statusObj = counterData->toStatusJSON();
    statusObj.insert("clientId", SettingConfig::getInstance().CLIENT_ID);
    statusObj.insert("master", SettingConfig::getInstance().MASTER);
    statusObj.insert("deviceId", deviceId);

    if (SettingConfig::getInstance().NEED_KAFKA == 1)
    {
        kafkaUtil.produceMessage(QString(QJsonDocument(messageArray).toJson(QJsonDocument::Compact)));
        kafkaUtil.produceMessage(SettingConfig::getInstance().KAFKA_STATUS_TOPIC, QString(QJsonDocument(statusObj).toJson(QJsonDocument::Compact)));

        QLogUtil::writeChannelDataLogByDate(date, msgLogFilename, QString(QJsonDocument(messageArray).toJson(QJsonDocument::Compact)));
    }

    // 4. 在界面上简单显示相差数据结果
    emit this->sendDataToDraw(messageArray);
}

void CounterDevice::calcResidualClockData(qlonglong tm)
{
    qDebug() << devCode << " start: " << QDateTime::currentMSecsSinceEpoch();
    QString date = QDateTime::fromSecsSinceEpoch(tm).toString("yyyy-MM-dd");

    QJsonArray messageArray;
    QString msgLogFilename = "MINUTE_msg_" + devCode + ".log";

    for (int j = 1; j <= 16; j++)
    {
        QString channelKey = QString("%1-%2").arg(deviceId).arg(j);
        if (ConstCache::getInstance().channelMap.contains(channelKey))
        {
            CounterChannel * channelPtr = ConstCache::getInstance().channelMap.value(channelKey);
            QList<QStringList> minuteDataList = channelPtr->getHisData().mid(channelPtr->getHisData().size() - 60, 60);

            QVector<double> timeVec;
            QVector<double> dataVec;

            for (int k = 0; k < minuteDataList.size(); k++)
            {
                qlonglong tmData = ((QString) minuteDataList.at(k).at(0)).toLongLong() / 1000;
                if (abs(tmData - tm) <= SettingConfig::getInstance().RESIDUAL_CALC_SEC) {
                    timeVec.append(tmData - tm);
                    dataVec.append(((QString) minuteDataList.at(k).at(1)).toDouble());
                }
            }

            LinearRegression lineReg;
            lineReg.xArray = timeVec.data();
            lineReg.yArray = dataVec.data();
            lineReg.calcLineRegParams(timeVec.size());
            double value = lineReg.slope * 0 + lineReg.intercept;

            if (j != this->refChannelNo)
            {
                // 写入通道的拟合数据日志
                QString chFilename("MINUTE_%1_CH_%2.log");
                chFilename = chFilename.arg(devCode).arg(j, 2, 10, QLatin1Char('0'));
                QString channelDataStr = QString("%1 %2").arg(QDateTime::fromSecsSinceEpoch(tm).toString("yyyy-MM-dd HH:mm:ss")).arg(QString::number(value, 'f', 12));

                // 写日志
                QLogUtil::writeChannelDataLogByDate(date, chFilename, channelDataStr);
            }

            // 生成json对象
            QJsonObject jsonObj;

            QJsonObject dataObj;
            dataObj.insert("channelRefNo", this->refChannelNo);
            dataObj.insert("dataValue", QString::number(value, 'f', 12));
            dataObj.insert("rawValue", QString::number(value, 'f', 12));
            dataObj.insert("frameId", QDateTime::fromSecsSinceEpoch(tm).toString("HHmmss"));
            jsonObj.insert("data", dataObj);

            jsonObj.insert("channelNo", j);
            jsonObj.insert("ts", tm * 1000);
            jsonObj.insert("clientId", SettingConfig::getInstance().CLIENT_ID);
            jsonObj.insert("master", SettingConfig::getInstance().MASTER);
            jsonObj.insert("deviceId", deviceId);
            messageArray.append(jsonObj);
        }
    }

    QLogUtil::writeChannelDataLogByDate(date, msgLogFilename, QString(QJsonDocument(messageArray).toJson(QJsonDocument::Compact)));

    qDebug() << devCode << " end: " << QDateTime::currentMSecsSinceEpoch();
}
