/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.

 * ubs-io is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *      http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "slice.h"
#include "bdm_core.h"
#include "bio_crc_util.h"
#include "bio_log.h"
#include "bio_tracepoint_helper.h"
#include "securec.h"

namespace ock {
namespace bio {
bool Slice::IsTheSameWith(const SlicePtr &other)
{
    if (other->GetFlowType() != GetFlowType()) {
        return false;
    }

    if (other->GetLength() != GetLength()) {
        return false;
    }

    auto &fromAddrs = other->GetAddrs();
    auto &toAddrs = GetAddrs();

    if (fromAddrs.size() != toAddrs.size()) {
        return false;
    }

    for (uint32_t i = 0; i < fromAddrs.size(); ++i) {
        auto &fromAddr = fromAddrs[i];
        auto &toAddr = toAddrs[i];
        if (fromAddr.chunkId != toAddr.chunkId) {
            return false;
        }
        if (fromAddr.chunkOffset != toAddr.chunkOffset) {
            return false;
        }
        if (fromAddr.chunkLen != toAddr.chunkLen) {
            return false;
        }
    }
    return true;
}

SlicePtr Slice::Split(uint64_t offset, uint64_t length)
{
    if (UNLIKELY(offset >= mLength || length > mLength || offset + length > mLength)) {
        return nullptr;
    }
    if (offset == 0 && length == mLength) {
        return this;
    }
    std::vector<FlowAddr> newAddrs;
    uint64_t rangeStart = 0;
    uint64_t rangeEnd = 0;
    uint64_t splitStart = offset;
    for (const auto &addr : mAddrs) {
        rangeEnd = rangeStart + addr.chunkLen;
        if (splitStart >= rangeStart && splitStart < rangeEnd) {
            uint64_t splitOffset = splitStart - rangeStart;
            uint64_t splitLen = rangeEnd - splitStart;
            if (splitLen >= length - (splitStart - offset)) {
                newAddrs.emplace_back(addr.chunkId, addr.chunkOffset + splitOffset, length - (splitStart - offset));
                LOG_DEBUG("Split, offset:" << addr.chunkOffset + splitOffset
                                           << ", len:" << length - (splitStart - offset));
                break;
            } else {
                LOG_DEBUG("Split, offset:" << addr.chunkOffset + splitOffset << ", len:" << splitLen);
                newAddrs.emplace_back(addr.chunkId, addr.chunkOffset + splitOffset, splitLen);
            }
            splitStart += splitLen;
        }
        rangeStart += addr.chunkLen;
    }

    return MakeRef<Slice>(length, newAddrs, mFlowType);
}

uint64_t Slice::GetSerializeLen()
{
    uint64_t len = 0;
    len += sizeof(mdataCrc);
    len += sizeof(mFlowType);
    len += sizeof(mLength);
    size_t vsize = mAddrs.size();
    len += sizeof(vsize);
    len += sizeof(FlowAddr) * vsize;
    return len;
}

BResult Slice::Serialize(char *data, uint64_t dataLen, uint64_t &length)
{
    uint64_t pos = 0;
    uint64_t cpyLen = dataLen;
    ChkTrueNot(data != nullptr, BIO_INVALID_PARAM);
    BResult ret = BIO_OK;
    ret = memcpy_s(data + pos, cpyLen, &mdataCrc, sizeof(mdataCrc));
    ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "Memory copy failed.");
    pos += sizeof(mdataCrc);
    cpyLen -= sizeof(mdataCrc);
    ret = memcpy_s(data + pos, cpyLen, &mFlowType, sizeof(mFlowType));
    ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "Memory copy failed.");
    pos += sizeof(mFlowType);
    cpyLen -= sizeof(mFlowType);
    ret = memcpy_s(data + pos, cpyLen, &mLength, sizeof(mLength));
    ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "Memory copy failed.");
    pos += sizeof(mLength);
    cpyLen -= sizeof(mLength);
    size_t vsize = mAddrs.size();
    ret = memcpy_s(data + pos, cpyLen, &vsize, sizeof(vsize));
    ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "Memory copy failed.");
    pos += sizeof(vsize);
    cpyLen -= sizeof(vsize);
    for (size_t i = 0; i < vsize; i++) {
        ret = memcpy_s(data + pos, cpyLen, &mAddrs[i], sizeof(FlowAddr));
        ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "Memory copy failed.");
        pos += sizeof(FlowAddr);
        cpyLen -= sizeof(FlowAddr);
    }
    length = pos;
    return BIO_OK;
}

BResult Slice::Deserialize(char *data, uint64_t length)
{
    uint64_t pos = 0;
    ChkTrueNot(data != nullptr, BIO_INVALID_PARAM);
    ChkTrue(length >= pos + sizeof(mdataCrc), BIO_INVALID_PARAM,
            "Failed to deserialize data, length:" << length << "  pos + sizeof(mFlowType):" << pos + sizeof(mdataCrc));
    int ret = memcpy_s(&mdataCrc, sizeof(mdataCrc), data + pos, sizeof(mdataCrc));
    ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "datacrc memory copy failed.");
    pos += sizeof(mdataCrc);
    ChkTrue(length >= pos + sizeof(mFlowType), BIO_INVALID_PARAM,
            "Failed to deserialize data, length:" << length << "  pos + sizeof(mFlowType):" << pos + sizeof(mFlowType));
    ret = memcpy_s(&mFlowType, sizeof(mFlowType), data + pos, sizeof(mFlowType));
    ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "flow data memory copy failed.");
    pos += sizeof(mFlowType);
    ChkTrue(length >= pos + sizeof(mLength), BIO_INVALID_PARAM,
            "Failed to deserialize data, length:" << length << "  pos + sizeof(mLength):" << pos + sizeof(mLength));
    ret = memcpy_s(&mLength, sizeof(mLength), data + pos, sizeof(mLength));
    ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "length memory copy failed.");
    pos += sizeof(mLength);
    size_t vsize = 0;
    BIO_TP_START(DESERIALIZE_SET_VSIZE, &vsize, NO_3);
    ChkTrue(length >= pos + sizeof(vsize), BIO_INVALID_PARAM,
            "Failed to deserialize data, length:" << length << "  pos + sizeof(vsize):" << pos + sizeof(vsize));
    ret = memcpy_s(&vsize, sizeof(vsize), data + pos, sizeof(vsize));
    ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "vsize memory copy failed.");
    pos += sizeof(vsize);
    BIO_TP_END;
    ChkTrue(vsize <= NO_4, BIO_INVALID_PARAM, "Failed to deserialize data, vsize:" << vsize << ", failed.");
    for (size_t i = 0; i < vsize; i++) {
        FlowAddr flowAddr;
        ChkTrue(length >= pos + sizeof(FlowAddr), BIO_INVALID_PARAM,
                "Failed to deserialize data, length:" << length
                                                      << " pos + sizeof(FlowAddr):" << pos + sizeof(FlowAddr));
        ret = memcpy_s(&flowAddr, sizeof(FlowAddr), data + pos, sizeof(FlowAddr));
        ChkTrue(ret == BIO_OK, BIO_INNER_ERR, "flow addr memory copy failed.");
        mAddrs.push_back(flowAddr);
        pos += sizeof(FlowAddr);
    }
    return BIO_OK;
}

std::string Slice::ToString()
{
    std::stringstream ss;
    ss << "type:" << mFlowType << ",length:" << mLength;
    ss << ",addr:";
    for (const auto &addr : mAddrs) {
        ss << "(" << addr.chunkOffset << "," << addr.chunkLen << ")";
    }
    return ss.str();
}

BResult Slice::CalculateDataCrc(uint32_t &valueCrc, uint64_t dataOffset, uint64_t dataLength)
{
    char *value = reinterpret_cast<char *>(aligned_alloc(NO_4096, mLength));
    ChkTrueNot(value != nullptr, BIO_ALLOC_FAIL);

    uint64_t cpyLength = mLength;
    uint64_t offset = 0;
    if (mFlowType == FLOW_MEMORY) {
        for (auto fromAddr : mAddrs) {
            auto ret = memcpy_s(reinterpret_cast<void *>(value + offset), cpyLength,
                                reinterpret_cast<void *>(fromAddr.chunkId + fromAddr.chunkOffset), fromAddr.chunkLen);
            if (ret != BIO_OK) {
                LOG_ERROR("Failed to copy data, length:" << fromAddr.chunkLen);
                free(value);
                value = nullptr;
                return ret;
            }
            offset += fromAddr.chunkLen;
            cpyLength -= fromAddr.chunkLen;
        }
    } else {
        for (auto fromAddr : mAddrs) {
            auto ret = BdmRead(fromAddr.chunkId, fromAddr.chunkOffset, reinterpret_cast<void *>(value + offset),
                               fromAddr.chunkLen);
            if (ret != BIO_OK) {
                LOG_ERROR("Failed to copy data from disk chunkId:" << (fromAddr.chunkId + fromAddr.chunkOffset)
                                                                   << " to memory by length:" << fromAddr.chunkLen
                                                                   << ".");
                free(value);
                value = nullptr;
                return BIO_DISK_IOERR;
            }
            offset += fromAddr.chunkLen;
        }
    }

    valueCrc = BioCrcUtil::Crc32(value + dataOffset, dataLength);
    free(value);
    value = nullptr;
    return BIO_OK;
}

BResult Slice::VerifyDataCrc(uint32_t originCrc, uint64_t dataOffset, uint64_t dataLength, Slice *slice)
{
    uint32_t currentCrc = 0;
    auto ret = CalculateDataCrc(currentCrc, dataOffset, dataLength);
    if (ret != BIO_OK || originCrc != currentCrc) {
        LOG_ERROR("slice verify the CRC fail, ret:" << ret << " origin crc:" << originCrc
                                                    << ", current crc:" << currentCrc);
        return ret != BIO_OK ? ret : BIO_CRC_ERR;
    }
    if (slice != nullptr) {
        slice->SetDataCrc(currentCrc);
    }
    return BIO_OK;
}
} // namespace bio
} // namespace ock