/*
 * @Author: mypx mypx_coder@163.com
 * @Date: 2023-04-17 14:52:43
 * @LastEditors: mypx mypx_coder@163.com
 * @LastEditTime: 2025-09-19 10:29:57
 * @FilePath: et_byte_conversion.c
 * @Description:
 *
 * Copyright (c) 2023 by wzh, All Rights Reserved.
 */
#include "etk_byte_conv.h"

/**
 * @brief Check the byte order of the system.
 * @return true if the system is little endian, false if big endian.
 */
bool et_is_little_endian(void)
{
#if (ET_USING_AUTO_LITTLE_ENDIAN == 1)
    uint16_t test = 0x0001;
    return *((uint8_t *)&test) == 1 ? true : false;
#else
    return ET_USING_LITTLE_ENDIAN;
#endif
}

/**
 * @brief Convert a 16-bit unsigned integer to an array of bytes.
 * @param n The 16-bit unsigned integer to convert.
 * @param arr Pointer to the byte array to store the result.
 * @return The number of bytes written to the array.
 */
int et_uint16_to_array(uint16_t n, uint8_t *arr)
{
    if (!et_is_little_endian())
    {
        arr[0] = (n >> 8) & 0xFF;
        arr[1] = n & 0xFF;
    }
    else
    {
        arr[1] = (n >> 8) & 0xFF;
        arr[0] = n & 0xFF;
    }

    return 2;
}

/**
 * @brief Convert an array of bytes to a 16-bit unsigned integer.
 * @param arr Pointer to the byte array.
 * @return The converted 16-bit unsigned integer.
 */
uint16_t et_array_to_uint16(const uint8_t *arr)
{
    if (!et_is_little_endian())
    {
        return (arr[0] << 8) | arr[1];
    }
    else
    {
        return (arr[1] << 8) | arr[0];
    }
}

/**
 * @brief Convert a 16-bit signed integer to an array of bytes.
 * @param n The 16-bit signed integer to convert.
 * @param arr Pointer to the byte array to store the result.
 * @return The number of bytes written to the array.
 */
int et_int16_to_array(int16_t n, uint8_t *arr)
{
    if (!et_is_little_endian())
    {
        arr[0] = (n >> 8) & 0xFF;
        arr[1] = n & 0xFF;
    }
    else
    {
        arr[1] = (n >> 8) & 0xFF;
        arr[0] = n & 0xFF;
    }

    return 2;
}

/**
 * @brief Convert an array of bytes to a 16-bit signed integer.
 * @param arr Pointer to the byte array.
 * @return The converted 16-bit signed integer.
 */
int16_t et_array_to_int16(const uint8_t *arr)
{
    int16_t value;
    if (!et_is_little_endian())
    {
        value = (arr[0] << 8) | arr[1];
    }
    else
    {
        value = (arr[1] << 8) | arr[0];
    }
    return value;
}

/**
 * @brief Convert an array of bytes to a 32-bit unsigned integer.
 * @param arr Pointer to the byte array.
 * @return The converted 32-bit unsigned integer.
 */
uint32_t et_array_to_uint32(const uint8_t *arr)
{
    if (!et_is_little_endian())
    {
        return (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
    }
    else
    {
        return (arr[3] << 24) | (arr[2] << 16) | (arr[1] << 8) | arr[0];
    }
}

/**
 * @brief Convert a 32-bit unsigned integer to an array of bytes.
 * @param n The 32-bit unsigned integer to convert.
 * @param arr Pointer to the byte array to store the result.
 * @return The number of bytes written to the array.
 */
int et_uint32_to_array(uint32_t n, uint8_t *arr)
{
    if (!et_is_little_endian())
    {
        arr[0] = (n >> 24) & 0xFF;
        arr[1] = (n >> 16) & 0xFF;
        arr[2] = (n >> 8) & 0xFF;
        arr[3] = n & 0xFF;
    }
    else
    {
        arr[3] = (n >> 24) & 0xFF;
        arr[2] = (n >> 16) & 0xFF;
        arr[1] = (n >> 8) & 0xFF;
        arr[0] = n & 0xFF;
    }

    return 4;
}

/**
 * @brief Convert an array of bytes to a 32-bit signed integer.
 * @param arr Pointer to the byte array.
 * @return The converted 32-bit signed integer.
 */
int32_t et_array_to_int32(const uint8_t *arr)
{
    int32_t value;
    if (!et_is_little_endian())
    {
        value = (arr[0] << 24) | (arr[1] << 16) | (arr[2] << 8) | arr[3];
    }
    else
    {
        value = (arr[3] << 24) | (arr[2] << 16) | (arr[1] << 8) | arr[0];
    }
    return value;
}

/**
 * @brief Convert a 32-bit signed integer to an array of bytes.
 * @param n The 32-bit signed integer to convert.
 * @param arr Pointer to the byte array to store the result.
 * @return The number of bytes written to the array.
 */
int et_int32_to_array(int32_t n, uint8_t *arr)
{
    if (!et_is_little_endian())
    {
        arr[0] = (n >> 24) & 0xFF;
        arr[1] = (n >> 16) & 0xFF;
        arr[2] = (n >> 8) & 0xFF;
        arr[3] = n & 0xFF;
    }
    else
    {
        arr[3] = (n >> 24) & 0xFF;
        arr[2] = (n >> 16) & 0xFF;
        arr[1] = (n >> 8) & 0xFF;
        arr[0] = n & 0xFF;
    }

    return 4;
}

/**
 * @brief Convert an array of bytes to a 64-bit unsigned integer.
 * @param arr Pointer to the byte array.
 * @return The converted 64-bit unsigned integer.
 */
uint64_t et_array_to_uint64(const uint8_t *arr)
{
    uint64_t value;
    if (!et_is_little_endian())
    {
        value = ((uint64_t)arr[0] << 56) | ((uint64_t)arr[1] << 48) | ((uint64_t)arr[2] << 40) | ((uint64_t)arr[3] << 32) | ((uint64_t)arr[4] << 24) | ((uint64_t)arr[5] << 16) | ((uint64_t)arr[6] << 8) | (uint64_t)arr[7];
    }
    else
    {
        value = ((uint64_t)arr[7] << 56) | ((uint64_t)arr[6] << 48) | ((uint64_t)arr[5] << 40) | ((uint64_t)arr[4] << 32) | ((uint64_t)arr[3] << 24) | ((uint64_t)arr[2] << 16) | ((uint64_t)arr[1] << 8) | (uint64_t)arr[0];
    }
    return value;
}

/**
 * @brief Convert a 64-bit unsigned integer to an array of bytes.
 * @param n The 64-bit unsigned integer to convert.
 * @param arr Pointer to the byte array to store the result.
 * @return The number of bytes written to the array.
 */
int et_uint64_to_array(uint64_t n, uint8_t *arr)
{
    if (!et_is_little_endian())
    {
        arr[0] = (n >> 56) & 0xFF;
        arr[1] = (n >> 48) & 0xFF;
        arr[2] = (n >> 40) & 0xFF;
        arr[3] = (n >> 32) & 0xFF;
        arr[4] = (n >> 24) & 0xFF;
        arr[5] = (n >> 16) & 0xFF;
        arr[6] = (n >> 8) & 0xFF;
        arr[7] = n & 0xFF;
    }
    else
    {
        arr[7] = (n >> 56) & 0xFF;
        arr[6] = (n >> 48) & 0xFF;
        arr[5] = (n >> 40) & 0xFF;
        arr[4] = (n >> 32) & 0xFF;
        arr[3] = (n >> 24) & 0xFF;
        arr[2] = (n >> 16) & 0xFF;
        arr[1] = (n >> 8) & 0xFF;
        arr[0] = n & 0xFF;
    }

    return 8;
}

typedef union
{
    uint32_t i;
    float    f;
} float_int_union_t;

/**
 * @brief Convert an array of bytes to a floating-point number.
 * @param array Pointer to the byte array.
 * @return The converted floating-point number.
 */
float et_array_to_float(const uint8_t *array)
{
    float_int_union_t u;

    u.i = et_array_to_uint32(array);

    return u.f;
}

/**
 * @brief Convert a floating-point number to an array of bytes.
 * @param f The floating-point number to convert.
 * @param array Pointer to the byte array to store the result.
 * @return The number of bytes written to the array.
 */
int et_float_to_array(float f, uint8_t *array)
{
    float_int_union_t u;

    u.f = f;

    return et_uint32_to_array(u.i, array);
}

typedef union
{
    uint8_t bytes[4];
    float   value;
} mb_float_converter_t;

/**
 * @brief Convert two Modbus registers to a floating-point number in ABCD byte order.
 * @param reg_high The high 16-bit register.
 * @param reg_low The low 16-bit register.
 * @return The converted floating-point number.
 */
float et_mb_to_float_abcd(uint16_t reg_high, uint16_t reg_low)
{
    mb_float_converter_t converter;

    if (et_is_little_endian())
    {
        // Little-endian platform, low address is low byte → reverse write
        converter.bytes[3] = (reg_high >> 8) & 0xFF; // A
        converter.bytes[2] = reg_high & 0xFF;        // B
        converter.bytes[1] = (reg_low >> 8) & 0xFF;  // C
        converter.bytes[0] = reg_low & 0xFF;         // D
    }
    else
    {
        // Big-endian platform, low address is high byte → sequential write
        converter.bytes[0] = (reg_high >> 8) & 0xFF; // A
        converter.bytes[1] = reg_high & 0xFF;        // B
        converter.bytes[2] = (reg_low >> 8) & 0xFF;  // C
        converter.bytes[3] = reg_low & 0xFF;         // D
    }

    return converter.value;
}

/**
 * @brief Convert two Modbus registers to a floating-point number in CDAB byte order.
 * @param reg_high The high 16-bit register.
 * @param reg_low The low 16-bit register.
 * @return The converted floating-point number.
 */
float et_mb_to_float_cdab(uint16_t reg_high, uint16_t reg_low)
{
    mb_float_converter_t converter;

    if (et_is_little_endian())
    {
        // CDAB: C D A B → reverse for little-endian
        converter.bytes[3] = (reg_high >> 8) & 0xFF; // A
        converter.bytes[2] = reg_high & 0xFF;        // B
        converter.bytes[1] = (reg_low >> 8) & 0xFF;  // C
        converter.bytes[0] = reg_low & 0xFF;         // D
    }
    else
    {
        // Big-endian: straight mapping
        converter.bytes[0] = (reg_low >> 8) & 0xFF;  // C
        converter.bytes[1] = reg_low & 0xFF;         // D
        converter.bytes[2] = (reg_high >> 8) & 0xFF; // A
        converter.bytes[3] = reg_high & 0xFF;        // B
    }

    return converter.value;
}

/**
 * @brief Convert two Modbus registers to a floating-point number in BADC byte order.
 * @param reg_high The high 16-bit register.
 * @param reg_low The low 16-bit register.
 * @return The converted floating-point number.
 */
float et_mb_to_float_badc(uint16_t reg_high, uint16_t reg_low)
{
    mb_float_converter_t converter;

    if (et_is_little_endian())
    {
        // BADC: B A D C → reverse for little-endian
        converter.bytes[3] = reg_high & 0xFF;        // B
        converter.bytes[2] = (reg_high >> 8) & 0xFF; // A
        converter.bytes[1] = reg_low & 0xFF;         // D
        converter.bytes[0] = (reg_low >> 8) & 0xFF;  // C
    }
    else
    {
        // Big-endian: straight mapping
        converter.bytes[0] = reg_high & 0xFF;        // B
        converter.bytes[1] = (reg_high >> 8) & 0xFF; // A
        converter.bytes[2] = reg_low & 0xFF;         // D
        converter.bytes[3] = (reg_low >> 8) & 0xFF;  // C
    }

    return converter.value;
}

/**
 * @brief Convert two Modbus registers to a floating-point number in DCBA byte order.
 * @param reg_high The high 16-bit register.
 * @param reg_low The low 16-bit register.
 * @return The converted floating-point number.
 */
float et_mb_to_float_dcba(uint16_t reg_high, uint16_t reg_low)
{
    mb_float_converter_t converter;

    if (et_is_little_endian())
    {
        // DCBA: D C B A → reverse for little-endian
        converter.bytes[3] = reg_low & 0xFF;         // D
        converter.bytes[2] = (reg_low >> 8) & 0xFF;  // C
        converter.bytes[1] = reg_high & 0xFF;        // B
        converter.bytes[0] = (reg_high >> 8) & 0xFF; // A
    }
    else
    {
        // Big-endian: straight mapping
        converter.bytes[0] = reg_low & 0xFF;         // D
        converter.bytes[1] = (reg_low >> 8) & 0xFF;  // C
        converter.bytes[2] = reg_high & 0xFF;        // B
        converter.bytes[3] = (reg_high >> 8) & 0xFF; // A
    }
    return converter.value;
}

/**
 * @brief Convert a floating-point number to two Modbus registers in ABCD byte order.
 * @param f The floating-point number to convert.
 * @param reg_high Pointer to store the high 16-bit register.
 * @param reg_low Pointer to store the low 16-bit register.
 * @return The number of registers written (always 2).
 */
int et_float_to_mb_abcd(float f, uint16_t *reg_high, uint16_t *reg_low)
{
    mb_float_converter_t converter;
    converter.value = f;

    if (et_is_little_endian())
    {
        // Little-endian platform: extract bytes in reverse order
        *reg_high = (converter.bytes[3] << 8) | converter.bytes[2]; // A B
        *reg_low  = (converter.bytes[1] << 8) | converter.bytes[0]; // C D
    }
    else
    {
        // Big-endian platform: extract bytes in sequential order
        *reg_high = (converter.bytes[0] << 8) | converter.bytes[1]; // A B
        *reg_low  = (converter.bytes[2] << 8) | converter.bytes[3]; // C D
    }

    return 2;
}

/**
 * @brief Convert a floating-point number to two Modbus registers in CDAB byte order.
 * @param f The floating-point number to convert.
 * @param reg_high Pointer to store the high 16-bit register.
 * @param reg_low Pointer to store the low 16-bit register.
 * @return The number of registers written (always 2).
 */
int et_float_to_mb_cdab(float f, uint16_t *reg_high, uint16_t *reg_low)
{
    mb_float_converter_t converter;
    converter.value = f;

    if (et_is_little_endian())
    {
        // CDAB: extract as C D A B
        *reg_high = (converter.bytes[2] << 8) | converter.bytes[3]; // A B
        *reg_low  = (converter.bytes[0] << 8) | converter.bytes[1]; // C D
    }
    else
    {
        // Big-endian: straight mapping
        *reg_high = (converter.bytes[2] << 8) | converter.bytes[3]; // A B
        *reg_low  = (converter.bytes[0] << 8) | converter.bytes[1]; // C D
    }

    return 2;
}

/**
 * @brief Convert a floating-point number to two Modbus registers in BADC byte order.
 * @param f The floating-point number to convert.
 * @param reg_high Pointer to store the high 16-bit register.
 * @param reg_low Pointer to store the low 16-bit register.
 * @return The number of registers written (always 2).
 */
int et_float_to_mb_badc(float f, uint16_t *reg_high, uint16_t *reg_low)
{
    mb_float_converter_t converter;
    converter.value = f;

    if (et_is_little_endian())
    {
        // BADC: extract as B A D C
        *reg_high = (converter.bytes[2] << 8) | converter.bytes[3]; // B A
        *reg_low  = (converter.bytes[0] << 8) | converter.bytes[1]; // D C
    }
    else
    {
        // Big-endian: straight mapping
        *reg_high = (converter.bytes[1] << 8) | converter.bytes[0]; // B A
        *reg_low  = (converter.bytes[3] << 8) | converter.bytes[2]; // D C
    }

    return 2;
}

/**
 * @brief Convert a floating-point number to two Modbus registers in DCBA byte order.
 * @param f The floating-point number to convert.
 * @param reg_high Pointer to store the high 16-bit register.
 * @param reg_low Pointer to store the low 16-bit register.
 * @return The number of registers written (always 2).
 */
int et_float_to_mb_dcba(float f, uint16_t *reg_high, uint16_t *reg_low)
{
    mb_float_converter_t converter;
    converter.value = f;

    if (et_is_little_endian())
    {
        // DCBA: extract as D C B A
        *reg_high = (converter.bytes[1] << 8) | converter.bytes[0]; // B A
        *reg_low  = (converter.bytes[3] << 8) | converter.bytes[2]; // D C
    }
    else
    {
        // Big-endian: straight mapping
        *reg_high = (converter.bytes[2] << 8) | converter.bytes[3]; // B A
        *reg_low  = (converter.bytes[0] << 8) | converter.bytes[1]; // D C
    }

    return 2;
}


