#include "lock.h"
#include "config.h"
#include <QtCore>
#include <QDebug>

extern config *pconfig;

lock::lock(QObject *parent) : QThread(parent)
{
    QLoggingCategory::setFilterRules("qt.serialport*=true");
}

void lock::slot_set_thread_stop(void)
{
    this->threadstatus = false;
}

#define PRESET_VALUE 0xFFFF
#define POLYNOMIAL  0xA001
quint16 lock::uiCrc16Cal(QByteArray &data, quint8 count)
{
    quint8 ucI,ucJ;
    quint16 uiCrcValue = PRESET_VALUE;
    for(ucI = 0; ucI < count; ucI++)
    {
        uiCrcValue = uiCrcValue ^ (quint8)(data.at(ucI));
        for(ucJ = 0; ucJ < 8; ucJ++)
        {
            if(uiCrcValue & 0x0001)
            {
                uiCrcValue = (uiCrcValue >> 1) ^ POLYNOMIAL;
            }
            else
            {
                uiCrcValue = (uiCrcValue >> 1);
            }
        }
    }
    return uiCrcValue;
}

// 发送数据给下位机
void lock::sendData(QByteArray &sendData)
{
    // qDebug() << "sendData: " << sendData.toHex();
    quint16 crc = uiCrc16Cal(sendData, sendData.size());
    // 输出CRC16校验和
    // qDebug() << "CRC16:" << QString::number(crc, 16).toUpper();
    sendData.append((quint8)crc);
    sendData.append((quint8)(crc >> 8));
    // qDebug() << "QByteArray: " << sendData.toHex();
    // 发送数据帧
    // this->devserial->clear(QSerialPort::AllDirections);
    qint64 bytesWritten = this->devserial->write(sendData);
    if (bytesWritten == -1) {
        qWarning() << "Failed to write data:" << this->devserial->errorString();
    }

    // 等待数据写入完成
    if (!this->devserial->waitForBytesWritten(1000)) {
        qWarning() << "Timeout: Failed to write data within 5000 milliseconds.";
    }
}

// 读取下位机发来数据
bool lock::readData(QByteArray *data, quint16 timeout)
{
    if(!this->devserial->waitForReadyRead(timeout))
    {
        return false;
    }
    msleep(100);
    *data = this->devserial->readAll();

    qDebug() << "readData: " << data->toHex();
    quint8 addr = data->at(0);
    if(addr == ELECTLOCKBOARD_ADDR || addr == 0x90)
    {
        quint16 crc1 = uiCrc16Cal(*data, data->size() - 2);
        quint16 crc2 = quint8(data->at(data->size() - 1));
        crc2 <<= 8;
        crc2 += quint8(data->at(data->size() - 2));
        if(crc1 == crc2)
        {
            return true;
        }
    }
    return false;
}

bool lock::openSerial(void)
{
    this->devserial->setPortName(pconfig->lockPort); // 设定串口名称，根据你的系统修改
    this->devserial->setBaudRate(pconfig->lockBaud);
    this->devserial->setDataBits(QSerialPort::Data8);
    this->devserial->setParity(QSerialPort::NoParity);
    this->devserial->setStopBits(QSerialPort::OneStop);
    this->devserial->setFlowControl(QSerialPort::NoFlowControl);
    // this->devserial->setTextModeEnabled(true);

    if (this->devserial->open(QIODevice::ReadWrite)) {
        qDebug() << "电磁锁控板串口打开成功";
        this->devserial->setReadBufferSize(1024);
        //信号与槽函数关联
        // connect(devserial, &QSerialPort::readyRead, this, &uart::slot_uartReadData);
        return true;
    }
    return false;
}

void lock::slot_uartinfo_change(void)
{
    if(openPort == true)
    {
        this->devserial->close();
        openPort = false;
        workstat = lockWorkStat::init;
    }
}

bool lock::slot_openLock(void)
{
    if(workstat != lockWorkStat::init)
    {
        workstat = lockWorkStat::controlLock;
        return true;
    }
    else{
        return false;
    }
}

void lock::getBoardType(void)
{
    quint8 dataArray[] = {ELECTLOCKBOARD_ADDR, 0x03, DEVTYPE_REG >> 8, DEVTYPE_REG, 0x00, 0x01};
    QByteArray ba(reinterpret_cast<char*>(dataArray), sizeof(dataArray));
    this->sendData(ba);

    QByteArray data;
    if(this->devserial->waitForReadyRead(1000))
    {
        msleep(200);
        data = this->devserial->readAll();
        int size = data.size();
        int idx = 0;
        if(size == 7)
        {
            QByteArray lockData = data.mid(idx, 5);
            // qDebug() << "lockData: " << lockData.toHex();
            quint16 crc1 = uiCrc16Cal(lockData, 5);
            quint16 crc2 = quint8(data.at(6));
            crc2 <<= 8;
            crc2 += quint8(data.at(5));
            if(crc1 != crc2)
            {
                return;
            }

            if((quint8)(lockData.at(0)) != ELECTLOCKBOARD_ADDR)
            {
                return;
            }
            if(lockData.at(1) != 0x03)
            {
                return;
            }
            if(lockData.at(2) != 0x02)
            {
                return;
            }

            // quint16 boardtype = 0x00;
            // boardtype = lockData.at(3);
            // boardtype <<= 8;
            // boardtype += lockData.at(4);
            // if(boardtype != DEVTYPE_ELECTLOCK)
            // {
            //     break;
            // }
            workstat = lockWorkStat::readLockStat;
            emit signal_lock_run(true);
        }
    }
    else{
        qWarning() << "Timeout: No data received within 1000 milliseconds.";
    }
}

void lock::openLock(void)
{
    // quint8 dataArray[] = {0x90, 0x06, 0x00, 0x08, 0x00, 0x01};
    quint8 dataArray[] = {ELECTLOCKBOARD_ADDR, 0x06, LOCKCONTRO_REG >> 8, LOCKCONTRO_REG, 0x03, 0x00};
    QByteArray ba(reinterpret_cast<char*>(dataArray), sizeof(dataArray));
    this->sendData(ba);

    QByteArray data;
    if(true == this->readData(&data, 1000))
    {
        workstat = lockWorkStat::readLockStat;
    }
}

void lock::openLight(void)
{
    quint8 dataArray[] = {ELECTLOCKBOARD_ADDR, 0x06, LIGHTCONTROL_REG >> 8, LIGHTCONTROL_REG, 0x03, 0x00};
    if(setRightlightStat == true)
    {
        dataArray[5] = dataArray[5] | 0x02;
    }
    if(setLeftlightStat == true)
    {
        dataArray[5] = dataArray[5] | 0x01;
    }
    QByteArray ba(reinterpret_cast<char*>(dataArray), sizeof(dataArray));
    this->sendData(ba);

    QByteArray data;
    if(true == this->readData(&data, 1000))
    {
        workstat = lockWorkStat::readLockStat;
    }
}

void lock::readLockStat(void)
{
    quint8 dataArray[] = {ELECTLOCKBOARD_ADDR, 0x03, LOCKCONTRO_REG >> 8, LOCKCONTRO_REG, 0x00, 0x01};
    QByteArray ba(reinterpret_cast<char*>(dataArray), sizeof(dataArray));
    this->sendData(ba);

    QByteArray data;
    if(this->devserial->waitForReadyRead(1000))
    {
        msleep(200);
        data = this->devserial->readAll();
        int size = data.size();
        int idx = 0;
        if(size == 7)
        {
            QByteArray lockData = data.mid(idx, 5);
            // qDebug() << "lockData: " << lockData.toHex();
            quint16 crc1 = uiCrc16Cal(lockData, 5);
            quint16 crc2 = quint8(data.at(6));
            crc2 <<= 8;
            crc2 += quint8(data.at(5));
            if(crc1 != crc2)
            {
                return;
            }

            if((quint8)(lockData.at(0)) != ELECTLOCKBOARD_ADDR)
            {
                return;
            }
            if(lockData.at(1) != 0x03)
            {
                return;
            }
            if(lockData.at(2) != 0x02)
            {
                return;
            }

            bool right = false;
            bool left = false;

            left = ((lockData.at(4) & 0x01) == 0x01)?true:false;
            right = ((lockData.at(4) & 0x02) == 0x02)?true:false;

            if(left != leftLockStat || right != rightLockStat)
            {
                leftLockStat = left;
                rightLockStat = right;
                setLeftlightStat = (leftLockStat == false)?true:false;
                setRightlightStat = (rightLockStat == false)?true:false;
                workstat = lockWorkStat::controlLight;

                emit signal_lockStat(leftLockStat, rightLockStat);
            }
        }
    }
}

void lock::run()
{
    this->devserial = new QSerialPort;
    while(this->threadstatus)
    {
        if(pconfig->lockInit == true)
        {
            if(openPort == false)
            {
                openPort = this->openSerial();
            }
            else{
                switch(workstat)
                {
                case lockWorkStat::init:getBoardType();break;
                case lockWorkStat::readLockStat:readLockStat();break;
                case lockWorkStat::controlLock:openLock();break;
                case lockWorkStat::controlLight:openLight();break;
                }
            }
        }
        msleep(20);
    }
    if(openPort == true)
    {
        this->devserial->close();
    }
    qDebug() << "lock thread exit!";
}
