// SPDX-License-Identifier: GPL-2.0-only
/*
 * Digital Voice Modem - Common Library
 * GPLv2 Open Source. Use is subject to license terms.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *  Copyright (C) 2009,2014,2015 Jonathan Naylor, G4KLX
 *  Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL
 *
 */
/**
 * @defgroup utils Common Utility Routines
 * @brief Defines and implements utility routines.
 * @ingroup common
 * 
 * @file Utils.h
 * @ingroup utils
 * @file Utils.cpp
 * @ingroup utils
 */
#if !defined(__UTILS_H__)
#define __UTILS_H__

#include "common/Defines.h"

#include <cstring>
#include <string>

#if !defined(_WIN32)
#include <arpa/inet.h>
#endif // !defined(WIN32)

// ---------------------------------------------------------------------------
//  Macros
// ---------------------------------------------------------------------------

/**
 * @brief Pointer magic to get the memory address of a floating point number.
 * @ingroup utils
 * @param x Floating Point Variable
 */
#define __FLOAT_ADDR(x)  (*(uint32_t*)& x)
/**
 * @brief Pointer magic to get the memory address of a double precision number.
 * @ingroup utils
 * @param x Double Precision Variable
 */
#define __DOUBLE_ADDR(x) (*(uint64_t*)& x)

// ---------------------------------------------------------------------------
//  Inline Global Functions
// ---------------------------------------------------------------------------

/**
 * @brief String from boolean.
 * @ingroup utils
 * @param value Boolean value to convert.
 * @return std::string String representation of the boolean value.
 */
inline std::string __BOOL_STR(const bool& value) {
    std::stringstream ss;
    ss << std::boolalpha << value;
    return ss.str();
}

/**
 * @brief String from integer number.
 * @ingroup utils
 * @param value Integer value to convert.
 * @return std::string String representation of the integer value.
 */
inline std::string __INT_STR(const int& value) {
    std::stringstream ss;
    ss << value;
    return ss.str();
}

/**
 * @brief String from hex integer number.
 * @ingroup utils
 * @param value Integer value to convert.
 * @return std::string String representation of the integer value in hexadecmial.
 */
inline std::string __INT_HEX_STR(const int& value) {
    std::stringstream ss;
    ss << std::hex << value;
    return ss.str();
}

/**
 * @brief String from floating point number.
 * @ingroup utils
 * @param value Floating point value to convert.
 * @return std::string String representation of the floating point value.
 */
inline std::string __FLOAT_STR(const float& value) {
    std::stringstream ss;
    ss << value;
    return ss.str();
}

/**
 * @brief IP address from uint32_t value.
 * @ingroup utils
 * @param value Packed IP address.
 * @return std::string String representation of the packed IP address.
 */
inline std::string __IP_FROM_UINT(const uint32_t& value) {
    std::stringstream ss;
    ss << ((value >> 24) & 0xFFU) << "." << ((value >> 16) & 0xFFU) << "." << ((value >> 8) & 0xFFU) << "." << (value & 0xFFU);
    return ss.str();
}

/**
 * @brief IP address from uint32_t value.
 * @ingroup utils
 * @param value String representation of the IP address.
 * @return uint32_t Packed IP address.
 */
#if !defined(_WIN32)
inline uint32_t __IP_FROM_STR(const std::string& value) {
    struct sockaddr_in sa;
    inet_pton(AF_INET, value.c_str(), &(sa.sin_addr));

    uint8_t ip[4U];
    ::memset(ip, 0x00U, 4U);

    ip[3U] = ((uint32_t)sa.sin_addr.s_addr >> 24) & 0xFFU;
    ip[2U] = ((uint32_t)sa.sin_addr.s_addr >> 16) & 0xFFU;
    ip[1U] = ((uint32_t)sa.sin_addr.s_addr >> 8) & 0xFFU;
    ip[0U] = ((uint32_t)sa.sin_addr.s_addr >> 0) & 0xFFU;

    return (ip[0U] << 24) | (ip[1U] << 16) | (ip[2U] << 8)  | (ip[3U] << 0);
}
#else
extern HOST_SW_API uint32_t __IP_FROM_STR(const std::string& value);
#endif // !defined(_WIN32)

/**
 * @brief Helper to lower-case an input string.
 * @ingroup utils
 * @param value String to lower-case.
 * @return std::string Lowercased string.
 */
inline std::string strtolower(const std::string value) {
    std::string v = value;
    std::transform(v.begin(), v.end(), v.begin(), ::tolower);
    return v;
}

/**
 * @brief Helper to upper-case an input string.
 * @ingroup utils
 * @param value String to upper-case.
 * @return std::string Uppercased string.
 */
inline std::string strtoupper(const std::string value) {
    std::string v = value;
    std::transform(v.begin(), v.end(), v.begin(), ::toupper);
    return v;
}

// ---------------------------------------------------------------------------
//  Class Declaration
// ---------------------------------------------------------------------------

/**
 * @brief Various helper utilities.
 * @ingroup utils
 */
class HOST_SW_API Utils {
public:
    /**
     * @brief Helper to dump the input buffer and display the hexadecimal output in the log.
     * @param title Name of buffer.
     * @param data Buffer to dump.
     * @param length Length of buffer.
     */
    static void dump(const std::string& title, const uint8_t* data, uint32_t length);
    /**
     * @brief Helper to dump the input buffer and display the hexadecimal output in the log.
     * @param level Log level.
     * @param title Name of buffer.
     * @param data Buffer to dump.
     * @param length Length of buffer.
     */
    static void dump(int level, const std::string& title, const uint8_t* data, uint32_t length);

    /**
     * @brief Helper to dump the input boolean bit buffer and display the hexadecimal output in the log.
     * @param title Name of buffer.
     * @param data Buffer to dump.
     * @param length Length of buffer.
     */
    static void dump(const std::string& title, const bool* bits, uint32_t length);
    /**
     * @brief Helper to dump the input boolean bit buffer and display the hexadecimal output in the log.
     * @param level Log level.
     * @param title Name of buffer.
     * @param data Buffer to dump.
     * @param length Length of buffer.
     */
    static void dump(int level, const std::string& title, const bool* bits, uint32_t length);

    /**
     * @brief Helper to dump the input buffer and display the output as a symbolic microslot output.
     * @param title Name of buffer.
     * @param data Buffer to dump.
     * @param length Length of buffer.
     */
    static void symbols(const std::string& title, const uint8_t* data, uint32_t length);

    /**
     * @brief Helper to convert the input byte to a boolean array of bits in big-endian.
     * @param byte Input byte.
     * @param bits Output bits array.
     */
    static void byteToBitsBE(uint8_t byte, bool* bits);
    /**
     * @brief Helper to convert the input byte to a boolean array of bits in little-endian.
     * @param byte Input byte.
     * @param bits Output bits array.
     */
    static void byteToBitsLE(uint8_t byte, bool* bits);

    /**
     * @brief Helper to convert the input boolean array of bits to a byte in big-endian.
     * @param byte Input bits array.
     * @param bits Output byte.
     */
    static void bitsToByteBE(const bool* bits, uint8_t& byte);
    /**
     * @brief Helper to convert the input boolean array of bits to a byte in little-endian.
     * @param byte Input bits array.
     * @param bits Output byte.
     */
    static void bitsToByteLE(const bool* bits, uint8_t& byte);

    /**
     * @brief Helper to reverse the endianness of the passed value.
     * @param value Value to reverse.
     * @returns uint16_t Endian reversed output.
     */
    static uint16_t reverseEndian(uint16_t value);
    /**
     * @brief Helper to reverse the endianness of the passed value.
     * @param value Value to reverse.
     * @returns uint32_t Endian reversed output.
     */
    static uint32_t reverseEndian(uint32_t value);
    /**
     * @brief Helper to reverse the endianness of the passed value.
     * @param value Value to reverse.
     * @returns uint64_t Endian reversed output.
     */
    static uint64_t reverseEndian(uint64_t value);

    /**
     * @brief Helper to retreive arbitrary length of bits from an input buffer.
     * @param in Input buffer.
     * @param out Output buffer.
     * @param start Starting bit offset in input buffer to read from.
     * @param stop Ending bit offset in input buffer to stop reading.
     * @returns uint32_t Count of bits read.
     */
    static uint32_t getBits(const uint8_t* in, uint8_t* out, uint32_t start, uint32_t stop);
    /**
     * @brief Helper to retreive arbitrary length of bits from an input buffer.
     * @param in Input buffer.
     * @param out Output buffer.
     * @param start Starting bit offset in input buffer to read from.
     * @param length Number of bits to read.
     * @returns uint32_t Count of bits read.
     */
    static uint32_t getBitRange(const uint8_t* in, uint8_t* out, uint32_t start, uint32_t length);
    /**
     * @brief Helper to set an arbitrary length of bits from an input buffer.
     * @param in Input buffer.
     * @param out Output buffer.
     * @param start Starting bit offset in input buffer to read from.
     * @param stop Ending bit offset in input buffer to stop reading.
     * @returns uint32_t Count of bits set.
     */
    static uint32_t setBits(const uint8_t* in, uint8_t* out, uint32_t start, uint32_t stop);
    /**
     * @brief Helper to set arbitrary length of bits from an input buffer.
     * @param in Input buffer.
     * @param out Output buffer.
     * @param start Starting bit offset in input buffer to read from.
     * @param length Number of bits to set.
     * @returns uint32_t Count of bits set.
     */
    static uint32_t setBitRange(const uint8_t* in, uint8_t* out, uint32_t start, uint32_t length);

    /**
     * @brief Helper to convert a binary input buffer into representative 6-bit byte.
     * @param input Input buffer.
     * @param offset Buffer offset.
     * @returns uint8_t Representative 6-bit output.
     */
    static uint8_t bin2Hex(const uint8_t* input, uint32_t offset);
    /**
     * @brief Helper to convert 6-bit input byte into representative binary buffer.
     * @param input Input 6-bit byte.
     * @param output Output buffer.
     * @param offset Buffer offset.
     */
    static void hex2Bin(const uint8_t input, uint8_t* output, uint32_t offset);

    /**
     * @brief Returns the count of bits in the passed 8 byte value.
     * @param bits uint8_t to count bits for.
     * @returns uint8_t Count of bits in passed value.
     */
    static uint8_t countBits8(uint8_t bits);
    /**
     * @brief Returns the count of bits in the passed 32 byte value.
     * @param bits uint32_t to count bits for.
     * @returns uint8_t Count of bits in passed value.
     */
    static uint8_t countBits32(uint32_t bits);
    /**
     * @brief Returns the count of bits in the passed 64 byte value.
     * @param bits ulong64_t to count bits for.
     * @returns uint8_t Count of bits in passed value.
     */
    static uint8_t countBits64(ulong64_t bits);
};

#endif // __UTILS_H__
