#include "loadometer.h"

#include <utility>

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

namespace zsy
{
    Loadometer::Loadometer(const LoadometerConfig &config, const std::shared_ptr<LoadometerDecoder> &decoder)
        : config(config),
          decoder(decoder),
          weight(0),
          lastUpdateTime(0),
          steady(0)
    {
    }

    int32_t Loadometer::currentWeight() const
    {
        if (!canUse())
        {
            LOGGER_ERROR("地磅：{}，不可用，无法读取数据", config.name());
            return 0;
        }
        uint32_t delay = config.delay();
        if (delay > 0)
        {
            LOGGER_INFO("将在 {} 秒后开始读取磅重", delay);
            delay = delay * 1000;
            std::this_thread::sleep_for(std::chrono::milliseconds(delay));
        }
        int32_t current = 0;

        uint32_t retry_delay = 0;

        for (int i = 1; i <= config.retries(); ++i)
        {
            if (retry_delay > 0)
            {
                LOGGER_INFO("将在 {} 秒后重新读取磅重", retry_delay);
                std::this_thread::sleep_for(std::chrono::seconds(retry_delay));
            }
            const int64_t timestamp = DateUtil::timestamp<std::chrono::seconds>();
            const uint64_t lastTime = lastUpdateTime.load();
            if (timestamp - lastTime > 5)
            {
                LOGGER_WARN("第 {} 次读取，磅重已超过 5 秒未更新", i);
                current = 0;
                retry_delay = config.retry_delay();
                continue;
            }
            if (steady.load() < config.steady())
            {
                LOGGER_WARN("第 {} 次读取，磅重不稳定", i);
                retry_delay = config.retry_delay();
                continue;
            }

            current = weight.load();
            if (uint32_t weight_line = config.weight_line(); current <= weight_line)
            {
                LOGGER_WARN("第 {} 次读取，磅重为 {}，小于最小阈值 {}", i, current, weight_line);
                current = 0;
                retry_delay = config.retry_delay();
                continue;
            }
            LOGGER_INFO("第 {} 次读取，当前磅重为 {}，成功", i, current);
            break;
        }
        return current;
    }

    void Loadometer::updateWeight(const int32_t newWeight)
    {
        if (!canUse())
        {
            LOGGER_ERROR("地磅：{}，不可用，无法更新数据", config.name());
            return;
        }
        const auto oldWeight = this->weight.load();
        int32_t delta = oldWeight - newWeight;
        int32_t accuracy = config.accuracy();
        if (-accuracy <= delta && delta <= accuracy)
        {
            ++steady;
        } else
        {
            steady.store(0);
        }
        this->weight.store(newWeight);
        const int64_t timestamp = DateUtil::timestamp<std::chrono::seconds>();
        this->lastUpdateTime.store(timestamp);
    }

    std::string Loadometer::sn() const
    {
        return config.sn();
    }

    std::string Loadometer::name() const
    {
        return config.name();
    }
} // zsy