#include "ModuleCommunication.h"
#include "BoostLog.h"
#include "StringUtility.h"
#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <mbedtls/md5.h>
#include <sstream>
#ifdef WIN32
#include <WinSock2.h>
#else
#include <arpa/inet.h>
#endif

static inline uint8_t xor_checksum_byte(const uint8_t *data, uint32_t len) {
    uint8_t sum = 0;
    for (uint32_t i = 0; i < len; ++i) {
        sum ^= data[i];
    }
    return sum;
}

ModuleCommunication::ModuleCommunication(QObject *parent) : QObject{parent} {
}

bool ModuleCommunication::open(const QString &portName, int baudRate) {
    if (portName.isEmpty()) return false;
    bool ret = true;
    m_serialPort = std::make_shared<QSerialPort>(portName);
    m_serialPort->setBaudRate(baudRate);
    connect(m_serialPort.get(), &QSerialPort::readyRead, this, &ModuleCommunication::onReadyRead);
    connect(m_serialPort.get(), &QSerialPort::errorOccurred, this, &ModuleCommunication::onErrorOccurred);

    ret = m_serialPort->open(QSerialPort::ReadWrite);
    LOG_CAT(info, GUI) << "打开串口(" << portName.toStdString() << ")" << (ret ? "成功" : "失败")
                       << ", 波特率: " << baudRate;
    if (!ret) {
        LOG(error) << m_serialPort->errorString().toStdString();
    }
    LOG_CAT(info, GUI) << Separator;
    return ret;
}

void ModuleCommunication::verify(uint8_t timeout) {
    VerifyRequest data = {0};
    data.timeout = timeout;

    auto [frameData, frameSize] = generateFrame(Verify, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    setCurrentMessageIdStatus(Verify);

    LOG_CAT(info, GUI) << "发送识别指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::verifyExtended(bool captureImage, uint8_t timeout) {
    VerifyRequest data = {0};
    data.timeout = timeout;
    data.save_image = captureImage ? 0x01 : 0x00;
    auto [frameData, frameSize] = generateFrame(VerifyExtended, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    setCurrentMessageIdStatus(VerifyExtended);

    LOG_CAT(info, GUI) << "发送扩展识别指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::reset() {
    auto [frameData, frameSize] = generateFrame(Reset);
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    setCurrentMessageIdStatus(Reset);
    LOG_CAT(info, GUI) << "发送复位指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::enroll(const std::string &username, bool strictMode, uint8_t excludeMode, bool persistence,
                                 uint8_t timeout) {
    EnrollRequest data = {0};
    data.strictMode = strictMode ? 1 : 0;
    data.excludeMode = excludeMode;
    data.timeout = timeout;
    data.skipSave = persistence ? 0 : 1;
    strncpy(reinterpret_cast<char *>(data.username), username.c_str(), sizeof(data.username));
    auto [frameData, frameSize] = generateFrame(EnrollSingle, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    setCurrentMessageIdStatus(EnrollSingle);

    LOG_CAT(info, GUI) << "发送注册指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast<int>(timeout) << "s";
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::enrollExtended(const std::string &username, bool strictMode, uint8_t excludeMode,
                                         bool persistence, uint8_t timeout) {
    EnrollRequest data = {};
    data.strictMode = strictMode ? 1 : 0;
    data.excludeMode = excludeMode;
    data.timeout = timeout;
    data.skipSave = persistence ? 0 : 1;
    strncpy(reinterpret_cast<char *>(data.username), username.c_str(), sizeof(data.username));
    auto [frameData, frameSize] = generateFrame(EnrollExtended, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    setCurrentMessageIdStatus(EnrollExtended);
    LOG_CAT(info, GUI) << "发送获取注册照片指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast<int>(timeout) << "s";
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::deleteUser(uint16_t userid) {
    uint16_t n = htons(userid);
    auto [frameData, frameSize] = generateFrame(DeleteUser, reinterpret_cast<const uint8_t *>(&n), sizeof(n));
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    setCurrentMessageIdStatus(DeleteUser);
    LOG_CAT(info, GUI) << "发送删除用户指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << "删除用户ID: " << userid;
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::deleteAll() {
    auto [frameData, frameSize] = generateFrame(DeleteAll);
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    setCurrentMessageIdStatus(DeleteAll);
    LOG_CAT(info, GUI) << "发送删除所有指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::requestEnrolledImage(uint32_t offset, uint32_t size) {
    ImageSliceRequest request;
    request.offset = htonl(offset);
    request.size = htonl(size);
    auto [data, frameSize] = generateFrame(GetImage, reinterpret_cast<const uint8_t *>(&request), sizeof(request));
    m_serialPort->write(reinterpret_cast<const char *>(data), frameSize);
    // 打印太耗时
    // LOG_CAT(info, GUI) << "发送获取图片指令: " << protocolDataFormatString(data, frameSize);
    // LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::uploadImageInfo(const UploadImageInformation &info) {
    UploadImageInformation request;
    request.operation = info.operation;
    request.width = htons(info.width);
    request.height = htons(info.height);
    request.size = htonl(info.size);
    memcpy(request.username, info.username, sizeof(request.username));
    memcpy(request.md5, info.md5, sizeof(request.md5));
    auto [data, frameSize] =
        generateFrame(UploadImageInfo, reinterpret_cast<const uint8_t *>(&request), sizeof(request));
    m_serialPort->write(reinterpret_cast<const char *>(data), frameSize);
}

void ModuleCommunication::uploadImageData(uint32_t offset, const uint8_t *data, uint32_t size) {
    uint32_t dataSize = sizeof(UploadImageDataSlice) + size;
    auto buffer = new uint8_t[dataSize];
    auto request = reinterpret_cast<UploadImageDataSlice *>(buffer);
    request->size = htonl(size);
    request->offset = htonl(offset);
    memcpy(request->data, data, size);
    auto [frameData, frameSize] = generateFrame(UploadImageData, buffer, dataSize);
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    delete[] buffer;
}

void ModuleCommunication::requestCurrentStatus() {
    auto [frameData, frameSize] = generateFrame(GetCurrentStatus);
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    LOG_CAT(info, GUI) << "发送状态指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::setDebugEnabled(bool enabled) {
    uint8_t data = enabled ? 0x01 : 0x00;
    auto [frameData, frameSize] = generateFrame(EnableDebug, &data, sizeof(data));
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
}

void ModuleCommunication::startOta() {
    auto [frameData, frameSize] = generateFrame(StartOta);
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
}

void ModuleCommunication::requestUniqueId() {
    auto [frameData, frameSize] = generateFrame(GetUniqueID);
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    LOG_CAT(info, GUI) << "发送获取ID指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << Separator;
}

void ModuleCommunication::requestVersion() {
    auto [frameData, frameSize] = generateFrame(GetVersion);
    m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
    LOG_CAT(info, GUI) << "发送获取版本指令: " << protocolDataFormatString(frameData, frameSize);
    LOG_CAT(info, GUI) << Separator;
}

ModuleCommunication::MessageId ModuleCommunication::currentMessageId() const {
    return m_currentMessageId;
}

void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
    uint8_t messageId = data[2];
    switch (messageId) {
    case Reply: {
        uint8_t replyId = data[5];
        auto result = data[6];
        switch (replyId) {
        case Reset: {
            LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
            if (result == Success) {
                LOG_CAT(info, GUI) << "复位完成。";
            }
            LOG_CAT(info, GUI) << Separator;
            break;
        }
        case Verify:
        case VerifyExtended: {
            LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
            if (result == Success) {
                auto info = reinterpret_cast<const VerifyReply *>(data + 7);
                uint16_t userid = ntohs(info->userid);
                uint16_t elapsed = ntohs(info->elapsed);
                LOG_CAT(info, GUI) << "用户ID: " << userid
                                   << ", 用户名: " << std::string_view(reinterpret_cast<const char *>(info->username))
                                   << ", 耗时: " << elapsed << "ms";
                emit newVerifyResult(userid, reinterpret_cast<const char *>(info->username), elapsed);
            } else if (result == Failed4Timeout) {
                LOG_CAT(info, GUI) << "识别超时。";
            } else if (result == Rejected) {
                LOG_CAT(info, GUI) << "模组拒绝该命令。";
            } else if (result == Failed4UnknownUser) {
                auto info = reinterpret_cast<const VerifyReply *>(data + 7);
                uint16_t elapsed = ntohs(info->elapsed);
                emit newVerifyResult(InvalidUserId, "", elapsed);
                LOG_CAT(info, GUI) << "未录入用户, 耗时: " << elapsed << "ms";
            } else if (result == Failed4UnknownReason) {
                auto info = reinterpret_cast<const VerifyReply *>(data + 7);
                uint16_t elapsed = ntohs(info->elapsed);
                emit newVerifyResult(InvalidUserId, "", elapsed);
                LOG_CAT(info, GUI) << "未知错误, 耗时: " << elapsed << "ms";
            } else {
                LOG_CAT(info, GUI) << "未知错误(" << static_cast<int>(result) << ")。";
            }
            if (replyId == VerifyExtended) {
                auto info = reinterpret_cast<const VerifyExtendReply *>(data + 7);
                uint16_t width = ntohs(info->image_width);
                uint16_t height = ntohs(info->image_height);
                if ((width > 0) && (height > 0)) {
                    emit newImageInfo(static_cast<MessageId>(replyId), width * height, info->md5);
                }
            }
            LOG_CAT(info, GUI) << Separator;
            break;
        }
        case EnrollSingle:
        case EnrollExtended: {
            LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
            if (result == Success) {
                uint16_t userId = InvalidUserId;
                if (replyId == EnrollExtended) {
                    auto info = reinterpret_cast<const EnrollExtendedReply *>(data + 7);
                    uint16_t width = ntohs(info->imageWidth);
                    uint16_t height = ntohs(info->imageHeight);
                    userId = ntohs(info->userid);
                    uint16_t plamX = ntohs(info->palmVeinInformation.x1);
                    uint16_t plamY = ntohs(info->palmVeinInformation.y1);
                    LOG_CAT(info, GUI) << "注册成功,用户ID: " << userId << ", 图片大小: " << width << "x" << height
                                       << ", 已有ID: " << ntohs(info->enrolledId)
                                       << ", 姓名: " << (const char *)info->enrolledUsername << ". palm vein: ("
                                       << plamX << "," << plamY << " " << (ntohs(info->palmVeinInformation.x2) - plamX)
                                       << "x" << (ntohs(info->palmVeinInformation.y2) - plamY)
                                       << "), detection probability: "
                                       << ntohs(info->palmVeinInformation.detectionProbability) / 1000.f
                                       << ", quality: " << ntohs(info->palmVeinInformation.quality) / 1000.f;

                    emit newImageInfo(static_cast<MessageId>(replyId), width * height, info->md5);
                } else {
                    auto info = reinterpret_cast<const EnrollReply *>(data + 7);
                    userId = ntohs(info->userid);
                    LOG_CAT(info, GUI) << "注册成功,用户ID: " << userId;
                }
                emit newEnrollResult(userId);
            } else if (result == Failed4Timeout) {
                LOG_CAT(info, GUI) << "录入超时。";
                emit errorOccurred(NoteId::InteractWarning, "录入", "录入超时");
            } else if (result == Failed4PalmEnrolled) {
                emit errorOccurred(NoteId::InteractWarning, "手掌已被录入");
                LOG_CAT(info, GUI) << "手掌已被录入。";
            } else if (result == Failed4MaxUser) {
                emit errorOccurred(NoteId::InteractWarning, "录入", "注册已达上限");
            } else {
                LOG_CAT(info, GUI) << "未知错误(" << static_cast<int>(result) << ")。";
            }
            LOG_CAT(info, GUI) << Separator;
            break;
        }
        case GetImage: {
            if (result == Success) {
                auto info = reinterpret_cast<const ImageSliceReply *>(data + 7);
                uint32_t sliceSize = ntohl(info->size);
                emit newImageSliceData(std::vector<uint8_t>(info->data, info->data + sliceSize));
            } else {
                LOG(info) << "GetImage failed, status: " << static_cast<int>(result);
            }
            break;
        }
        case DeleteUser: {
            if (result == Success) {
                LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
                LOG_CAT(info, GUI) << "删除用户成功。";
                LOG_CAT(info, GUI) << Separator;
            } else {
                LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
                LOG_CAT(info, GUI) << "删除用户失败。";
                LOG_CAT(info, GUI) << Separator;
            }
            break;
        }
        case DeleteAll: {
            if (result == Success) {
                LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
                LOG_CAT(info, GUI) << "删除所有用户成功。";
                LOG_CAT(info, GUI) << Separator;
            }
            break;
        }
        case UploadImageInfo: {
            break;
        }
        case UploadImageData: {
            auto info = reinterpret_cast<const UploadImageReply *>(data + 7);
            if (result == Success) {
                if (info->operation == 0) {
                    LOG_CAT(info, GUI) << "图片下发注册成功,用户ID: " << ntohs(info->userid);
                } else if (info->operation == 1) {
                    auto result = reinterpret_cast<const UploadImageVerifyReply *>(info);
                    LOG_CAT(info, GUI) << "图片下发识别成功,用户ID: " << ntohs(result->userid) << ", 用户名: "
                                       << std::string_view(reinterpret_cast<const char *>(result->result.username))
                                       << ", 耗时: " << ntohs(result->result.elapsed) << "ms";
                } else {
                    LOG(warning) << "unknown upload image operation: " << info->operation;
                }
                LOG_CAT(info, GUI) << Separator;
            } else if (result == Needmore) {
            } else {
                LOG(info) << "upload image failed, operation: " << static_cast<uint16_t>(info->operation)
                          << ", status: " << static_cast<uint16_t>(result);
            }
            break;
        }
        case GetCurrentStatus: {
            LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
            LOG_CAT(info, GUI) << "模组当前状态: " << static_cast<int>(data[7]);
            LOG_CAT(info, GUI) << Separator;
            break;
        }
        case GetUniqueID: {
            auto id = reinterpret_cast<const ModuleId *>(data + 7);
            int idCount = 0;
            std::ostringstream oss;
            oss << "[";
            for (int byteIndex = 0; byteIndex < sizeof(id->userids); byteIndex++) {
                for (uint8_t bitPosition = 0; bitPosition < 8; bitPosition++) {
                    if (id->userids[byteIndex] & (1 << bitPosition)) {
                        uint16_t uid = byteIndex * 8 + bitPosition;
                        if (uid >= 1) {
                            oss << uid << ", ";
                            idCount++;
                        }
                    }
                }
            }
            oss << "]";
            LOG_CAT(info, GUI) << "模组ID: " << std::string_view(id->id, sizeof(id->id)) << ", user ids[" << idCount
                               << "]: " << oss.str();
            LOG_CAT(info, GUI) << Separator;
            break;
        }
        case GetVersion: {
            LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
            auto version = reinterpret_cast<const ModuleVersion *>(data + 7);
            int length = std::strlen(version->version);
            if (length > sizeof(version->version)) {
                length = sizeof(version->version);
            }
            int ota = ntohl(version->otaVersion);
            LOG_CAT(info, GUI) << "模组烧录版本: " << std::string_view(version->version, length)
                               << ", OTA版本: " << ntohl(version->otaVersion);
            LOG_CAT(info, GUI) << Separator;
            m_verison = QString::fromLocal8Bit(version->version, length);
            m_otaVerison = ota;
            emit verisonChanged();
            break;
        }
        case EnableDebug: {
            LOG(info) << "set moudle debug mode: " << (result == Success);
            break;
        }
        default:
            LOG(warning) << "unknown reply command: 0x" << (static_cast<int>(replyId) & 0xff)
                         << ", data: " << protocolDataFormatString(data, size);
            break;
        }
        m_currentMessageId = Idle;
        emit currentMessageIdChanged();
        emit commandFinished(static_cast<MessageId>(replyId), static_cast<MessageStatus>(result));
        break;
    }
    case Note: {
        auto noteId = static_cast<NoteId>(data[5]);
        switch (noteId) {
        case NoteId::Ready: {
            LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
            LOG_CAT(info, GUI) << "模组上电初始化成功。";
            LOG_CAT(info, GUI) << Separator;
            break;
        }
        case NoteId::PalmState: { // 模组返回的数据为当前帧的手掌状态
            auto state = reinterpret_cast<const PalmStateNote *>(data + 6);
            PalmState palmState = static_cast<PalmState>(ntohs(state->state));
            if (palmState == NeedMoveToCenter) {
                emit errorOccurred(NoteId::InteractWarning, "录入提示", "请将手掌置于画面中心");
            } else if (palmState == TooFar) {
                emit errorOccurred(NoteId::InteractWarning, "录入提示", "请将手掌靠近一点");
            } else if (palmState == ManyPalm) {
                emit errorOccurred(NoteId::InteractWarning, "录入提示", "检测到多个手掌");
            } else if (palmState == NoAlive) {
                emit errorOccurred(NoteId::InteractWarning, "录入提示", "活体检测未通过");
            }
            LOG(info) << "palm state: " << palmState;
            break;
        }
        case NoteId::UnknownError: {
            LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
            LOG_CAT(info, GUI) << "未知错误。";
            LOG_CAT(info, GUI) << Separator;
            break;
        }
        case NoteId::DebugInfo: {
            auto message = reinterpret_cast<const char *>(data + 6);
            LOG_CAT(info, GUI) << "模组日志: " << message;
            break;
        }
        case NoteId::NoAliveImage: {
            LOG(info) << "no alive image";
            emit newImageInfo(Note, 600 * 800, nullptr);
            break;
        }
        default:
            LOG(warning) << "unknown note command: 0x" << (static_cast<int>(noteId) & 0xff)
                         << ", data: " << protocolDataFormatString(data, size);
            break;
        }

        break;
    }
    default:
        LOG(warning) << "unknown command: 0x" << (static_cast<int>(data[2]) & 0xff)
                     << ", data: " << protocolDataFormatString(data, size);
        break;
    }
}

void ModuleCommunication::onReadyRead() {
    // LOG(info) << "ModuleCommunication::onReadyRead";
    m_receivedBuffer.append(m_serialPort->readAll());
    while (m_receivedBuffer.size() >= 2) {
        int beginIndex = -1;
        for (int i = 0; i < m_receivedBuffer.size() - 1; i++) {
            if (static_cast<uint8_t>(m_receivedBuffer[i]) == 0xEF &&
                static_cast<uint8_t>(m_receivedBuffer[i + 1]) == 0xAA) {
                beginIndex = i;
                break;
            }
        }
        if (beginIndex < 0) {
            m_receivedBuffer.clear();
            break;
        } else if (beginIndex != 0) {
            m_receivedBuffer.remove(0, beginIndex);
            beginIndex = 0;
        }

        if (m_receivedBuffer.size() < 5) break;
        uint16_t packageSize = *reinterpret_cast<uint16_t *>(m_receivedBuffer.data() + 3);
        packageSize = ntohs(packageSize);

        uint16_t totalSize = 0;
        if (packageSize == 0) {
            totalSize = 6;
        } else {
            totalSize = packageSize + 6;
        }
        if (m_receivedBuffer.size() >= totalSize) {
            processPackage(reinterpret_cast<const uint8_t *>(m_receivedBuffer.data()), totalSize);
            m_receivedBuffer.remove(0, totalSize);
        } else {
            break;
        }
    }
}

void ModuleCommunication::onErrorOccurred(QSerialPort::SerialPortError error) {
    if (error == QSerialPort::NoError) return;
    LOG_CAT(info, GUI) << m_serialPort->portName().toStdString() << ": " << m_serialPort->errorString().toStdString();
    emit errorOccurred(NoteId::DeviceError, m_serialPort->errorString());
}

std::pair<uint8_t *, uint32_t> ModuleCommunication::generateFrame(MessageId command, const uint8_t *data,
                                                                  uint16_t size) {
    static uint8_t sendBuffer[4096] = {0};
    memset(sendBuffer, 0, sizeof(sendBuffer));
    sendBuffer[0] = 0xef;
    sendBuffer[1] = 0xaa;
    sendBuffer[2] = command;
    sendBuffer[3] = size >> 8;
    sendBuffer[4] = size & 0xff;
    if (size > 0) {
        memcpy(sendBuffer + 5, data, size);
    }
    sendBuffer[5 + size] = xor_checksum_byte(sendBuffer + 2, 3 + size);
    return std::make_pair(sendBuffer, size + 6);
}

void ModuleCommunication::setCurrentMessageIdStatus(MessageId messageId) {
    if (m_currentMessageId != messageId) {
        m_currentMessageId = messageId;
        emit commandStarted(messageId);
        emit currentMessageIdChanged();
    }
}

std::string ModuleCommunication::protocolDataFormatString(const uint8_t *data, int size) {
    std::ostringstream oss;
    for (int i = 0; i < size; i++) {
        oss << "0x" << std::setfill('0') << std::setw(2) << std::hex << (static_cast<int>(data[i]) & 0xff) << " ";
    }
    return oss.str();
}

BOOST_DESCRIBE_ENUM(ModuleCommunication::PalmState, NoPalm, TooFar, NeedMoveToCenter, ManyPalm, NoAlive)
namespace std {
std::ostream &operator<<(std::ostream &stream, const ModuleCommunication::PalmState &element) {
    char const *r = "(unnamed)";
    boost::mp11::mp_for_each<boost::describe::describe_enumerators<ModuleCommunication::PalmState>>([&](auto D) {
        if (element == D.value) r = D.name;
    });
    stream << r << "(" << static_cast<int>(element) << ")";
    return stream;
}
} // namespace std
