#include "serial_port.h"

#include "common/spdlog/logging.h"
#include "common/utils/thread_pool.h"

namespace zsy
{
    SerialPort::DCB::DCB(const std::string &portName,
                         const int32_t baudRate,
                         const int32_t dataBits,
                         const int32_t stopBits,
                         const int32_t parity,
                         const int32_t flowControl)
        : portName(QString::fromStdString(portName)), baudRate(static_cast<QSerialPort::BaudRate>(baudRate)),
          dataBits(static_cast<QSerialPort::DataBits>(dataBits)), stopBits(static_cast<QSerialPort::StopBits>(stopBits)),
          parity(static_cast<QSerialPort::Parity>(parity)), flowControl(static_cast<QSerialPort::FlowControl>(flowControl))
    {
    }

    SerialPort::SerialPort(DCB dcb, const std::function<int32_t(const QByteArray & buf)> &processor, QObject *parent)
        : QObject(parent),
          port(nullptr),
          dcb(std::move(dcb)),
          buf(std::make_shared<QByteArray>()),
          processor(processor),
          stopped(false)
    {
        open();
    }

    SerialPort::~SerialPort()
    {
        stopped = true;
        close();
    }

    void SerialPort::open()
    {
        if (stopped.load())return;
        LOGGER_INFO("正在打开串口，串口号：{}", dcb.portName.toStdString());
        port = QSharedPointer<QSerialPort>(new QSerialPort());
        connect(port.get(), &QSerialPort::readyRead, this, &SerialPort::onData);
        connect(port.get(), &QSerialPort::errorOccurred, this, &SerialPort::onError);
        port->setPortName(dcb.portName);
        port->setBaudRate(dcb.baudRate);
        port->setDataBits(dcb.dataBits);
        port->setStopBits(dcb.stopBits);
        port->setParity(dcb.parity);
        port->setFlowControl(dcb.flowControl);

        if (port->open(QIODevice::ReadOnly))
        {
            // status = Available;
            LOGGER_INFO("已打开串口，串口号：{}，波特率：{}，数据位：{}，停止位：{}，校验位：{}，流控：{}",
                        dcb.portName.toStdString(),
                        std::to_string(dcb.baudRate),
                        std::to_string(dcb.dataBits),
                        std::to_string(dcb.stopBits),
                        std::to_string(dcb.parity),
                        std::to_string(dcb.flowControl)
            );
        } else
        {
            // status = Unavailable;
            LOGGER_ERROR("串口打开失败，错误：{}，串口号：{}，波特率：{}，数据位：{}，停止位：{}，校验位：{}，流控：{}",
                         port->errorString().toStdString(),
                         dcb.portName.toStdString(),
                         std::to_string(dcb.baudRate),
                         std::to_string(dcb.dataBits),
                         std::to_string(dcb.stopBits),
                         std::to_string(dcb.parity),
                         std::to_string(dcb.flowControl)
            );
        }
    }


    void SerialPort::close()
    {
        LOGGER_INFO("正在关闭串口，串口号：{}", dcb.portName.toStdString());
        if (port != nullptr)
        {
            if (port->isOpen())
            {
                port->close();
            }
            disconnect(port.get(), &QSerialPort::readyRead, this, &SerialPort::onData);
            disconnect(port.get(), &QSerialPort::errorOccurred, this, &SerialPort::onError);
            port.reset();
        }
        buf.reset();
        LOGGER_INFO("已关闭串口，串口号：{}", dcb.portName.toStdString());
    }

    bool SerialPort::canUse()
    {
        return !stopped.load();
    }

    void SerialPort::onData() const
    {
        if (stopped.load())return;
        if (const QByteArray data = port->readAll(); !data.isEmpty())
        {
            *buf += data;
            int32_t len = 0;
            while (true)
            {
                try
                {
                    len = processor(*buf);
                } catch (std::exception &e)
                {
                    LOGGER_ERROR("串口数据解析失败，串口号：{}，错误：{}", dcb.portName.toStdString(), e.what());
                } catch (...)
                {
                    LOGGER_ERROR("串口数据解析失败，串口号：{}，错误：未知异常", dcb.portName.toStdString());
                }
                if (len > 0)
                {
                    buf->remove(0, len);
                    if (!buf->isEmpty()) continue;
                }
                break;
            }
        }
    }

    void SerialPort::onError(const QSerialPort::SerialPortError error)
    {
        if (error != QSerialPort::NoError)
        {
            stopped = true;
            LOGGER_ERROR("串口操作失败，串口号：{}，错误：{}", port->portName().toStdString(), port->errorString().toStdString());
            ThreadPool::submit([this]
            {
                LOGGER_WARN("串口操作失败，串口号：{}，错误：{}，将在10秒后重新打开", port->portName().toStdString(), port->errorString().toStdString());
                std::this_thread::sleep_for(std::chrono::seconds(10));
                Q_EMIT sperr();
            });
        }
    }
} // zsy