#include "serialportmanager.h"
#include <QSerialPortInfo>
#include <QThread>
#include <QDebug>
#include <QModbusRtuSerialMaster>
#include <QModbusDataUnit>
#include <QtEndian>
#include <cmath>

SerialPortManager::SerialPortManager(QObject *parent)
    : QObject(parent),
      serialPort(nullptr) {

}

SerialPortManager::~SerialPortManager() {
    if (serialPort) {
        delete serialPort;
    }
}

void SerialPortManager::setConnectionMode(ConnectionMode mode) {
    connectionMode = mode;
}

void SerialPortManager::configureSerialPort(const QString &portName, int baudRate, int dataBits, int stopBits, int parity) {
    this->portName = portName;
    this->baudRate = baudRate;
    this->dataBits = dataBits;
    this->stopBits = stopBits;
    this->parity = parity;
}

bool SerialPortManager::connectSerialPort() {
    if (serialPort) {
        disconnectSerialPort();
    }

    if (connectionMode == Manual) {
        serialPort = new QSerialPort(this);
        serialPort->setPortName(portName);
        serialPort->setBaudRate(baudRate);
        serialPort->setDataBits(static_cast<QSerialPort::DataBits>(dataBits));
        serialPort->setStopBits(static_cast<QSerialPort::StopBits>(stopBits));
        serialPort->setParity(static_cast<QSerialPort::Parity>(parity));

    } else {
        QString portName = autoConnect(command, expectedResponse);
        if (portName != "-1") {
            serialPort = new QSerialPort(portName, this);
            serialPort->setBaudRate(QSerialPort::Baud9600);
            serialPort->setDataBits(QSerialPort::Data8);
            serialPort->setStopBits(QSerialPort::OneStop);
            serialPort->setParity(QSerialPort::NoParity);
        }
    }

    if (serialPort && serialPort->open(QIODevice::ReadWrite)) {
        qDebug() << "open serialPort ok";
        return true;
    } else {
        if (serialPort) {
            delete serialPort;
            serialPort = nullptr;
        }
        qDebug() << "open serialPort fail";
        return false;
    }
}

void SerialPortManager::disconnectSerialPort() {
    if (serialPort && serialPort->isOpen()) {
        serialPort->close();
    }
    if (serialPort) {
        delete serialPort;
        serialPort = nullptr;
    }
}

QSerialPort* SerialPortManager::getSerialPort() const {
    return serialPort;
}

QString SerialPortManager::autoConnect(const QByteArray &command, const QByteArray &expectedResponse) {
    const auto ports = QSerialPortInfo::availablePorts();
    for (const QSerialPortInfo &portInfo : ports) {
        QSerialPort port(portInfo);
        port.setBaudRate(QSerialPort::Baud9600);
        port.setDataBits(QSerialPort::Data8);
        port.setStopBits(QSerialPort::OneStop);
        port.setParity(QSerialPort::NoParity);

        qDebug() << "Trying port:" << portInfo.portName();
        if (port.open(QIODevice::ReadWrite)) {
            if (testPort(port, command, expectedResponse)) {
                port.close();
                qDebug() << "Port found:" << portInfo.portName();
                return portInfo.portName();
            }
            port.close();
        } else {
            qDebug() << "Failed to open port:" << portInfo.portName();
        }
    }
    return "-1";
}


bool SerialPortManager::testPort(QSerialPort &port, const QByteArray &command, const QByteArray &expectedResponse) {
    for (int attempt = 0; attempt < 3; ++attempt) {
        port.write(hexStringToByteArray(command));
        if (port.waitForBytesWritten(1000) && port.waitForReadyRead(1000)) {

            QByteArray responseData = port.readAll();
            while (port.waitForReadyRead(100)) {
                responseData += port.readAll();
            }
            qDebug() << "Attempt" << attempt + 1 << ": Received response:" << responseData.toHex();

            QString responseHex = responseData.toHex();
            QString expectedResponseHex = expectedResponse.toHex();

//            qDebug() << "test  responseHex " <<responseHex;

            if (responseHex.contains(hexStringToByteArray(expectedResponseHex))) {
                qDebug() << "auto conn test ok";
                return true;
            }
        } else {
            qDebug() << "Attempt" << attempt + 1 << ": No response or timeout.";
        }
    }

    return false;
}


QByteArray SerialPortManager::hexStringToByteArray(const QString &hex) {
    QByteArray byteArray;
    for (int i = 0; i < hex.length(); i += 2) {
        bool ok;
        byteArray.append(static_cast<char>(hex.mid(i, 2).toInt(&ok, 16)));
    }
    return byteArray;
}

uint16_t SerialPortManager::CRC16_Calculate(const QByteArray &data)
{
    uint16_t crc = 0xFFFF;

    for (int pos = 0; pos < data.size(); pos++) {
        crc ^= static_cast<uint8_t>(data[pos]);

        for (int i = 8; i != 0; i--) {
            if ((crc & 0x0001) != 0) {
                crc >>= 1;
                crc ^= 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }

    return crc;
}

void SerialPortManager::sendModbusCommand(const QString &hexCommand)
{
    if (serialPort && serialPort->isOpen())
    {
        QByteArray command = hexStringToByteArray(hexCommand);
        uint16_t crc = CRC16_Calculate(command);

        command.append(static_cast<char>(crc & 0xFF));          //低
        command.append(static_cast<char>((crc >> 8) & 0xFF));   //高

        serialPort->write(command);
    }
    else
    {
        qWarning() << "Serial port is not open!";
    }
}



double SerialPortManager::receiveModbusResponse(int RESPONSE_LENGTH) {
    if (serialPort && serialPort->isOpen()) {

        QByteArray responseData = serialPort->readAll();
        while (serialPort->waitForReadyRead(100)) {
            responseData += serialPort->readAll();
        }

//        qDebug() << "Raw response data:" << responseData.toHex();

        while (responseData.size() >= RESPONSE_LENGTH) {
            QByteArray singleResponse = responseData.left(RESPONSE_LENGTH);
            responseData.remove(0, RESPONSE_LENGTH);

//            qDebug() << "Processing single response:" << singleResponse.toHex();

            // Extract the received CRC
            uint16_t receivedCrc = static_cast<uint8_t>(singleResponse[singleResponse.size() - 2]) |
                                   (static_cast<uint8_t>(singleResponse[singleResponse.size() - 1]) << 8);

            // Remove the CRC from the response data
            singleResponse.chop(2);

            // Calculate the CRC of the received data
            uint16_t calculatedCrc = CRC16_Calculate(singleResponse);

            qDebug() << "Data without CRC:" << singleResponse.toHex();
            qDebug() << "Received CRC:" << QString::number(receivedCrc, 16).toUpper();
            qDebug() << "Calculated CRC:" << QString::number(calculatedCrc, 16).toUpper();

            if (receivedCrc == calculatedCrc) {
                qDebug() << "Received valid response with correct CRC:" << singleResponse;

                // Extract the specific data part (e.g., 03E80003)
                if (singleResponse.size() == 5 ) {
                    QByteArray specificData = singleResponse.mid(3, 2); // Extract 4 bytes starting from the 4th byte
//                    qDebug() << "Specific data part:" << specificData.toHex();

                    if(specificData.toHex() == "7fff"){
                        return -2;
                    } else if(specificData.toHex() == "7ffc"){
                        return -3;
                    }

                    // Extract the first 2 bytes for the integer part
                    uint16_t integerPart = (static_cast<uint8_t>(specificData[0]) << 8) |
                                           static_cast<uint8_t>(specificData[1]);

                    // Extract the second 2 bytes for the decimal places
                    uint16_t decimalPlaces = (static_cast<uint8_t>(specificData[2]) << 8) |
                                             static_cast<uint8_t>(specificData[3]);

                    /**
                     * @brief value 1000
                     */

                    //一位小数除10，三位除1000
                    double value = static_cast<double>(integerPart) / 1000;
                    qDebug() << "Converted value:" << value;
                    qDebug() << "Converted value:" << QString::number(value, 'f', decimalPlaces);

                    return value; // Return the calculated value
                } else if (singleResponse.size() == 7 ) {
                    QByteArray specificData = singleResponse.mid(3, 4); // Extract 4 bytes starting from the 4th byte
                    qDebug() << "Specific data part:" << specificData.toHex();

                    if(specificData.toHex() == "7fff"){
                        return -2;
                    } else if(specificData.toHex() == "7ffc"){
                        return -3;
                    }

                    // Extract the first 2 bytes for the integer part
                    uint16_t integerPart = (static_cast<uint8_t>(specificData[0]) << 8) |
                                           static_cast<uint8_t>(specificData[1]);

                    // Extract the second 2 bytes for the decimal places
                    uint16_t decimalPlaces = (static_cast<uint8_t>(specificData[2]) << 8) |
                                             static_cast<uint8_t>(specificData[3]);

                    double value = static_cast<double>(integerPart) / std::pow(10, decimalPlaces);
                    qDebug() << "Converted value:" << value;
                    qDebug() << "Converted value:" << QString::number(value, 'f', decimalPlaces);
                } else {
                    qWarning() << "The response is too short to extract the specific data part.";
                }

            } else {
                qWarning() << "CRC check failed. Received CRC:" << receivedCrc << "Calculated CRC:" << calculatedCrc;
            }
        }

        if (!responseData.isEmpty()) {
            qWarning() << "Remaining data after processing:" << responseData.toHex();
        }
    } else {
        qWarning() << "receiveModbusResponse";
    }

    return -1;
}




double SerialPortManager::sendModbus_receive(const QString &hexCommand ,int RESPONSE_LENGTH) {
    if (serialPort && serialPort->isOpen()) {
        QByteArray command = hexStringToByteArray(hexCommand);
        uint16_t crc = CRC16_Calculate(command);

        command.append(static_cast<char>(crc & 0xFF));          //低
        command.append(static_cast<char>((crc >> 8) & 0xFF));   //高

        serialPort->write(command);


        if (serialPort->waitForBytesWritten(1000)) {
            return receiveModbusResponse(RESPONSE_LENGTH);
        } else {
            qWarning() << "Failed to write command to serial port.";
        }
    } else {
        qWarning() << "sendModbus_receive Serial port is not open!";
    }
    return -1;
}

bool SerialPortManager::sendModbus_receive_2(const QString &hexCommand, int RESPONSE_LENGTH) {
    if (serialPort && serialPort->isOpen()) {
        QByteArray command = hexStringToByteArray(hexCommand);
        uint16_t crc = CRC16_Calculate(command);

        command.append(static_cast<char>(crc & 0xFF));          //低
        command.append(static_cast<char>((crc >> 8) & 0xFF));   //高

        serialPort->write(command);

        if (serialPort->waitForBytesWritten(1000)) {
            QByteArray responseData = serialPort->readAll();
            while (serialPort->waitForReadyRead(100)) {
                responseData += serialPort->readAll();
            }

//            qDebug() << "Raw response data:" << responseData.toHex();

            while (responseData.size() >= RESPONSE_LENGTH) {
                QByteArray singleResponse = responseData.left(RESPONSE_LENGTH);
                responseData.remove(0, RESPONSE_LENGTH);

//                qDebug() << "Processing single response:" << singleResponse.toHex();

                uint16_t receivedCrc = static_cast<uint8_t>(singleResponse[singleResponse.size() - 2]) |
                                       (static_cast<uint8_t>(singleResponse[singleResponse.size() - 1]) << 8);

                singleResponse.chop(2);

                uint16_t calculatedCrc = CRC16_Calculate(singleResponse);

//                qDebug() << "Data without CRC:" << singleResponse.toHex();
//                qDebug() << "Received CRC:" << QString::number(receivedCrc, 16).toUpper();
//                qDebug() << "Calculated CRC:" << QString::number(calculatedCrc, 16).toUpper();

                if (receivedCrc == calculatedCrc) {
//                    qDebug() << "Received valid response with correct CRC:" << singleResponse;
                    return true;
                } else {
                    qWarning() << "CRC check failed. Received CRC:" << receivedCrc << "Calculated CRC:" << calculatedCrc;
                    return false;
                }
            }

            if (!responseData.isEmpty()) {
                qWarning() << "Remaining data after processing:" << responseData.toHex();
            }
        } else {
            qWarning() << "Failed to write command to serial port.";
        }
    } else {
        qWarning() << "Serial port is not open!";
    }
    return false;
}
