/*
 * Copyright © Stéphane Raimbault <stephane.raimbault@gmail.com>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

#include <stdlib.h>

// clang-format off
#include <stdint.h>

#include <string.h>
#include <assert.h>

#include <arpa/inet.h>

#include "config.h"

#include "modbus.h"

// clang-format on

/* 从单个字节值中设置多个位（字节值的所有8位都会被设置） */
void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value)
{
    int i;

    for (i = 0; i < 8; i++) {
        dest[idx + i] = (value & (1 << i)) ? 1 : 0;
    }
}

/* 从字节表中设置多个位（仅设置 idx 到 idx + nb_bits 之间的位） */
void modbus_set_bits_from_bytes(uint8_t *dest,
                                int idx,
                                unsigned int nb_bits,
                                const uint8_t *tab_byte)
{
    unsigned int i;
    int shift = 0;

    for (i = idx; i < idx + nb_bits; i++) {
        dest[i] = tab_byte[(i - idx) / 8] & (1 << shift) ? 1 : 0;
        /* gcc 不喜欢: shift = (++shift) % 8; */
        shift++;
        shift %= 8;
    }
}

/* 从多个位中获取字节值。
   要获得完整的一个字节，请将 nb_bits 设置为 8。 */
uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits)
{
    unsigned int i;
    uint8_t value = 0;

    if (nb_bits > 8) {
        /* 如果设置了 NDEBUG，则断言会被忽略 */
        assert(nb_bits < 8);
        nb_bits = 8;
    }

    for (i = 0; i < nb_bits; i++) {
        value |= (src[idx + i] << i);
    }

    return value;
}

/* 从4个字节（Modbus格式）中获取浮点数，不做任何转换（ABCD字节序） */
float modbus_get_float_abcd(const uint16_t *src)
{
    float f;
    uint32_t i;
    uint8_t a, b, c, d;

    // 注意: src 包含按处理器字节序排列的16位数字，
    //       因此我们使用移位操作，不直接访问内存
    a = (src[0] >> 8) & 0xFF; // 第一个字的高字节
    b = (src[0] >> 0) & 0xFF; // 第一个字的低字节
    c = (src[1] >> 8) & 0xFF; // 第二个字的高字节
    d = (src[1] >> 0) & 0xFF; // 第二个字的低字节

    // 我们总是通过移位操作按 abcd 顺序组装32位整数
    i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
    memcpy(&f, &i, 4);

    return f;
}

/* 从4个字节（Modbus格式）中获取浮点数，使用反向格式（DCBA字节序） */
float modbus_get_float_dcba(const uint16_t *src)
{
    float f;
    uint32_t i;
    uint8_t a, b, c, d;

    // 从 src 读取时的字节顺序定义为: dcba
    d = (src[0] >> 8) & 0xFF;
    c = (src[0] >> 0) & 0xFF;
    b = (src[1] >> 8) & 0xFF;
    a = (src[1] >> 0) & 0xFF;

    // 我们总是通过移位操作按 abcd 顺序组装32位整数
    i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
    memcpy(&f, &i, 4);

    return f;
}

/* 从4个字节（Modbus格式）中获取浮点数，交换字节顺序（BADC） */
float modbus_get_float_badc(const uint16_t *src)
{
    float f;
    uint32_t i;
    uint8_t a, b, c, d;

    // 从 src 读取时的字节顺序定义为: badc
    b = (src[0] >> 8) & 0xFF;
    a = (src[0] >> 0) & 0xFF;
    d = (src[1] >> 8) & 0xFF;
    c = (src[1] >> 0) & 0xFF;

    // 我们总是通过移位操作按 abcd 顺序组装32位整数
    i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
    memcpy(&f, &i, 4);

    return f;
}

/* 从4个字节（Modbus格式）中获取浮点数，交换字顺序（CDAB） */
float modbus_get_float_cdab(const uint16_t *src)
{
    float f;
    uint32_t i;
    uint8_t a, b, c, d;

    // 从 src 读取时的字节顺序定义为: cdab
    c = (src[0] >> 8) & 0xFF;
    d = (src[0] >> 0) & 0xFF;
    a = (src[1] >> 8) & 0xFF;
    b = (src[1] >> 0) & 0xFF;

    // 我们总是通过移位操作按 abcd 顺序组装32位整数
    i = (a << 24) | (b << 16) | (c << 8) | (d << 0);
    memcpy(&f, &i, 4);

    return f;
}

/* 【已弃用】 - 从4个字节中以某种 Modbus 格式获取浮点数 */
float modbus_get_float(const uint16_t *src)
{
    return modbus_get_float_cdab(src);
}

/* 将浮点数设置为4字节 Modbus 格式，不做任何转换（ABCD字节序） */
void modbus_set_float_abcd(float f, uint16_t *dest)
{
    // 直接类型转换会因为类型双关指针别名警告而无法工作
    // uint32_t i = *(uint32_t*)(&f);
    float * fptr = &f;
    uint32_t * iptr = (uint32_t *)fptr;
    uint32_t i = *iptr;
    uint8_t a, b, c, d;

    a = (i >> 24) & 0xFF;
    b = (i >> 16) & 0xFF;
    c = (i >> 8) & 0xFF;
    d = (i >> 0) & 0xFF;

    dest[0] = (a << 8) | b;
    dest[1] = (c << 8) | d;
}

/* 将浮点数设置为4字节 Modbus 格式，进行字节和字交换转换（DCBA字节序） */
void modbus_set_float_dcba(float f, uint16_t *dest)
{
    float * fptr = &f;
    uint32_t * iptr = (uint32_t *)fptr;
    uint32_t i = *iptr;
    uint8_t a, b, c, d;

    a = (i >> 24) & 0xFF;
    b = (i >> 16) & 0xFF;
    c = (i >> 8) & 0xFF;
    d = (i >> 0) & 0xFF;

    dest[0] = (d << 8) | c;
    dest[1] = (b << 8) | a;
}

/* 将浮点数设置为4字节 Modbus 格式，仅交换字节顺序（BADC） */
void modbus_set_float_badc(float f, uint16_t *dest)
{
    float * fptr = &f;
    uint32_t * iptr = (uint32_t *)fptr;
    uint32_t i = *iptr;
    uint8_t a, b, c, d;

    a = (i >> 24) & 0xFF;
    b = (i >> 16) & 0xFF;
    c = (i >> 8) & 0xFF;
    d = (i >> 0) & 0xFF;

    dest[0] = (b << 8) | a;
    dest[1] = (d << 8) | c;
}

/* 将浮点数设置为4字节 Modbus 格式，仅交换字顺序（CDAB） */
void modbus_set_float_cdab(float f, uint16_t *dest)
{
    float * fptr = &f;
    uint32_t * iptr = (uint32_t *)fptr;
    uint32_t i = *iptr;
    uint8_t a, b, c, d;

    a = (i >> 24) & 0xFF;
    b = (i >> 16) & 0xFF;
    c = (i >> 8) & 0xFF;
    d = (i >> 0) & 0xFF;

    dest[0] = (c << 8) | d;
    dest[1] = (a << 8) | b;
}

/* 【已弃用】 - 将浮点数设置为4字节，采用某种 Modbus 格式！ */
void modbus_set_float(float f, uint16_t *dest)
{
    modbus_set_float_cdab(f, dest);
}
