#include "serial_port.h"

#include "common/logging.h"
#include "common/sys_util.h"

namespace zsy
{
    SerialPort::SerialPort(const std::string &port, uint32_t baudRate, uint8_t byteSize, uint8_t parity, uint8_t stopBits, Processor *processor)
        : buffer(new std::vector<uint8_t>()),
          status(0),
          readStatus(0),
          port(port),
          baudRate(baudRate),
          byteSize(byteSize),
          parity(parity),
          stopBits(stopBits),
          readThread(nullptr),
          processor(processor),
          com(INVALID_HANDLE_VALUE),
          waitOverlapped({.hEvent = CreateEvent(nullptr, false, false, nullptr)}),
          readOverlapped({.hEvent = CreateEvent(nullptr, false, false, nullptr)})
    {
        auto process_error = [this]
        {
            status.store(3);
            if (com != nullptr && com != INVALID_HANDLE_VALUE) CloseHandle(com);
            com = INVALID_HANDLE_VALUE;
            if (waitOverlapped.hEvent != nullptr && waitOverlapped.hEvent != INVALID_HANDLE_VALUE) CloseHandle(waitOverlapped.hEvent);
            waitOverlapped.hEvent = INVALID_HANDLE_VALUE;
            if (readOverlapped.hEvent != nullptr && readOverlapped.hEvent != INVALID_HANDLE_VALUE) CloseHandle(readOverlapped.hEvent);
            readOverlapped.hEvent = INVALID_HANDLE_VALUE;
        };
        this->com = CreateFile(SysUtil::str_a_w(port).c_str(),
                               GENERIC_READ,
                               0,
                               nullptr,
                               OPEN_EXISTING,
                               FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
                               nullptr);
        if (com == INVALID_HANDLE_VALUE)
        {
            auto [_,code,msg] = SysUtil::getError();
            LOGGER_ERROR("串口打开失败，串口号：{}，错误码：{}，错误信息：{}", port, code, msg);
            status.store(3);
            process_error();
            return;
        }

        SetupComm(com, 1024, 1024);

        DCB dcb{};
        if (!GetCommState(com, &dcb))
        {
            auto [_,code,msg] = SysUtil::getError();
            LOGGER_ERROR("串口状态获取失败，串口号：{}，错误码：{}，错误信息：{}", port, code, msg);
            process_error();
            return;
        }
        dcb.BaudRate = baudRate;
        dcb.ByteSize = byteSize;
        // 0-4=None,Odd,Even,Mark,Space
        dcb.Parity = parity;
        // 0,1,2 = 1, 1.5, 2
        dcb.StopBits = stopBits;
        if (!SetCommState(com, &dcb))
        {
            auto [_,code,msg] = SysUtil::getError();
            LOGGER_ERROR("串口状态设置失败，串口号：{}，错误码：{}，错误信息：{}", port, code, msg);
            process_error();
            return;
        }

        COMMTIMEOUTS comTimeOut;
        comTimeOut.ReadIntervalTimeout = MAXDWORD;
        comTimeOut.ReadTotalTimeoutMultiplier = 0;
        comTimeOut.ReadTotalTimeoutConstant = 0;
        comTimeOut.WriteTotalTimeoutMultiplier = 0;
        comTimeOut.WriteTotalTimeoutConstant = 0;
        if (!SetCommTimeouts(com, &comTimeOut))
        {
            auto [_,code,msg] = SysUtil::getError();
            LOGGER_ERROR("串口参数设置失败，串口号：{}，错误码：{}，错误信息：{}", port, code, msg);
            process_error();
            return;
        }

        if (!PurgeComm(com, PURGE_RXCLEAR | PURGE_TXCLEAR))
        {
            auto [_,code,msg] = SysUtil::getError();
            LOGGER_ERROR("串口缓冲区清理失败，串口号：{}，错误码：{}，错误信息：{}", port, code, msg);
            process_error();
            return;
        }

        if (!SetCommMask(com, EV_RXCHAR))
        {
            auto [_,code,msg] = SysUtil::getError();
            LOGGER_ERROR("串口读时间设置失败，串口号：{}，错误码：{}，错误信息：{}", port, code, msg);
            process_error();
            return;
        }
        status.store(1);
        readThread = new std::thread([this] { read(); });
    }

    void SerialPort::read()
    {
        try
        {
            LOGGER_INFO("开始读取串口数据，串口号：{}", port);
            readStatus.store(1);
            while (true)
            {
                if (status.load() != 1)
                {
                    LOGGER_ERROR("无法读取数据，串口不可用，串口号：{}", port);
                    readStatus.store(3);
                    return;
                }
                DWORD eventMask = 0;
                waitOverlapped.Offset = 0;
                const DWORD waitResult = WaitCommEvent(com, &eventMask, &waitOverlapped);
                if (waitResult == FALSE)
                {
                    auto error = SysUtil::getError();
                    if (std::get<1>(error) != ERROR_IO_PENDING)
                    {
                        LOGGER_INFO("串口已断开，停止读取数据，串口号：{}，错误码：{}，错误信息：{}", port, std::get<1>(error), std::get<2>(error));
                        break;
                    }
                    // LOGGER_INFO("等待数据，串口号：{}", port);
                    DWORD bytes = 0;
                    if (GetOverlappedResult(com, &waitOverlapped, &bytes, TRUE) == FALSE)
                    {
                        error = SysUtil::getError();
                        auto ss = std::get<2>(error);

                        LOGGER_ERROR("串口已断开，停止读取数据，串口号：{}，错误码：{}，错误信息：{}", port, std::get<1>(error), std::get<2>(error));
                        break;
                    }
                }

                if (eventMask & EV_RXCHAR != EV_RXCHAR) continue;

                COMSTAT comStat;
                DWORD errors;
                ClearCommError(com, &errors, &comStat);

                if (comStat.cbInQue > 0)
                {
                    // LOGGER_INFO("收到数据，串口号：{}，字节数：{}", port, comStat.cbInQue);
                    DWORD bytesRead = 0;
                    auto data = std::make_unique<uint8_t[]>(comStat.cbInQue);
                    auto dataPtr = data.get();
                    readOverlapped.Offset = 0;
                    if (ReadFile(com, dataPtr, comStat.cbInQue, &bytesRead, &readOverlapped))
                    {
                        this->buffer->insert(this->buffer->end(), dataPtr, dataPtr + bytesRead);
                        const auto processedBytes = processor->process(*this->buffer);
                        if (processedBytes > 0)this->buffer->erase(this->buffer->begin(), this->buffer->begin() + processedBytes);
                    } else
                    {
                        readStatus.store(3);
                        LOGGER_ERROR("读取数据失败，串口号：{}，错误码：{}", port, GetLastError());
                        break;
                    }
                } else
                {
                    // LOGGER_INFO("未读取到数据，串口号：{}", port);
                }
            }
            readStatus.store(2);
            LOGGER_INFO("停止读取串口数据，串口号：{}", port);
        } catch (const std::exception &e)
        {
            readStatus.store(3);
            LOGGER_ERROR("串口读取任务异常退出，串口号：{}，错误信息：{}", port, e.what());
        }
        catch (...)
        {
            readStatus.store(3);
            LOGGER_ERROR("未知异常，串口读取任务异常退出，串口号：{}", port);
        }
    }

    SerialPort::~SerialPort()
    {
        status.store(2);
        if (readThread != nullptr)
        {
            if (com != nullptr && com != INVALID_HANDLE_VALUE) CancelIoEx(com, nullptr);
            readThread->join();
            delete readThread;
            readThread = nullptr;
        }
        if (processor != nullptr)
        {
            delete processor;
            processor = nullptr;
        }
        buffer->clear();
        delete buffer;
        buffer = nullptr;
        if (com != nullptr && com != INVALID_HANDLE_VALUE) CloseHandle(com);
        com = INVALID_HANDLE_VALUE;
        if (waitOverlapped.hEvent != nullptr && waitOverlapped.hEvent != INVALID_HANDLE_VALUE) CloseHandle(waitOverlapped.hEvent);
        waitOverlapped.hEvent = INVALID_HANDLE_VALUE;
        if (readOverlapped.hEvent != nullptr && readOverlapped.hEvent != INVALID_HANDLE_VALUE) CloseHandle(readOverlapped.hEvent);
        readOverlapped.hEvent = INVALID_HANDLE_VALUE;
    }
} // zsy
