/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 * Description: registry  function  implementation
 */
#include "xxhash64_hash.h"
#include "operator/hash_util.h"
#include "type/decimal128_utils.h"

using namespace std;
using namespace omniruntime::op;

namespace omniruntime::codegen::function {
static const int64_t PRIME64_5 = 0x27D4EB2F165667C5L;
static const int64_t SIZE_OF_INT = 4L;
static const int64_t SIZE_OF_LONG = 8L;

int64_t ALWAYS_INLINE HashInt(int32_t val, int64_t seed)
{
    auto hash = seed + PRIME64_5 + SIZE_OF_INT;
    hash = HashUtil::XxHash64UpdateTail(hash, val);
    hash = static_cast<int64_t>(HashUtil::XxHash64FinalShuffle(static_cast<uint64_t>(hash)));
    return hash;
}

int64_t ALWAYS_INLINE HashLong(int64_t val, int64_t seed)
{
    auto hash = seed + PRIME64_5 + SIZE_OF_LONG;
    hash = HashUtil::XxHash64UpdateTail(hash, val);
    hash = static_cast<int64_t>(HashUtil::XxHash64FinalShuffle(static_cast<uint64_t>(hash)));
    return hash;
}

extern "C" DLLEXPORT int64_t XxH64Int8(int8_t val, bool isValNull, int64_t seed, bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    return isValNull ? seed : HashInt(static_cast<int32_t>(val), seed);
}

extern "C" DLLEXPORT int64_t XxH64Int16(int16_t val, bool isValNull, int64_t seed, bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    return isValNull ? seed : HashInt(static_cast<int32_t>(val), seed);
}

extern "C" DLLEXPORT int64_t XxH64Int32(int32_t val, bool isValNull, int64_t seed, bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    return isValNull ? seed : HashInt(val, seed);
}

extern "C" DLLEXPORT int64_t XxH64Int64(int64_t val, bool isValNull, int64_t seed, bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    return isValNull ? seed : HashLong(val, seed);
}

extern "C" DLLEXPORT int64_t XxH64String(const char *val, int32_t valLen, bool isValNull, int64_t seed, bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    if (isValNull) {
        return seed;
    }

    auto data = reinterpret_cast<int8_t *>(const_cast<char *>(val));
    return HashUtil::XxHash64Hash(seed, data, 0, valLen);
}

extern "C" DLLEXPORT int64_t XxH64Double(double val, bool isValNull, int64_t seed, bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    if (isValNull) {
        return seed;
    }

    return HashLong(HashUtil::DoubleToLongBits(val), seed);
}

extern "C" DLLEXPORT int64_t XxH64Decimal64(int64_t val, int32_t precision, int32_t scale, bool isValNull, int64_t seed,
    bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    return isValNull ? seed : HashLong(val, seed);
}

extern "C" DLLEXPORT int64_t XxH64Decimal128(int64_t xHigh, uint64_t xLow, int32_t precision, int32_t scale,
    bool isValNull, int64_t seed, bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    if (isValNull) {
        return seed;
    }

    int32_t byteLen = 0;
    auto bytes = omniruntime::type::Decimal128Utils::Decimal128ToBytes(xHigh, xLow, byteLen);
    auto result = op::HashUtil::XxHash64Hash(seed, bytes, 0, byteLen);
    delete[] bytes;
    bytes = nullptr;
    return result;
}

extern "C" DLLEXPORT int64_t XxH64Boolean(bool val, bool isValNull, int64_t seed, bool isSeedNull)
{
    if (isSeedNull) {
        seed = 0;
    }
    if (isValNull) {
        return seed;
    }

    int32_t intVal = val ? 1 : 0;
    return HashInt(intVal, seed);
}
}