#include "M4255NFCHelper.h"

#include <QApplication>
#include <QCoreApplication>
#include <QMutex>
#include <QTime>
#include <QTimer>
#include <QDebug>
#include <QThread>
#include <QMutexLocker>
#include <QScopedPointer>
#include <QAtomicInt>
#include <QPointer>

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

namespace {
    constexpr const int DEFAULT_TIMEOUT = 1000;  // 默认超时时间（毫秒）
    constexpr const int MAX_RETRY_COUNT = 3;     // 最大重试次数
}

// 静态成员：全局串口打开计数器和互斥锁
static QAtomicInt g_nfc_open_count(0);
static QMutex g_nfc_open_mutex;

M4255NFCHelper::M4255NFCHelper(QObject *parent)
    : QObject{parent}
    , m_controller(new RS485Controller())
    , m_isInitialized(false)
    , m_retryCount(0)
    , m_isPortOpen(false)
{}

M4255NFCHelper::~M4255NFCHelper()
{
    closePort();
}

bool M4255NFCHelper::openPort(const QString &portName, qint32 baudRate, 
                             QSerialPort::DataBits dataBits, QSerialPort::Parity parity,
                             QSerialPort::StopBits stopBits, QSerialPort::FlowControl flowControl)
{
    if (!m_controller) {
        m_controller = new RS485Controller();
    }

    bool result = m_controller->openPort(portName, baudRate, dataBits, parity, stopBits, flowControl);
    if (result) {
        m_isInitialized = true;
        m_isPortOpen = true;
        m_isPortOpen = true;
        QMutexLocker locker(&g_nfc_open_mutex);
        g_nfc_open_count++;
    }
    return result;
}

int M4255NFCHelper::parseCardIdRecvData(const QByteArray &data, QByteArray &cardType, QByteArray &cardID)
{
    auto rxbyte = data.count();
    if (rxbyte == 12) {
        if (checkSum(data, data[1]) == data[11] &&
            data[4] == 0x00) {
            cardType.append(data.mid(5, 2));
            cardID.append(data.mid(7, 4));
            return 0;
        }
    } else if (rxbyte == 8) {
        if (checkSum(data, data[1]) != data[7] &&
            data[4] == 0x01) {
            return -3;
        }
    }

    return -2;
}

int M4255NFCHelper::parseReadDataBlockRecvData(const QByteArray& data, QByteArray &buff)
{
    auto rxbyte = data.count();
    if (rxbyte == 0x16) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x00) {
            buff.append(data.mid(5, 16));
            return 0;
        }
    } else if (rxbyte == 0x08) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x01) {
            return -3;
        }
    }
    return -2;
}

int M4255NFCHelper::parseFillDataBlockRecvData(const QByteArray &data)
{
    return generalRecvData(data);
}

int M4255NFCHelper::parseFillPinRecvData(const QByteArray &data)
{
    return generalRecvData(data);
}

int M4255NFCHelper::parseFillOrgRecvData(const QByteArray &data)
{
    return generalRecvData(data);
}

int M4255NFCHelper::parseFillPinBlockRecvData(const QByteArray &data)
{
    return generalRecvData(data);
}

int M4255NFCHelper::parseInitPrepaidICRecvData(const QByteArray &data)
{
    auto rxbyte = data.count();
    if (rxbyte == 0xA) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x00) {
            return 0;
        }
    } else if (rxbyte == 0x8) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x03) {
            return 0;
        }
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x01) {
            return -3;
        }
    }
    return -2;
}

int M4255NFCHelper::parseSubPrepaidICRecvData(const QByteArray &data)
{
    auto rxbyte = data.count();
    if (rxbyte == 0xA) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x00) {
            return 0;
        }
    } else if (rxbyte == 0x8) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x03) {
            return 0;
        }
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x01) {
            return -3;
        }
    }
    return -2;
}

int M4255NFCHelper::parseAddPrepaidICRecvData(const QByteArray &data)
{
    auto rxbyte = data.count();
    if (rxbyte == 0xA) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x00) {
            return 0;
        }
    } else if (rxbyte == 0x8) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x03) {
            return 0;
        }
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x01) {
            return -3;
        }
    }
    return -2;
}

int M4255NFCHelper::parseReadPrepaidICRecvData(const QByteArray &data, uint32_t &value)
{
    auto rxbyte = data.count();
    if (rxbyte == 0xA) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x00) {
            value = data[5] |
                    (data[6] << 8) |
                    (data[7] << 16) |
                    (data[8] << 24);
            return 0;
        }
    } else if (rxbyte == 0x8) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] &&
            data[4] == 0x01) {
            return -3;
        }
    }
    return -2;
}

int M4255NFCHelper::parseSetBeepRecvData(const QByteArray &data)
{
    return generalRecvData(data);
}

int M4255NFCHelper::parseForceBeepRecvData(const QByteArray &data)
{
    return generalRecvData(data);
}

int M4255NFCHelper::parseSetWorkModeRecvData(const QByteArray &data)
{
    return generalRecvData(data);
}

int M4255NFCHelper::parseSetReadOnceRecvData(const QByteArray &data)
{
    return generalRecvData(data);
}

int M4255NFCHelper::generalRecvData(const QByteArray &data)
{
    auto rxbyte = data.count();
    if (rxbyte == 8) {
        if (checkSum(data, data[1]) == data[rxbyte - 1]) {
            if (data[4] == 0x00) {
                return 0;
            } else if (data[4] == 0x01) {
                LOG_WARNING("The command execution failed.");
                return -3;
            } else {
                return -4;
            }
        }
    }
    LOG_WARNING("The data length does not conform to the standard.");
    return -2;
}

void M4255NFCHelper::closePort()
{
    if (!m_controller) return;
    if (m_isPortOpen) {
        QMutexLocker locker(&g_nfc_open_mutex);
        if (g_nfc_open_count > 0) g_nfc_open_count--;
        m_isPortOpen = false;
    }
    m_controller->closePort();
    m_isInitialized = false;
    m_retryCount = 0;
}

bool M4255NFCHelper::isPortOpen()
{
    // 只要有一个实例的串口已打开，则返回true
    return g_nfc_open_count > 0;
}

int M4255NFCHelper::init(const QString &portName)
{
    if (!m_controller) {
        m_controller = new RS485Controller();
    }

    if (!openPort(portName)) {
        LOG_ERROR("Failed to open port: " + portName);
        return -1;
    }

    // 连接信号槽
    connect(m_controller,    &RS485Controller::dataReceived,        this,   &M4255NFCHelper::parseReceiveData,        Qt::QueuedConnection);
    connect(m_controller,    &RS485Controller::errorOccurred,       this,   &M4255NFCHelper::parseErrorOccurred,      Qt::QueuedConnection);

    return 0;
}

// 通用发送数据函数
int M4255NFCHelper::sendCommand(const QByteArray& command, int timeout)
{
    if (!m_isInitialized || !m_controller) {
        LOG_ERROR("Device not initialized");
        return -1;
    }

    m_retryCount = 0;
    while (m_retryCount < MAX_RETRY_COUNT) {
        if (m_controller->sendData(command, m_recvData, timeout > 0 ? timeout : DEFAULT_TIMEOUT)) {
            return 0;
        }
        m_retryCount++;
        if (m_retryCount < MAX_RETRY_COUNT) {
            LOG_WARNING(QString("Retry sending command: %1/%2").arg(m_retryCount).arg(MAX_RETRY_COUNT));
            QThread::msleep(100); // 短暂延迟后重试
        }
    }
    return -2;
}

// 通用数据解析函数
int M4255NFCHelper::parseResponse(const QByteArray& data, int expectedLength, int successCode)
{
    if (data.isEmpty()) return -2;

    auto rxbyte = data.count();
    if (rxbyte == expectedLength) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] && data[4] == successCode) {
            return 0;
        }
    } else if (rxbyte == 8) {
        if (checkSum(data, data[1]) == data[rxbyte - 1] && data[4] == 0x01) {
            return -3;
        }
    }
    return -2;
}

int M4255NFCHelper::readCardId(QByteArray &cardType, QByteArray &cardID)
{
    QByteArray sendData;
    sendData.append(M4255_CARD_CMD);
    sendData.append(8);
    sendData.append(M4255_CMD_READ_CARDID);
    sendData.append(M4255_RS485ADDR);
    sendData.append(static_cast<char>(0x00));
    sendData.append(static_cast<char>(0x01));
    sendData.append(static_cast<char>(0x00));
    sendData.append(checkSum(sendData, sendData[1]));

    if (sendCommand(sendData, Constants::NFC_RS485_TX_TIMEOUT * 1000) == 0) {
        return parseCardIdRecvData(m_recvData, cardType, cardID);
    }
    return -2;
}

int M4255NFCHelper::readDataBlock(const uint8_t &useKeyB, const uint8_t blockId, QByteArray &buff)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_CARD_CMD;
    sendData[idx++] = 8;
    if (useKeyB) sendData[idx++] = M4255_CMD_READ_DATABLOCK_USEKEYB;
    else sendData[idx++] = M4255_CMD_READ_DATABLOCK_USEKEYA;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = blockId;
    sendData[idx++] = 0x01;
    sendData[idx++] = 0x00;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseReadDataBlockRecvData(m_recvData, buff);
    }
    return -2;
}

int M4255NFCHelper::fillDataBlock(const uint8_t &useKeyB, const uint8_t blockId, const QByteArray& data)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_CARD_CMD;
    sendData[idx++] = 0x17;
    if (useKeyB) sendData[idx++] = M4255_CMD_FILL_DATABLOCK_USEKEYB;
    else sendData[idx++] = M4255_CMD_FILL_DATABLOCK_USEKEYA;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = blockId;
    sendData[idx++] = 0x01;
    sendData.append(data);
    sendData.append(checkSum(sendData, sendData[1]));

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseFillDataBlockRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::fillPIN(const QByteArray &data)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_READER_SET;
    sendData[idx++] = 0x15;
    sendData[idx++] = M4255_CMD_FILL_PIN;
    sendData[idx++] = M4255_RS485ADDR;
    sendData.append(data);
    sendData.append(checkSum(sendData, sendData[1]));

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseFillPinRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::fillORG(const QByteArray &data)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_READER_SET;
    sendData[idx++] = 0x0B;
    sendData[idx++] = M4255_CMD_FILL_ORG;
    sendData[idx++] = M4255_RS485ADDR;
    sendData.append(data);
    sendData.append(checkSum(sendData, sendData[1]));

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseFillOrgRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::fillPINBlock(const uint8_t &useKeyB, const uint8_t &block_id, const QByteArray &keyA, const QByteArray &keyB)
{
    QByteArray orgpin;
    orgpin.append(0xFF);
    orgpin.append(0xFF);
    orgpin.append(0xFF);
    orgpin.append(0xFF);
    orgpin.append(0xFF);
    orgpin.append(0xFF);

    if (fillORG(orgpin) != 0) {
        return -5;
    }

    QByteArray data;
    data.append(keyA);
    data.append(0xFF);
    data.append(0x07);
    data.append(0x80);
    data.append(0x69);
    data.append(keyB);
    if (fillPIN(data) != 0) {
        return -6;
    }

    QByteArray sendData;
    sendData.append(M4255_CARD_CMD);
    sendData.append(0x08);
    if (useKeyB) sendData.append(M4255_CMD_FILL_PINBLOCK_USEKEYB);
    else sendData.append(M4255_CMD_FILL_PINBLOCK_USEKEYA);
    sendData.append(M4255_RS485ADDR);
    sendData.append(block_id);
    sendData.append(0x01);
    sendData[6] = 0x00;
    sendData.append(checkSum(sendData, sendData[1]));

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseFillPinBlockRecvData(m_recvData);
    }

    return -2;
}

int M4255NFCHelper::initPrepaidIC(const uint8_t &useKeyB, const uint8_t &blockId, const uint32_t &value)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_CARD_CMD;
    sendData[idx++] = 0x0B;
    if (useKeyB) sendData[idx++] = M4255_CMD_INIT_PREPAIDIC_USEKEYB;
    else sendData[idx++] = M4255_CMD_INIT_PREPAIDIC_USEKEYA;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = blockId;
    sendData[idx++] = 0x01;
    sendData[idx++] = (value >> 0) & 0xFF;
    sendData[idx++] = (value >> 8) & 0xFF;
    sendData[idx++] = (value >> 16) & 0xFF;
    sendData[idx++] = (value >> 24) & 0xFF;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseInitPrepaidICRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::subPrepaidIC(const uint8_t &useKeyB, const uint8_t &blockId, const uint32_t &value)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_CARD_CMD;
    sendData[idx++] = 0x0B;
    if (useKeyB) sendData[idx++] = M4255_CMD_SUB_PREPAIDIC_USEKEYB;
    else sendData[idx++] = M4255_CMD_SUB_PREPAIDIC_USEKEYA;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = blockId;
    sendData[idx++] = 0x01;
    sendData[idx++] = (value >> 0) & 0xFF;
    sendData[idx++] = (value >> 8) & 0xFF;
    sendData[idx++] = (value >> 16) & 0xFF;
    sendData[idx++] = (value >> 24) & 0xFF;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseSubPrepaidICRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::addPrepaidIC(const uint8_t &useKeyB, const uint8_t &blockId, const uint32_t &value)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_CARD_CMD;
    sendData[idx++] = 0x0B;
    if (useKeyB) sendData[idx++] = M4255_CMD_ADD_PREPAIDIC_USEKEYB;
    else sendData[idx++] = M4255_CMD_ADD_PREPAIDIC_USEKEYA;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = blockId;
    sendData[idx++] = 0x01;
    sendData[idx++] = (value >> 0) & 0xFF;
    sendData[idx++] = (value >> 8) & 0xFF;
    sendData[idx++] = (value >> 16) & 0xFF;
    sendData[idx++] = (value >> 24) & 0xFF;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseAddPrepaidICRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::readPrepaidIC(const uint8_t &useKeyB, const uint8_t &blockId, uint32_t &value)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_CARD_CMD;
    sendData[idx++] = 0x08;
    if (useKeyB) sendData[idx++] = M4255_CMD_ADD_PREPAIDIC_USEKEYB;
    else sendData[idx++] = M4255_CMD_ADD_PREPAIDIC_USEKEYA;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = blockId;
    sendData[idx++] = 0x01;
    sendData[idx++] = 0x00;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseReadPrepaidICRecvData(m_recvData, value);
    }
    return -2;
}

int M4255NFCHelper::setBeep(const uint8_t &on)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_READER_SET;
    sendData[idx++] = 0x08;
    sendData[idx++] = M4255_CMD_SET_BEEP;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = on;
    sendData[idx++] = 0x00;
    sendData[idx++] = 0x00;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseSetBeepRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::forceBeep(const uint8_t &on)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_OTHER_CMD;
    sendData[idx++] = 0x08;
    if (on) sendData[idx++] = M4255_CMD_FORCE_BEEP_ON;
    else sendData[idx++] = M4255_CMD_FORCE_BEEP_OFF;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = 0x00;
    sendData[idx++] = 0x00;
    sendData[idx++] = 0x00;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseForceBeepRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::setWorkMode(const uint8_t &mode)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_READER_SET;
    sendData[idx++] = 0x08;
    sendData[idx++] = M4255_CMD_SET_WORK_MODE;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = mode;
    sendData[idx++] = 0x00;
    sendData[idx++] = 0x00;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseSetWorkModeRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::setReadOnce(const uint8_t &type)
{
    int idx = 0;
    QByteArray sendData;
    sendData[idx++] = M4255_READER_SET;
    sendData[idx++] = 0x08;
    sendData[idx++] = M4255_CMD_SET_READ_ONCE;
    sendData[idx++] = M4255_RS485ADDR;
    sendData[idx++] = type;
    sendData[idx++] = 0x00;
    sendData[idx++] = 0x00;
    sendData[idx++] = checkSum(sendData, sendData[1]);

    if (m_controller->sendData(sendData, m_recvData, Constants::NFC_RS485_TX_TIMEOUT * 1000)) {
        return parseSetReadOnceRecvData(m_recvData);
    }
    return -2;
}

int M4255NFCHelper::waitReadCardId(QByteArray &cardId, int timeout)
{
    if (!m_isInitialized) {
        LOG_ERROR("Device not initialized");
        return -1;
    }

    QEventLoop loop;
    QTimer timeoutTimer;
    timeoutTimer.setSingleShot(true);
    
    // 用 QPointer 防止悬挂指针
    QPointer<QEventLoop> loopPtr(&loop);
    QPointer<QTimer> timerPtr(&timeoutTimer);

    QMetaObject::Connection conn = connect(m_controller, &RS485Controller::serialPortClosed, this, [loopPtr, timerPtr]() {
        // 判断对象是否还活着
        if (timerPtr && timerPtr->isActive()) {
            timerPtr->stop();
        }
        if (loopPtr && loopPtr->isRunning()) {
            loopPtr->quit();
        }
    });

    connect(this, &M4255NFCHelper::signalWaitReadCardId, &loop, &QEventLoop::quit);
    connect(&timeoutTimer, &QTimer::timeout, &loop, &QEventLoop::quit);
    
    timeoutTimer.start(timeout > 0 ? timeout : DEFAULT_TIMEOUT);
    loop.exec();

    // 事件循环结束后，断开连接，防止悬挂
    QObject::disconnect(conn);

    if (m_recvData.isEmpty()) {
        LOG_WARNING("No card data received");
        return -2;
    }

    auto rxbyte = m_recvData.count();
    if (checkSum(m_recvData, m_recvData[1]) == m_recvData[rxbyte - 1]) {
        if ((rxbyte == 0xC || rxbyte == 0x1C) && m_recvData.data()[4] == 0x00) {
            cardId.clear();
            cardId.append(m_recvData.mid(5, 6));
            return 0;
        }
        return -1;
    }

    return -2;
}

uint8_t M4255NFCHelper::checkSum(const QByteArray &data, int len)
{
    if (data.isEmpty() || len <= 0) return 0;
    
    uint8_t sum = 0;
    for (int i = 0; i < len - 1 && i < data.size(); i++) {
        sum ^= data[i];
    }
    return ~sum;
}

void M4255NFCHelper::parseResponseReceived(const QByteArray &data)
{
    m_recvData = data;
}

void M4255NFCHelper::parseReceiveData(const QByteArray &data)
{
    m_recvData = data;

    qDebug() << "RS485 recv data : " << data.toHex(' ');

    if (m_recvData.count() == 0xC || m_recvData.count() == 0x1C) {
        emit signalWaitReadCardId();
    }
}

void M4255NFCHelper::parseErrorOccurred(const QString &errorString)
{
    LOG_ERROR("NFC Error: " + errorString);
    m_retryCount = 0;
}
