#include "serialport.h"

#include <qdebug.h>
#include <qdatetime.h>
#include <iostream>
#include <cstring>
#include <qmessagebox.h>
#include <QSerialPortInfo>

#include "protocol.h"
#include "defines.h"
#include "dataparser.h"
#include "global.h"
#include "logger.h"

using namespace std;

const u16 conVid = 1155;     // 0x0483
const u16 conPid = 22336;    // 0x5740

SerialPort::SerialPort(QObject *parent)
    : QObject(parent), _sp(new QSerialPort(this)),
      _portCheckTimer(new QTimer(this)) {
    //
    connect(_portCheckTimer, &QTimer::timeout,
            this, &SerialPort::slotPortCheck);
    _portCheckTimer->start(1000);    // 每秒检测一次

    //    displayPortInfo();

#if 0
    // 1.初始化
    _sp = new QSerialPort();
    _sp->setPortName(conSPName);

    if(_sp == nullptr){
        qDebug()<<"sp is null.";
        exit(0);
        return;
    }
    // 3.设置
    //设置波特率和读写方向
    _sp->setBaudRate(QSerialPort::Baud115200,
                     QSerialPort::AllDirections);
    _sp->setDataBits(QSerialPort::Data8);      //数据位为8位
    _sp->setFlowControl(QSerialPort::NoFlowControl);//无流控制
    _sp->setParity(QSerialPort::NoParity); //无校验位
    _sp->setStopBits(QSerialPort::OneStop); //一位停止位

    //4.连接信号槽
    connect(_sp,&QSerialPort::readyRead,
            this,&SerialPort::slotReadData);

    // 2.打开串口
    if(false == _sp->open(QIODevice::ReadWrite)){
        qDebug()<<"open failed."
               <<_sp->error();
        exit(0);
    }
    else{
        qDebug()<<"open succ.";

        //         设置 DTR 信号为就绪状态（true 表示低电平）
        _sp->setDataTerminalReady(true);
    }

    //写数据定时器
    //    startTimer(500);
#endif

#if 0
    sendCmd(e_zero);

    for(int i = 0; i < 9; i++){
        sendCmd(e_back);
    }
#endif
}

SerialPort *SerialPort::instance() {
    static SerialPort ins;
    return &ins;
}

SerialPort::~SerialPort() {
    logde << "serialport destructor.";

    closeSp();
    //
    delete _sp;
    _sp = nullptr;
    //
    _portCheckTimer->stop();
    delete _portCheckTimer;
    _portCheckTimer = nullptr;
}

void SerialPort::slotReadData() {
    QByteArray ba = _sp->readAll();
    if (ba.size() == 0) {
        return;
    }

#if 0
    QString hexData = ba.toHex(' ');    // ' ' 作为分隔符，可选参数
    logde << "receive info (hex):" << hexData.toStdString();
#endif

    SerialPortProtocol *spp = (SerialPortProtocol *)ba.data();
    if (FRANE_HEAD != spp->head) {
        logde << "Data header error.";
        return;
    }

    // phase setting data
    if (spp->addr == PHASE_START_ADDR) {
        emit sigSendPhaseInfo(ba);
        return;
    }

    int dataLength = spp->len - 5;

    CommonData cd;
    u8        *cdPtr = (u8 *)&cd;
    memcpy(cdPtr + spp->addr, spp->data_buf, dataLength);

    if (WRITE_CMD == spp->cmd) {
        commonDataParser(dataLength, spp->addr, cd);
    } else if (READ_CMD == spp->cmd) {
        //        DataParser::experimentalStateSwitching(cd);
        updateStatus(cd);

        if (spp->addr == 0) {
            if (Global::Mode::Experiment == Global::_mode) {
                emit sigSendCommonData(cd);
            }
            // emit sigSendCommonDataToRealDataForm(cd);
        } else {
            QString hexData = ba.toHex(' ');    // ' ' 作为分隔符，可选参数
            logde << "receive info (hex):" << hexData.toStdString();
        }
    }
}

void SerialPort::updateStatus(const CommonData &cd) {
#if 0
    //    logde<<"phase:"<<(int)cd.current_phase;
    //    logde<<"run type:"<<(int)cd.run_type;
    logde<<"software mode:"<<Global::_mode;
#endif

    // Switch the software mode accord to the serial port data.
    switch (cd.run_type) {
        case DeviceRunStatus::Heat:
        case DeviceRunStatus::ConstantTemp:
            if (Global::_mode != Global::Mode::Experiment) {
                Global::_mode = Global::Mode::Experiment;
            }
            break;
        case DeviceRunStatus::Idle:
        case DeviceRunStatus::Cooling:
            if (Global::_mode != Global::Mode::Analysis) {
                Global::_mode = Global::Mode::Analysis;
            }
            break;
        default:
            break;
    }

    // 判断是否根据下位机数据，切换软件实验模式。
    static Global::Mode preMode            = Global::Mode::Analysis;
    bool                sendSaveSignalFlag = false;
    if (Global::_mode != preMode) {
        if (preMode == Global::Mode::Experiment
            && Global::_mode == Global::Mode::Analysis) {
            sendSaveSignalFlag = true;
        } else if (preMode == Global::Mode::Analysis
                   && Global::_mode == Global::Mode::Experiment) {

            // 根据下位机数据，软件进入实验模式，开始实验。
            logde << "start experiment, accord to device data...";
            logde << "---------------------";

            emit sigStartExperiment();
        }

        preMode = Global::_mode;
    }

    // If phase update, add new phase data to the global param.
    if (Global::_mode == Global::Mode::Experiment) {
        if (Global::_currentPhase != (int)cd.current_phase) {

            logde << "mode experiment,phase not equeal.";

            Global::_currentPhase = (int)cd.current_phase;

            Global::CurveExperimentData ced;
            ced.phaseIndex = cd.current_phase;

            Global::_curveExperimentDataVtr.push_back(ced);

            Global::_currentCurveExperimentDataPtr =
                &Global::_curveExperimentDataVtr.last();

            Global::_currentCurveExperimentDataPtr->fileName =
                Global::_experimentInfo.sampleName;
        }
    }

    //    logde<<"_curveExperimentDataVtr size:"
    //        <<Global::_curveExperimentDataVtr.size();

    //
    updateAxis();

    // Update axis.
    bool    experimentEnded = false;
    QString devRunModeStr;
    switch (cd.run_type) {
        case DeviceRunStatus::Heat:
            devRunModeStr   = "升温";
            experimentEnded = true;
            break;
        case DeviceRunStatus::ConstantTemp:
            devRunModeStr   = "实验";
            experimentEnded = true;
            break;
        case DeviceRunStatus::Idle:
        case DeviceRunStatus::Cooling:
            devRunModeStr   = "冷却";
            experimentEnded = false;
            break;
            break;
        default:
            break;
    }

    QString msg;
    if (experimentEnded) {
        msg = QString("设备运行状态:%2 实验阶段:%3 实验进行中").arg(devRunModeStr).arg(cd.current_phase);
    } else {
        // msg = QString("设备运行状态:%2 实验阶段:%3 实验结束").arg(devRunModeStr).arg(cd.current_phase);
        msg = QString("设备运行状态:%2 实验结束").arg(devRunModeStr);
    }

    emit sigUpdateStatusbarMsg(msg);

#if 0
    // If save experiment data.
    static Global::Mode preMode            = Global::Mode::Analysis;
    bool                sendSaveSignalFlag = false;
    if (Global::_mode != preMode) {
        if (preMode == Global::Mode::Experiment
            && Global::_mode == Global::Mode::Analysis) {
            sendSaveSignalFlag = true;
        } else if (preMode == Global::Mode::Analysis
                   && Global::_mode == Global::Mode::Experiment) {
                    
            // 根据下位机数据，软件进入实验模式，开始实验。
            logde << "start experiment, accord to device data...";
            logde << "---------------------";

            emit sigStartExperiment();
        }

        preMode = Global::_mode;
    }
#endif

    // 弹出保存数据对话框。
    if (sendSaveSignalFlag && !Global::_OITAutoAnalysisModeFlag) {
        emit sigSaveExperimentalDataMsgBox();
    }
}

void SerialPort::commonDataParser(const int dataLength, const u16 addr,
                                  const CommonData &cd) {
    int localLength = dataLength;
    int localAddr   = addr;

    int  phaseByteSize   = sizeof(Phase);
    auto phaseParserFunc = [&](const int index) {
        Phase phase;
        phase.onoff       = 1;
        phase.gas         = cd.phase_data[index].gas;
        phase.temp_flow   = cd.phase_data[index].temp_flow;
        phase.cutoff_temp = cd.phase_data[index].cutoff_temp;
        phase.constant_temp_time_min =
            cd.phase_data[index].constant_temp_time_min;

        localLength -= phaseByteSize;
        localAddr += phaseByteSize;
    };

    while (localLength) {
        logde << "localLength:" << localLength;
        switch (localAddr) {
            case offsetof(CommonData, run_type):    // 运行状态
            {
                switch (cd.run_type) {
                    case DeviceRunStatus::Cooling:
                        //                Global::instance()->setMode(Global::Mode::Analysis);
                        Global::_mode = Global::Mode::Analysis;
                        logde << "set global mode analysis.common data parser.";
                        break;
                    default:
                        break;
                }

                localAddr += 1;
                localLength -= 1;

                break;
            }
            case offsetof(CommonData, current_gas):    // 当前气氛
                //            gas_type_set(dev->temp, msg_data.current_gas);
                localAddr += 1;
                localLength -= 1;
                break;
            case offsetof(CommonData, auto_pid_temp_flow):    // 自整定升温速率
                localAddr += 4;
                localLength -= 4;
                break;
            case offsetof(CommonData, run_mode): {
                DeviceStartMode mode = (DeviceStartMode)cd.run_mode;

                switch (mode) {
                    case DeviceStartMode::Stop:
                        Global::_mode = Global::Mode::Analysis;
                        logde << "set global mode analysis.";
                        break;
                    case DeviceStartMode::Start:
                        Global::_mode = Global::Mode::Experiment;
                        break;
                    default:
                        break;
                }
                //
                localLength--;
                localAddr++;

                break;
            }
            case offsetof(CommonData, phase_data[0].onoff):
                phaseParserFunc(0);
                break;
            case offsetof(CommonData, phase_data[1].onoff):
                phaseParserFunc(1);
                break;
            case offsetof(CommonData, phase_data[2].onoff):
                phaseParserFunc(2);
                break;
            case offsetof(CommonData, phase_data[3].onoff):
                phaseParserFunc(3);
                break;
            case offsetof(CommonData, phase_data[4].onoff):
                phaseParserFunc(4);
                break;
            case offsetof(CommonData, phase_data[5].onoff):
                phaseParserFunc(5);
                break;
            default:
                localLength--;
                break;
        };
    }
}

bool SerialPort::openSp() {
    logde << "openSp 1";
    closeSp();
    QThread::msleep(100);

#if 0
    if (_sp != nullptr && _sp->isOpen()) {
        return true;
    }
#endif

    logde << "openSp 2";
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        u16 pid = info.productIdentifier();
        u16 vid = info.vendorIdentifier();

        if ((pid == conPid) && (vid == conVid)) {
            _sp->setPort(info);
            break;
        }
    }
    logde << "openSp 3";
    if (_sp == nullptr) {
        qDebug() << "Device not found.";
        return false;
    }

    // 设置波特率和读写方向
    _sp->setBaudRate(QSerialPort::Baud115200,
                     QSerialPort::AllDirections);
    _sp->setDataBits(QSerialPort::Data8);               // 数据位为8位
    _sp->setFlowControl(QSerialPort::NoFlowControl);    // 无流控制
    _sp->setParity(QSerialPort::NoParity);              // 无校验位
    _sp->setStopBits(QSerialPort::OneStop);             // 一位停止位

    // 4.连接信号槽
    connect(_sp, &QSerialPort::readyRead,
            this, &SerialPort::slotReadData);

    logde << "openSp 4";
    // 2.打开串口
    if (!_sp->open(QIODevice::ReadWrite)) {
        qDebug() << "open failed." << _sp->error();
        return false;
    } else {
        //        qDebug() << "open succ.";
        logde << "openSp 5.";
        // 设置 DTR 信号为就绪状态（true 表示低电平）
        _sp->setDataTerminalReady(true);
    }

    logde << "openSp 6.before return.";
    return true;
}

bool SerialPort::closeSp() {
    if (_sp && _sp->isOpen()) {
        _sp->clear();
        _sp->close();
    }
}

void SerialPort::sendCmd(const SerialPort::E_CMD_TYPE e) {
#if 1
    int  length      = 20;
    char in_char[21] = { '\0' };

    if (e == e_zero) {
        //        char data[10] = {'55','aa','0b','0a','20',
        //                         '4e','00','00','00','c3'};
        const char *data = "55aa0b0a204e000000c3";
        memcpy(in_char, data, length);
    } else if (e == e_back) {
        //        char data[10] = {'55','aa','08','10','27',
        //                         'f0','d8','ff','ff','c3'};
        const char *data = "55aa081027f0d8ffffc3";
        memcpy(in_char, data, length);
    } else if (e == e_forward) {
        //        char data[10] = {'55','aa','08','10','27',
        //                         '10','27','00','00','c3'};
        const char *data = "55aa08102710270000c3";
        memcpy(in_char, data, length);
    }

#endif
    //    char in_char[] = "55aa0b0a204e000000c3";

    char out_char[21] = { '\0' };
    int  hex_length   = 10;
    toHex(in_char, hex_length, out_char);

    int num = _sp->write(out_char,
                         hex_length);
    if (num == -1) {
        qDebug() << "write failed.";
    } else {
        qDebug() << "write ok|num:" << num;
    }
}

void SerialPort::toHex(char *in_char, int char_length, char *out_char) {
    while (char_length--) {
        *out_char = (*in_char & 0x40 ? *in_char + 9 : *in_char) << 4;
        ++in_char;
        *out_char |= (*in_char & 0x40 ? *in_char + 9 : *in_char) & 0xF;
        ++in_char;
        ++out_char;
    }
}

void SerialPort::displayPortInfo() {
    // 获取系统中所有可用的串口信息
    QList<QSerialPortInfo> serialPorts = QSerialPortInfo::availablePorts();

    // 遍历每个串口信息
    for (const QSerialPortInfo &portInfo : serialPorts) {
        qDebug() << "================================================";
        // 打印串口的名称
        qDebug() << "串口名称: " << portInfo.portName();
        // 打印串口的描述信息
        qDebug() << "描述信息: " << portInfo.description();
        // 打印串口的制造商信息
        qDebug() << "制造商: " << portInfo.manufacturer();
        // 打印串口的序列号
        qDebug() << "序列号: " << portInfo.serialNumber();
        // 打印串口的系统位置
        qDebug() << "系统位置: " << portInfo.systemLocation();
        // 打印是否有虚拟调制解调器
        qDebug() << "是否有虚拟调制解调器: " << (portInfo.hasVendorIdentifier() ? "是" : "否");
        // 打印是否有产品标识符
        qDebug() << "是否有产品标识符: " << (portInfo.hasProductIdentifier() ? "是" : "否");
        // 如果有虚拟调制解调器，打印其标识符
        if (portInfo.hasVendorIdentifier()) {
            qint16 vid = portInfo.vendorIdentifier();
            qDebug() << "虚拟调制解调器标识符: " << vid;
            QString hexStr = QString("0x%1").arg(vid, 4, 16, QChar('0')).toUpper();
            qDebug() << "vid 0x" << hexStr;
        }
        // 如果有产品标识符，打印其标识符
        if (portInfo.hasProductIdentifier()) {
            qint16 pid = portInfo.productIdentifier();
            qDebug() << "产品标识符: " << pid;
            QString hexStr = QString("0x%1").arg(pid, 4, 16, QChar('0')).toUpper();
            qDebug() << "pid 0x" << hexStr;
        }
    }
}

void SerialPort::updateAxis() {
    static Global::Mode previousMode = Global::Analysis;    // 记录上一次的模式

    if (previousMode == Global::Analysis && Global::_mode == Global::Experiment) {
        //        std::cout << "Mode has changed from Analysis to Experiment!" << std::endl;
        Global::ExperimentInfo &eti = Global::_experimentInfo;
        if (eti.phaseVtr.size() > 0) {
            logde << "serialport set axis.";
            float temp = eti.phaseVtr.at(0).cutoff_temp;
            emit  sigAxisModify(temp);
        }
    }

    if (previousMode != Global::_mode) {
        previousMode = Global::_mode;
    }
}
void SerialPort::parserTest() {
    const uchar data[]         = { 0xa5, 0x5a, 0x2d, 0x83,
                                   0x00, 0x00,
                                   0x00, 0x01, 0x23, 0x23,
                                   0x23, 0x23, 0x23, 0x23, 0x23,
                                   0x23, 0x07, 0xbc, 0xb5, 0xf2, 0xc8, 0x57, 0x38, 0x40, 0x1b, 0x63, 0x27, 0xbc, 0x04, 0xa7, 0xf2, 0x3f, 0x55, 0x55, 0x55, 0xd5, 0x04, 0xf3, 0xab, 0xbf, 0xfa, 0x2b, 0xcd, 0x41, 0x00, 0x00, 0x93, 0xba };
    CommonData *serialPortData = nullptr;

    qDebug() << "data length:" << sizeof(data) / sizeof(uchar);
    //    serialPortData = (CommonData)&data[4];

    serialPortData = reinterpret_cast<CommonData *>(const_cast<uchar *>(&data[6]));
    qDebug() << "run type:" << serialPortData->run_type;

    //    qDebug()<<"run time:"<<serialPortData->add_run_time;
    //    qDebug()<<"sample temp:"<<serialPortData->sample_temp;

    // 格式化输出 float 类型，保留两位小数
    QString formattedRunTime = QString::number(serialPortData->add_run_time, 'f', 2);
    qDebug() << "run time:" << formattedRunTime;

    // 格式化输出 double 类型，保留三位小数
    QString formattedSampleTemp = QString::number(serialPortData->sample_temp, 'f', 3);
    qDebug() << "sample temp:" << formattedSampleTemp;

    QString formattedColdTemp = QString::number(serialPortData->cold_temp, 'f', 3);
    qDebug() << "cold temp:" << formattedColdTemp;

#if 0
    // 定义小端序的 4 字节数据
    unsigned char bytesLittle[] = {0x23, 0x23, 0x01, 0x00};
    // 调用函数进行转换
    float result = 0.0f;
    std::memcpy(&result, bytesLittle, sizeof(float));
    //        float floatValue = bytesToFloatLittleEndian(bytesLittle);
    // 输出转换后的浮点数
    std::cout << "result: " << result << std::endl;
#endif
}

void SerialPort::slotDeliverData(const QByteArray &ba) {
    openSp();
    slotSendData(ba);
}

void SerialPort::slotSendData(const QByteArray &ba) {
#if 1
    logde << "slotSendData ==========================";
    logde << "slotSendData:" << ba.size();
    QString hexData = ba.toHex(' ');    // ' ' 作为分隔符，可选参数
    logde << "slotSendData:" << hexData.toStdString();
#endif

    if (_sp != nullptr && _sp->isOpen()) {
        int bytesWritten = _sp->write(ba);
        if (bytesWritten == -1) {
            logde << "Write failed." << _sp->errorString().toStdString();
        } else if (bytesWritten != ba.size()) {
            logde << "Not all data was writen.";
        } else {
            logde << "All data writen.";
        }
    } else {
        logde << "sp not open.";
        return;
    }
}

void SerialPort::slotCloseSp() {
    if (_sp != nullptr && _sp->isOpen()) {
        _sp->close();
    }
}

void SerialPort::slotPortCheck() {
   // logde<<"slotPortCheck...";

    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        u16 pid = info.productIdentifier();
        u16 vid = info.vendorIdentifier();

        if ((pid == conPid) && (vid == conVid)) {
           // logde<<"slotPortCheck... device connected.";
            return;
        }
    }
    // Device disconnected
    // logde<<"device disconnected.";
    emit sigDeviceDisconnected();

   // logde<<"slotPortCheck... device disconnected.";
}
