#include <stdint.h>
#include <string.h>

#include "core_runtime_private.h"

#define CODUO_BYTE_SWAP_32(value) \
    ((((value) & 0x000000ffU) << 24) | (((value) & 0x0000ff00U) << 8) | \
     (((value) & 0x00ff0000U) >> 8) | (((value) & 0xff000000U) >> 24))

static int32_t (*com_bigShort)(int16_t value);
static int32_t (*com_littleShort)(int16_t value);
static int32_t (*com_bigLong)(int32_t value);
static int32_t (*com_littleLong)(int32_t value);
static coduo_uint64_pair_t (*com_bigLong64)(coduo_uint64_pair_t value);
static coduo_uint64_pair_t (*com_littleLong64)(coduo_uint64_pair_t value);
static float (*com_bigFloat)(float value);
static float (*com_littleFloat)(float value);

int32_t BigShort(int16_t value)
{
    return (int32_t)(int16_t)com_bigShort(value);
}

int32_t BigLong(int32_t value)
{
    return com_bigLong(value);
}

coduo_uint64_pair_t BigLong64(coduo_uint64_pair_t value)
{
    return com_bigLong64(value);
}

coduo_uint64_pair_t LittleLong64(coduo_uint64_pair_t value)
{
    return com_littleLong64(value);
}

float BigFloat(float value)
{
    return com_bigFloat(value);
}

int32_t ShortSwap(int16_t value)
{
    uint16_t bits = (uint16_t)value;
    uint16_t swapped = (uint16_t)((bits << 8) | (bits >> 8));

    return (int32_t)(int16_t)swapped;
}

int32_t ShortNoSwap(int16_t value)
{
    return value;
}

int32_t LongSwap(int32_t value)
{
    return (int32_t)CODUO_BYTE_SWAP_32((uint32_t)value);
}

int32_t LongNoSwap(int32_t value)
{
    return value;
}

coduo_uint64_pair_t Long64Swap(coduo_uint64_pair_t value)
{
    coduo_uint64_pair_t swapped;

    swapped.low = CODUO_BYTE_SWAP_32(value.high);
    swapped.high = CODUO_BYTE_SWAP_32(value.low);
    return swapped;
}

coduo_uint64_pair_t Long64NoSwap(coduo_uint64_pair_t value)
{
    return value;
}

float FloatSwap(float value)
{
    uint32_t bits;
    float swapped;

    memcpy(&bits, &value, sizeof(bits));
    bits = CODUO_BYTE_SWAP_32(bits);
    memcpy(&swapped, &bits, sizeof(swapped));

    return swapped;
}

float FloatNoSwap(float value)
{
    return value;
}

void Swap_Init(void)
{
    uint16_t byteOrderProbe = UINT16_C(1);

    if (((const uint8_t *)&byteOrderProbe)[0] == UINT8_C(1)) {
        com_bigShort = ShortSwap;
        com_littleShort = ShortNoSwap;
        com_bigLong = LongSwap;
        com_littleLong = LongNoSwap;
        com_bigLong64 = Long64Swap;
        com_littleLong64 = Long64NoSwap;
        com_bigFloat = FloatSwap;
        com_littleFloat = FloatNoSwap;
    } else {
        com_bigShort = ShortNoSwap;
        com_littleShort = ShortSwap;
        com_bigLong = LongNoSwap;
        com_littleLong = LongSwap;
        com_bigLong64 = Long64NoSwap;
        com_littleLong64 = Long64Swap;
        com_bigFloat = FloatNoSwap;
        com_littleFloat = FloatSwap;
    }
}
