/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "ecmascript/tests/test_helper.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/platform/string_hash.h"

using namespace panda::ecmascript;
using namespace common;

namespace panda::test {
class EcmaStringHashTest : public BaseTestWithScope<false> {
public:
    template <typename T>
    static uint32_t ComputeHashRaw(const T *data, size_t size, uint32_t hashSeed)
    {
        uint32_t hash = hashSeed;
        for (size_t i = 0; i < size; i++) {
            hash = hash * StringHash::HASH_MULTIPLY + data[i];
        }
        return hash;
    }

    template <typename T>
    static uint32_t ComputeHashOpt(const T *data, size_t size, uint32_t hashSeed)
    {
        return BaseString::ComputeHashForData(data, size, hashSeed);
    }

    static uint32_t ComputeConcatHash(
        const JSThread *thread,
        const JSHandle<EcmaString> &firstString,
        const JSHandle<EcmaString> &secondString)
    {
        return EcmaString::CalculateConcatHashCode(thread, firstString, secondString);
    }
};

/*
* @tc.name: ComputeHashForData
* @tc.desc: For short data,
* test new hashcode compute function result equals original algorithm: hash = hash*31 + data[i]
* @tc.type: FUNC
*/
HWTEST_F_L0(EcmaStringHashTest, ComputeHashForData_001)
{
    std::string data = "Hello World! OpenHarmony";
    uint32_t rawHash8 = ComputeHashRaw(reinterpret_cast<uint8_t*>(data.data()), data.size(), 0);
    uint32_t optHash8 = ComputeHashOpt(reinterpret_cast<uint8_t*>(data.data()), data.size(), 0);
    EXPECT_EQ(rawHash8, optHash8);

    std::u16string data16 = u"Hello World! 鸿蒙 OpenHarmony";
    uint32_t rawHash16 = ComputeHashRaw(reinterpret_cast<uint16_t*>(data16.data()), data16.size(), 0);
    uint32_t optHash16 = ComputeHashOpt(reinterpret_cast<uint16_t*>(data16.data()), data16.size(), 0);
    EXPECT_EQ(rawHash16, optHash16);
}

/*
* @tc.name: ComputeHashForData
* @tc.desc: For medium data,
* test new hashcode compute function result equals original algorithm: hash = hash*31 + data[i]
* @tc.type: FUNC
*/
HWTEST_F_L0(EcmaStringHashTest, ComputeHashForData_002)
{
    std::string data = "Hello World! abcdefghijklmnopqrstuvwxyz0123456789 !#$%&'()*+,-./:;=?@[]^_`{|}~";
    uint32_t rawHash8 = ComputeHashRaw(reinterpret_cast<uint8_t*>(data.data()), data.size(), 0);
    uint32_t optHash8 = ComputeHashOpt(reinterpret_cast<uint8_t*>(data.data()), data.size(), 0);
    EXPECT_EQ(rawHash8, optHash8);

    std::u16string data16 = u"Hello World! abcdefghijklmnopqrstuvwxyz0123456789 !#$%&'()*+,-./:;=?@[]^_`{|}~ 鸿蒙";
    uint32_t rawHash16 = ComputeHashRaw(reinterpret_cast<uint16_t*>(data16.data()), data16.size(), 0);
    uint32_t optHash16 = ComputeHashOpt(reinterpret_cast<uint16_t*>(data16.data()), data16.size(), 0);
    EXPECT_EQ(rawHash16, optHash16);
}

/*
* @tc.name: ComputeHashForData
* @tc.desc: For long data,
* test new hashcode compute function result equals original algorithm: hash = hash*31 + data[i]
* @tc.type: FUNC
*/
HWTEST_F_L0(EcmaStringHashTest, ComputeHashForData_003)
{
    std::string data = "Hello World! abcdefghijklmnopqrstuvwxyz0123456789 !#$%&'()*+,-./:;=?@[]^_`{|}~";
    std::u16string data16 = u"Hello World! abcdefghijklmnopqrstuvwxyz0123456789 !#$%&'()*+,-./:;=?@[]^_`{|}~ 鸿蒙";
    for (int i = 0; i < 100; i++) {
        data += std::to_string(i);
        std::wstring wstring = std::to_wstring(i);
        data16 += std::u16string(wstring.begin(), wstring.end());
    }
    uint32_t rawHash8 = ComputeHashRaw(reinterpret_cast<uint8_t*>(data.data()), data.size(), 0);
    uint32_t optHash8 = ComputeHashOpt(reinterpret_cast<uint8_t*>(data.data()), data.size(), 0);
    EXPECT_EQ(rawHash8, optHash8);

    uint32_t rawHash16 = ComputeHashRaw(reinterpret_cast<uint16_t*>(data16.data()), data16.size(), 0);
    uint32_t optHash16 = ComputeHashOpt(reinterpret_cast<uint16_t*>(data16.data()), data16.size(), 0);
    EXPECT_EQ(rawHash16, optHash16);
}

HWTEST_F_L0(EcmaStringHashTest, CalculateConcatHashCode)
{
    std::string firstData = "Hello World! OpenHarmony";
    std::string secondData = "Hello World! OpenHarmony";
    JSHandle<EcmaString> firstString(thread, EcmaStringAccessor::CreateFromUtf8(
        instance, reinterpret_cast<uint8_t*>(firstData.data()), firstData.size(), true));
    JSHandle<EcmaString> secondString(thread, EcmaStringAccessor::CreateFromUtf8(
        instance, reinterpret_cast<uint8_t*>(secondData.data()), secondData.size(), true));

    std::string totalString = firstData + secondData;
    uint32_t totalHash = ComputeHashOpt(reinterpret_cast<uint8_t*>(totalString.data()), totalString.size(), 0);
    uint32_t concatHash = ComputeConcatHash(thread, firstString, secondString);
    EXPECT_EQ(totalHash, concatHash);
}
}