#include "generic_platform_scale.h"

#include "common/sys_util.h"

namespace zsy
{
    GenericPlatformScaleProcessor::GenericPlatformScaleProcessor()
        : steady(0),
          weight(0.0)
    {
    }

    uint32_t GenericPlatformScaleProcessor::process(const std::vector<uint8_t> &buffer)
    {
        size_t bufferSize = buffer.size();
        if (bufferSize < 12)
        {
            return 0;
        }

        // LOGGER_INFO("处理数据：{}", data(buffer));

        if (buffer[0] != 0x02 || buffer[11] != 0x03)
        {
            LOGGER_INFO("地磅数据格式错误：{}", data(buffer));

            for (uint32_t i = 0; i < bufferSize; i++)
            {
                if (buffer[i] == 0x02 && i + 11 < bufferSize && buffer[i + 11] == 0x03)
                {
                    LOGGER_WARN("地磅数据格式错误，已丢弃：{}", data(buffer,i + 1));
                    return i;
                }
            }
            return 0;
        }

        uint8_t checkCode = buffer[1];
        for (uint8_t i = 2; i < 9; i++)
        {
            checkCode ^= buffer[i];
        }
        if (std::string{static_cast<char>(buffer[9]), static_cast<char>(buffer[10])} != SysUtil::toHex(checkCode))
        {
            LOGGER_WARN("地磅数据校验失败，已丢弃：{}", data(buffer));
            return 12;
        }
        double tempWeight = 0.0;
        double tempData = 0;
        for (uint8_t i = 2; i < 8; i++)
        {
            tempData += (buffer[i] - 0x30) * std::pow<uint32_t>(10, 7 - i);
        }
        tempWeight = tempData / std::pow<uint32_t>(10, buffer[8] - 0x30);
        if (buffer[1] == 0x2D)
        {
            tempWeight = -tempWeight;
        }

        if (tempWeight == weight.load())
        {
            ++steady;
        } else
        {
            weight.store(tempWeight);
            steady.store(0);
        }
        return 12;
    }

    std::string GenericPlatformScaleProcessor::data(const std::vector<uint8_t> &buffer, uint8_t count)
    {
        std::string dataStr;
        for (uint32_t j = 0; j < count; j++)
        {
            dataStr += " " + SysUtil::toHex(buffer[j]);
        }
        return dataStr;
    }

    GenericPlatformScale::GenericPlatformScale(const PlatformScaleProperties &config)
        : status(0),
          config(config),
          serialPort(new SerialPort(config.port, config.baudRate, config.byteSize, config.parity, config.stopBits, new GenericPlatformScaleProcessor()))
    {
        std::unique_lock lock(status_mtx);
        status = 1;
    }

    double GenericPlatformScale::reading()
    {
        // 02 2B 30 30 32 30 30 30 32 31 42 03
        std::shared_lock lock(status_mtx);
        try
        {
            LOGGER_INFO("正在读取地磅数据，设备名称：{}", config.name);

            int retry_count = 0;
            while ((serialPort->status.load() == 3 || serialPort->readStatus.load() == 3) && retry_count < 10)
            {
                retry_count++;
                LOGGER_INFO("无法称重，串口异常，正在重启串口...");
                delete serialPort;
                serialPort = new SerialPort(config.port, config.baudRate, config.byteSize, config.parity, config.stopBits, new GenericPlatformScaleProcessor());
                std::this_thread::sleep_for(std::chrono::milliseconds(3000));
            }

            if (serialPort->status.load() == 3 || serialPort->readStatus.load() == 3)
            {
                LOGGER_ERROR("无法称重，串口异常，重试失败");
                return 0.0;
            }

            LOGGER_INFO("正在称重...");
            if (config.delay > 0) std::this_thread::sleep_for(std::chrono::milliseconds(config.delay * 1000));
            auto processor = static_cast<GenericPlatformScaleProcessor *>(serialPort->processor);
            int count = 0;
            while (processor->steady.load() < config.sample || processor->weight.load() <= config.weight)
            {
                count++;

                LOGGER_INFO("第 {} 次称重", count);

                if (count >= config.retries)
                {
                    LOGGER_ERROR("称重失败，连续 {} 次未稳定或磅重低于阈值 {} KG，当前磅重：{} KG", count, config.weight, processor->weight.load());
                    return 0.0;
                }
                std::this_thread::sleep_for(std::chrono::milliseconds(3000));
            }
            auto res = processor->weight.load();
            LOGGER_INFO("称重完成：{} KG", res);
            return res;
        } catch (std::exception &e)
        {
            LOGGER_ERROR("称重失败：{}", e.what());
            status = 4;
            return 0.0;
        } catch (...)
        {
            LOGGER_ERROR("未知异常，称重失败");
            status = 4;
            return 0.0;
        }
    }

    GenericPlatformScale::~GenericPlatformScale()
    {
        LOGGER_INFO("正在停止地磅...");
        std::unique_lock lock(status_mtx);
        status = 3;
        delete serialPort;
        serialPort = nullptr;
        LOGGER_INFO("地磅已停止");
    }
} // zsy
