#include "RS485Controller.h"
#include "DataTypeDef.h"
#include "LoggerHelper.h"

#include <sys/ioctl.h>
#include <linux/serial.h>
#include <unistd.h>

RS485Controller::RS485Controller(QObject *parent)
    : QObject{parent}
    , m_serialPort(new QSerialPort())
    , m_workerThread(new QThread(this))
{
    // 将窗口对象移动到工作线程
    m_serialPort->moveToThread(m_workerThread);

    // 连接信号槽
    connect(m_workerThread, &QThread::finished, m_serialPort, &QSerialPort::deleteLater);
    connect(m_serialPort, &QSerialPort::readyRead, this, &RS485Controller::handleReadRead);
    connect(m_serialPort, &QSerialPort::errorOccurred, this, &RS485Controller::handleError);
    
    // 初始化响应定时器
    QMetaObject::invokeMethod(this, &RS485Controller::setupResponseTimer, Qt::BlockingQueuedConnection);

    // 启动工作线程
    m_workerThread->start();
}

RS485Controller::~RS485Controller()
{
    closePort();
    m_workerThread->quit();
    m_workerThread->wait();
    
    if (m_responseTimer) {
        m_responseTimer->deleteLater();
    }
}

void RS485Controller::setupResponseTimer()
{
    m_responseTimer = new QTimer(this);
    m_responseTimer->setSingleShot(true);
    connect(m_responseTimer, &QTimer::timeout, this, &RS485Controller::handleTimeout);
}

bool RS485Controller::openPort(const QString &portName, qint32 baudRate, QSerialPort::DataBits dataBits, QSerialPort::Parity parity, QSerialPort::StopBits stopBits, QSerialPort::FlowControl flowControl)
{
    bool result = false;
    QMetaObject::invokeMethod(m_serialPort, [this, portName, baudRate, dataBits, parity, stopBits, flowControl, &result]() {
        m_serialPort->setPortName(portName);

        // 配置 RS485 模式
        auto fd = m_serialPort->handle();
        struct serial_rs485 rs485conf;
        memset(&rs485conf, 0, sizeof(serial_rs485));
        rs485conf.flags |= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND;
        rs485conf.delay_rts_before_send = 1;
        rs485conf.delay_rts_after_send = 1;

        if (ioctl(fd, TIOCSRS485, &rs485conf) < 0) {
            emit errorOccurred(QString("Failed to configure RS485 mode: %1").arg(strerror(errno)));
        }

        // 打开并配置串口
        if (m_serialPort->open(QIODevice::ReadWrite)) {
            m_serialPort->setBaudRate(baudRate);
            m_serialPort->setDataBits(dataBits);
            m_serialPort->setParity(parity);
            m_serialPort->setStopBits(stopBits);
            m_serialPort->setFlowControl(flowControl);

            result = true;

            // 启动发送队列处理
            QMetaObject::invokeMethod(this, &RS485Controller::processSendQueue, Qt::QueuedConnection);
        } else {
            emit errorOccurred(QString("Failed to open port : %1").arg(m_serialPort->errorString()));
            result = false;
        }
    }, Qt::BlockingQueuedConnection);

    return result;
}

void RS485Controller::closePort()
{
    QMutexLocker locker(&m_mutex);
    m_stopped = true;
    m_queueNotEmpty.wakeAll();

    QMetaObject::invokeMethod(m_serialPort, &QSerialPort::close, Qt::BlockingQueuedConnection);
}

void RS485Controller::sendData(const QByteArray &data)
{
    QMutexLocker locker(&m_mutex);
    if (m_stopped) return;
    
    m_sendQueue.enqueue(data);
    m_queueNotEmpty.wakeAll();
}

bool RS485Controller::sendData(const QByteArray &data, int timeout)
{
    QMutexLocker locker(&m_responseMutex);
    
    // 发送数据
    sendData(data);
    
    // 设置等待响应状态
    m_waitingResponse = true;
    
    // 启动超时定时器
    QMetaObject::invokeMethod(m_responseTimer, [this, timeout]() {
        m_responseTimer->start(timeout);
    }, Qt::QueuedConnection);
    
    // 等待响应或超时
    m_responseCondition.wait(&m_responseMutex);
    
    // 返回是否收到响应
    return !m_waitingResponse;
}

void RS485Controller::handleReadRead()
{
    QByteArray data = m_serialPort->readAll();
    while (m_serialPort->waitForReadyRead(NFC_RS485_TX_TIMEOUT * 1000)) {
        data += m_serialPort->readAll();
    }
    
    // 触发数据接收信号
    emit dataReceived(data);
    
    // 如果正在等待响应
    QMutexLocker locker(&m_responseMutex);
    if (m_waitingResponse) {
        m_waitingResponse = false;
        if (m_responseTimer && m_responseTimer->isActive()) {
            QMetaObject::invokeMethod(m_responseTimer, &QTimer::stop, Qt::QueuedConnection);
        }
        m_responseCondition.wakeAll();
        emit responseReceived(data);
    }

    LoggerHelper::intance()->debug("rs485 recv data : " + data.toHex(' ').toUpper());
}

void RS485Controller::handleTimeout()
{
    QMutexLocker locker(&m_responseMutex);
    if (m_waitingResponse) {
        m_waitingResponse = false;
        emit errorOccurred("Timeout waiting for response");
        m_responseCondition.wakeAll();
    }
}

void RS485Controller::handleError(QSerialPort::SerialPortError error)
{
    if (error == QSerialPort::ResourceError) {
        emit errorOccurred("Critical error: " + m_serialPort->errorString());
        closePort();
        
        // 如果正在等待响应，则唤醒
        QMutexLocker locker(&m_responseMutex);
        if (m_waitingResponse) {
            m_waitingResponse = false;
            m_responseCondition.wakeAll();
        }
    }
}

void RS485Controller::processSendQueue()
{
    while(true) {
        QMutexLocker locker(&m_mutex);

        while (m_sendQueue.isEmpty() && !m_stopped) {
            m_queueNotEmpty.wait(&m_mutex);
        }

        if (m_stopped) return;

        QByteArray data = m_sendQueue.dequeue();
        locker.unlock();

        // 实际发送数据
        QMetaObject::invokeMethod(m_serialPort, [this, data]() {
            qint64 byteWritten = m_serialPort->write(data);
            if (byteWritten == -1) {
                emit errorOccurred("Write failed: " + m_serialPort->errorString());
            } else if (byteWritten < data.size()) {
                emit errorOccurred("Write incoplete, expected " 
                    + QString::number(data.size()) 
                    + ", wrote " 
                    + QString::number(byteWritten));
            }

            if (!m_serialPort->waitForBytesWritten(NFC_RS485_TX_TIMEOUT * 1000)) {
                emit errorOccurred("Wait for write timed out");
            }

            LoggerHelper::intance()->debug("rs485 send data : " + data.toHex(' ').toUpper());
        }, Qt::BlockingQueuedConnection);
    }
}
