/*
 * Copyright (c) 2021 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 "image_diff.h"
#include <iostream>
#include <vector>
#include "diffpatch.h"

using namespace Hpackage;

namespace UpdatePatch {
#define GET_REAL_DATA_LEN(info) (info) ->packedSize + (info)->dataOffset - (info)->headerOffset
constexpr int32_t LZ4F_MAX_BLOCKID = 7;
constexpr int32_t ZIP_MAX_LEVEL = 9;
constexpr int32_t MAX_NEW_LENGTH = 1 << 20;

template<class DataType>
static void WriteToFile(std::ofstream &patchFile, DataType data, size_t dataSize)
{
    patchFile.write(reinterpret_cast<const char*>(&data), dataSize);
}

static size_t GetHeaderSize(const ImageBlock &block)
{
    switch (block.type) {
        case BLOCK_NORMAL:
            return sizeof(uint32_t) + PATCH_NORMAL_MIN_HEADER_LEN;
        case BLOCK_DEFLATE:
            return sizeof(uint32_t) + PATCH_DEFLATE_MIN_HEADER_LEN;
        case BLOCK_RAW:
            return sizeof(uint32_t) + sizeof(uint32_t) + block.newInfo.length;
        case BLOCK_LZ4:
            return sizeof(uint32_t) + PATCH_LZ4_MIN_HEADER_LEN;
        default:
            return 0;
    }
}

int32_t ImageDiff::MakePatch(const std::string &patchName)
{
    PATCH_LOGI("ImageDiff::MakePatch %s limit_:%d", patchName.c_str(), limit_);
    if (newParser_ == nullptr || oldParser_ == nullptr) {
        PATCH_LOGE("Invalid parser");
        return -1;
    }
    BlockBuffer newBuffer;
    BlockBuffer olduffer;
    int32_t ret = newParser_->GetPkgBuffer(newBuffer);
    int32_t ret1 = oldParser_->GetPkgBuffer(olduffer);
    if (ret != 0 || ret1 != 0) {
        PATCH_LOGE("Failed to get pkgbuffer");
        return -1;
    }

    PATCH_LOGI("ImageDiff::MakePatch newBuffer %zu %zu ", newBuffer.length, olduffer.length);

    if (limit_ == 0 || newBuffer.length <= limit_) {
        ImageBlock block = {
            BLOCK_NORMAL,
            { newBuffer.buffer, 0, newBuffer.length },
            { olduffer.buffer, 0, olduffer.length },
        };
        updateBlocks_.push_back(std::move(block));
    } else {
        PatchBuffer oldInfo = { olduffer.buffer, 0, olduffer.length };
        PatchBuffer newInfo = { newBuffer.buffer, 0, newBuffer.length };
        ret = SplitImage(oldInfo, newInfo);
        if (ret != 0) {
            PATCH_LOGE("Failed to split imgage");
            return ret;
        }
    }
    return DiffImage(patchName);
}

int32_t ImageDiff::SplitImage(const PatchBuffer &oldInfo, const PatchBuffer &newInfo)
{
    size_t blockCount = newInfo.length / limit_ + 1;
    size_t oldBlockSize = oldInfo.length / blockCount;
    size_t newBlockSize = newInfo.length / blockCount;
    int32_t type = (oldInfo.length == 0) ? BLOCK_RAW : BLOCK_NORMAL;
    size_t i = 0;
    while (i < blockCount - 1) {
        ImageBlock block = {
            type,
            { newInfo.buffer, newInfo.start + newBlockSize * i, newBlockSize },
            { oldInfo.buffer, oldInfo.start + oldBlockSize * i, oldBlockSize },
        };
        updateBlocks_.push_back(std::move(block));
        i++;
    }
    ImageBlock block = {
        type,
        { newInfo.buffer, newInfo.start + newBlockSize * i, newInfo.length - (newInfo.start + newBlockSize * i) },
        { oldInfo.buffer, oldInfo.start + oldBlockSize * i, oldInfo.length - (oldInfo.start + oldBlockSize * i) },
    };
    updateBlocks_.push_back(std::move(block));
    return 0;
}

int32_t ImageDiff::WriteHeader(std::ofstream &patchFile,
    std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const
{
    int32_t ret = 0;
    switch (block.type) {
        case BLOCK_NORMAL: {
            size_t patchSize = 0;
            BlockBuffer newInfo = { block.newInfo.buffer + block.newInfo.start, block.newInfo.length };
            BlockBuffer oldInfo = { block.oldInfo.buffer + block.oldInfo.start, block.oldInfo.length };
            ret = MakeBlockPatch(block, blockPatchFile, newInfo, oldInfo, patchSize);
            if (ret != 0) {
                PATCH_LOGE("Failed to make block patch");
                return -1;
            }
            PATCH_LOGI("WriteHeader BLOCK_NORMAL patchOffset %zu oldInfo %ld %ld newInfo:%zu %zu patch %zu %zu",
                static_cast<size_t>(patchFile.tellp()),
                block.oldInfo.start, block.oldInfo.length, block.newInfo.start, block.newInfo.length,
                dataOffset, patchSize);
            WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.start), sizeof(int64_t));
            WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.length), sizeof(int64_t));
            WriteToFile<int64_t>(patchFile, static_cast<int64_t>(dataOffset), sizeof(int64_t));
            dataOffset += patchSize;
            break;
        }
        case BLOCK_RAW: {
            PATCH_LOGI("WriteHeader BLOCK_ROW patchOffset %zu dataOffset:%zu newInfo:%zu",
                static_cast<size_t>(patchFile.tellp()), dataOffset, block.newInfo.length);
            WriteToFile<int32_t>(patchFile, static_cast<int32_t>(block.newInfo.length), sizeof(int32_t));
            patchFile.write(reinterpret_cast<const char*>(block.newInfo.buffer + block.newInfo.start),
                block.newInfo.length);
            BlockBuffer rawData = { block.newInfo.buffer + block.newInfo.start, block.newInfo.length };
            PATCH_LOGI("WriteHeader BLOCK_ROW hash %zu %s",
                block.newInfo.length, GeneraterBufferHash(rawData).c_str());
            break;
        }
        default:
            break;
    }
    return ret;
}

int32_t ImageDiff::MakeBlockPatch(ImageBlock &block, std::fstream &blockPatchFile,
    const BlockBuffer &newInfo, const BlockBuffer &oldInfo, size_t &patchSize) const
{
    if (!usePatchFile_) {
        std::vector<uint8_t> patchData;
        int32_t ret = BlocksDiff::MakePatch(newInfo, oldInfo, patchData, 0, patchSize);
        if (ret != 0) {
            PATCH_LOGE("Failed to make block patch");
            return -1;
        }
        BlockBuffer patchBuffer = {patchData.data(), patchSize};
        PATCH_DEBUG("MakeBlockPatch hash %zu %s", patchSize, GeneraterBufferHash(patchBuffer).c_str());
        block.patchData = std::move(patchData);
    } else {
        int32_t ret = BlocksDiff::MakePatch(newInfo, oldInfo, blockPatchFile, patchSize);
        if (ret != 0) {
            PATCH_LOGE("Failed to make block patch");
            return -1;
        }
        PATCH_LOGI("MakeBlockPatch patch %zu patch %zu",
            patchSize, static_cast<size_t>(blockPatchFile.tellp()));
    }
    return 0;
}

int32_t ImageDiff::WritePatch(std::ofstream &patchFile, std::fstream &blockPatchFile)
{
    if (usePatchFile_) { // copy to patch
        size_t bsPatchSize = static_cast<size_t>(blockPatchFile.tellp());
        PATCH_LOGI("WritePatch patch block patch %zu img patch offset %zu",
            bsPatchSize, static_cast<size_t>(patchFile.tellp()));
        blockPatchFile.seekg(0, std::ios::beg);
        std::vector<char> buffer(IGMDIFF_LIMIT_UNIT);
        while (bsPatchSize > 0) {
            size_t readLen = (bsPatchSize > IGMDIFF_LIMIT_UNIT) ? IGMDIFF_LIMIT_UNIT : bsPatchSize;
            blockPatchFile.read(buffer.data(), readLen);
            patchFile.write(buffer.data(), readLen);
            bsPatchSize -= readLen;
        }
        PATCH_LOGI("WritePatch patch %zu", static_cast<size_t>(patchFile.tellp()));
    } else {
        for (size_t index = 0; index < updateBlocks_.size(); index++) {
            if (updateBlocks_[index].type == BLOCK_RAW) {
                continue;
            }
            PATCH_LOGI("WritePatch [%zu] write patch patchOffset %zu length %zu",
                index, static_cast<size_t>(patchFile.tellp()), updateBlocks_[index].patchData.size());
            patchFile.write(reinterpret_cast<const char*>(updateBlocks_[index].patchData.data()),
                updateBlocks_[index].patchData.size());
        }
    }
    return 0;
}

int32_t ImageDiff::DiffImage(const std::string &patchName)
{
    std::fstream blockPatchFile;
    std::ofstream patchFile(patchName, std::ios::out | std::ios::trunc | std::ios::binary);
    if (patchFile.fail()) {
        PATCH_LOGE("Failed to open %s", patchName.c_str());
        return -1;
    }
    patchFile.write(PKGDIFF_MAGIC, std::char_traits<char>::length(PKGDIFF_MAGIC));
    size_t dataOffset = std::char_traits<char>::length(PKGDIFF_MAGIC);
    uint32_t size = static_cast<uint32_t>(updateBlocks_.size());
    patchFile.write(reinterpret_cast<const char*>(&size), sizeof(uint32_t));
    dataOffset += sizeof(uint32_t);

    for (size_t index = 0; index < updateBlocks_.size(); index++) {
        dataOffset += GetHeaderSize(updateBlocks_[index]);
        if (updateBlocks_[index].destOriginalLength >= MAX_NEW_LENGTH ||
            updateBlocks_[index].newInfo.length >= MAX_NEW_LENGTH) {
            usePatchFile_ = true;
        }
    }

    if (usePatchFile_) {
        blockPatchFile.open(patchName + ".bspatch", std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
        if (blockPatchFile.fail()) {
            PATCH_LOGE("Failed to open bspatch %s", patchName.c_str());
            return -1;
        }
    }

    for (size_t index = 0; index < updateBlocks_.size(); index++) {
        PATCH_LOGI("DiffImage [%zu] write header patchOffset %zu dataOffset %zu",
            index, static_cast<size_t>(patchFile.tellp()), dataOffset);
        patchFile.write(reinterpret_cast<const char*>(&updateBlocks_[index].type), sizeof(uint32_t));
        int32_t ret = WriteHeader(patchFile, blockPatchFile, dataOffset, updateBlocks_[index]);
        if (ret != 0) {
            PATCH_LOGE("Failed to write header");
            return -1;
        }
    }

    int32_t ret = WritePatch(patchFile, blockPatchFile);
    if (ret != 0) {
        PATCH_LOGE("Failed to write patch");
        return -1;
    }
    PATCH_LOGI("DiffImage success patchOffset %zu %s", static_cast<size_t>(patchFile.tellp()), patchName.c_str());
    patchFile.close();
    return 0;
}

int32_t CompressedImageDiff::MakePatch(const std::string &patchName)
{
    PATCH_DEBUG("CompressedImageDiff::MakePatch %s limit_:%d", patchName.c_str(), limit_);
    if (newParser_ == nullptr || oldParser_ == nullptr) {
        PATCH_LOGE("Invalid parser");
        return -1;
    }
    BlockBuffer newBuffer;
    BlockBuffer oldBuffer;
    int32_t ret = newParser_->GetPkgBuffer(newBuffer);
    int32_t ret1 = oldParser_->GetPkgBuffer(oldBuffer);
    if (ret != 0 || ret1 != 0) {
        PATCH_LOGE("Failed to get pkgbuffer");
        return -1;
    }

    if (limit_ != 0 && newBuffer.length >= limit_) {
        PatchBuffer oldInfo = { oldBuffer.buffer, 0, oldBuffer.length };
        PatchBuffer newInfo = { newBuffer.buffer, 0, newBuffer.length };
        ret = SplitImage(oldInfo, newInfo);
        PATCH_CHECK(ret == 0, return -1, "Failed to split imgage");
        return DiffImage(patchName);
    }

    size_t oldOffset = 0;
    size_t newOffset = 0;
    for (size_t i = 0; i < newParser_->GetFileIds().size(); i++) {
        PATCH_LOGI("CompressedImageDiff::DiffFile %s oldOffset:%zu newOffset:%zu",
            newParser_->GetFileIds()[i].c_str(), oldOffset, newOffset);
        ret = DiffFile(newParser_->GetFileIds()[i], oldOffset, newOffset);
        PATCH_CHECK(ret == 0, break, "Failed to generate patch");
    }
    if (ret != 0) {
        updateBlocks_.clear();
        return ImageDiff::MakePatch(patchName);
    }
    PATCH_LOGI("MakePatch oldOffset:%zu newOffset:%zu newBuffer %zu oldBuffer: %zu",
        oldOffset, newOffset, newBuffer.length, oldBuffer.length);
    if (newOffset < newBuffer.length) {
        ImageBlock block = {
            BLOCK_RAW,
            { newBuffer.buffer, newOffset, newBuffer.length - newOffset },
            { oldBuffer.buffer, oldOffset, oldBuffer.length - oldOffset },
        };
        updateBlocks_.push_back(std::move(block));
    }
    return DiffImage(patchName);
}

int32_t CompressedImageDiff::DiffFile(const std::string &fileName, size_t &oldOffset, size_t &newOffset)
{
    BlockBuffer orgNewBuffer;
    BlockBuffer orgOldBuffer;
    int32_t ret = newParser_->GetPkgBuffer(orgNewBuffer);
    int32_t ret1 = oldParser_->GetPkgBuffer(orgOldBuffer);
    PATCH_CHECK((ret == 0 && ret1 == 0), return -1, "Failed to get pkgbuffer");

    std::vector<uint8_t> newBuffer;
    std::vector<uint8_t> oldBuffer;
    ret = newParser_->Extract(fileName, newBuffer);
    const FileInfo *newFileInfo = newParser_->GetFileInfo(fileName);
    PATCH_CHECK(ret == 0 && newFileInfo != nullptr, return -1, "Failed to get new data");
    newOffset += GET_REAL_DATA_LEN(newFileInfo);
    PATCH_CHECK(limit_ == 0 || newFileInfo->unpackedSize < limit_, return PATCH_EXCEED_LIMIT,
        "Exceed limit, so make patch by original file");

    ret = oldParser_->Extract(fileName, oldBuffer);
    if (ret != 0) {
        ImageBlock block = {
            BLOCK_RAW,
            { orgNewBuffer.buffer, newFileInfo->headerOffset, GET_REAL_DATA_LEN(newFileInfo) },
            { orgOldBuffer.buffer, 0, orgOldBuffer.length },
        };
        updateBlocks_.push_back(std::move(block));
        return 0;
    }
    const FileInfo *oldFileInfo = oldParser_->GetFileInfo(fileName);
    PATCH_CHECK(oldFileInfo != nullptr, return -1, "Failed to get file info");
    oldOffset += GET_REAL_DATA_LEN(oldFileInfo);

    BlockBuffer newData = {newBuffer.data(), newFileInfo->unpackedSize};
    ret = TestAndSetConfig(newData, fileName);
    PATCH_CHECK(ret == 0, return -1, "Failed to test zip config");

    if (type_ != BLOCK_LZ4 && newFileInfo->dataOffset > newFileInfo->headerOffset) {
        ImageBlock block = {
            BLOCK_RAW,
            { orgNewBuffer.buffer, newFileInfo->headerOffset, newFileInfo->dataOffset - newFileInfo->headerOffset },
            { orgOldBuffer.buffer, oldFileInfo->headerOffset, oldFileInfo->dataOffset - oldFileInfo->headerOffset },
        };
        updateBlocks_.push_back(std::move(block));
    }

    ImageBlock block = {
        type_,
        { orgNewBuffer.buffer, newFileInfo->dataOffset, newFileInfo->packedSize },
        { orgOldBuffer.buffer, oldFileInfo->dataOffset, oldFileInfo->packedSize },
    };
    block.srcOriginalData = std::move(oldBuffer);
    block.destOriginalData = std::move(newBuffer);
    block.srcOriginalLength = oldFileInfo->unpackedSize;
    block.destOriginalLength = newFileInfo->unpackedSize;
    updateBlocks_.push_back(std::move(block));
    return 0;
}

int32_t ZipImageDiff::WriteHeader(std::ofstream &patchFile,
    std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const
{
    int32_t ret = 0;
    if (block.type == BLOCK_DEFLATE) {
        size_t patchSize = 0;
        BlockBuffer oldInfo = { block.srcOriginalData.data(), block.srcOriginalLength };
        BlockBuffer newInfo = { block.destOriginalData.data(), block.destOriginalLength };
        ret = MakeBlockPatch(block, blockPatchFile, newInfo, oldInfo, patchSize);
        if (ret != 0) {
            PATCH_LOGE("Failed to make block patch");
            return -1;
        }

        PATCH_LOGI("WriteHeader BLOCK_DEFLATE patchoffset %zu dataOffset:%zu patchData:%zu",
            static_cast<size_t>(patchFile.tellp()), dataOffset, patchSize);
        PATCH_LOGI("WriteHeader oldInfo start:%zu length:%zu", block.oldInfo.start, block.oldInfo.length);
        PATCH_LOGI("WriteHeader uncompressedLength:%zu %zu", block.srcOriginalLength, block.destOriginalLength);
        PATCH_LOGI("WriteHeader level_:%d method_:%d windowBits_:%d memLevel_:%d strategy_:%d",
            level_, method_, windowBits_, memLevel_, strategy_);

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.start), sizeof(int64_t));
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.length), sizeof(int64_t));
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(dataOffset), sizeof(int64_t));
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.srcOriginalLength), sizeof(int64_t));
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.destOriginalLength), sizeof(int64_t));

        WriteToFile<int32_t>(patchFile, level_, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, method_, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, windowBits_, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, memLevel_, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, strategy_, sizeof(int32_t));
        dataOffset += patchSize;
    } else {
        ret = ImageDiff::WriteHeader(patchFile, blockPatchFile, dataOffset, block);
    }
    return ret;
}

int32_t ZipImageDiff::TestAndSetConfig(const BlockBuffer &buffer, const std::string &fileName)
{
    const FileInfo *fileInfo = newParser_->GetFileInfo(fileName);
    if (fileInfo == nullptr) {
        PATCH_LOGE("Failed to get file info");
        return -1;
    }
    ZipFileInfo *info = reinterpret_cast<ZipFileInfo *>(const_cast<FileInfo *>(fileInfo));
    method_ = info->method;
    level_ = info->level;
    windowBits_ = info->windowBits;
    memLevel_ = info->memLevel;
    strategy_ = info->strategy;

    BlockBuffer orgNewBuffer;
    int32_t ret = newParser_->GetPkgBuffer(orgNewBuffer);
    if (ret != 0) {
        PATCH_LOGE("Failed to get pkgbuffer");
        return -1;
    }
    ZipFileInfo zipInfo {};
    zipInfo.fileInfo.packMethod = info->fileInfo.packMethod;
    zipInfo.method = info->method;
    zipInfo.level = info->level;
    zipInfo.windowBits = info->windowBits;
    zipInfo.memLevel = info->memLevel;
    zipInfo.strategy = info->strategy;

    PATCH_LOGI("TestAndSetConfig new info %zu %zu %zu %zu",
        fileInfo->unpackedSize, fileInfo->packedSize, fileInfo->dataOffset, fileInfo->headerOffset);
    PATCH_LOGI("TestAndSetConfig level_:%d method_:%d windowBits_:%d memLevel_:%d strategy_:%d",
        level_, method_, windowBits_, memLevel_, strategy_);
    BlockBuffer orgData = {orgNewBuffer.buffer + fileInfo->dataOffset, fileInfo->packedSize};
    PATCH_DEBUG("DiffFile new orignial hash %zu %s", fileInfo->packedSize, GeneraterBufferHash(orgData).c_str());

    std::vector<uint8_t> data;
    for (int32_t i = ZIP_MAX_LEVEL; i >= 0; i--) {
        zipInfo.level = i;
        size_t bufferSize = 0;
        ret = CompressData(&zipInfo.fileInfo, buffer, data, bufferSize);
        PATCH_CHECK(ret == 0, return -1, "Can not Compress buff ");

        if ((bufferSize == fileInfo->packedSize) &&
            memcmp(data.data(), orgNewBuffer.buffer + fileInfo->dataOffset, bufferSize) == 0) {
            level_ = i;
            return 0;
        }
    }
    return -1;
}

int32_t Lz4ImageDiff::WriteHeader(std::ofstream &patchFile,
    std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const
{
    int32_t ret = 0;
    if (block.type == BLOCK_LZ4) {
        size_t patchSize = 0;
        BlockBuffer oldInfo = { block.srcOriginalData.data(), block.srcOriginalLength };
        BlockBuffer newInfo = { block.destOriginalData.data(), block.destOriginalLength };
        ret = MakeBlockPatch(block, blockPatchFile, newInfo, oldInfo, patchSize);
        if (ret != 0) {
            PATCH_LOGE("Failed to make block patch");
            return -1;
        }

        PATCH_LOGI("WriteHeader BLOCK_LZ4 patchoffset %zu dataOffset:%zu %zu",
            static_cast<size_t>(patchFile.tellp()), dataOffset, patchSize);
        PATCH_LOGI("WriteHeader oldInfo start:%zu length:%zu", block.oldInfo.start, block.oldInfo.length);
        PATCH_LOGI("WriteHeader uncompressedLength:%zu %zu", block.srcOriginalLength, block.destOriginalLength);
        PATCH_LOGI("WriteHeader level_:%d method_:%d blockIndependence_:%d contentChecksumFlag_:%d blockSizeID_:%d %d",
            compressionLevel_, method_, blockIndependence_, contentChecksumFlag_, blockSizeID_, autoFlush_);
        PATCH_LOGI("WriteHeader BLOCK_LZ4 decompressed hash %zu %s",
            newInfo.length, GeneraterBufferHash(newInfo).c_str());
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.start), sizeof(int64_t));
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.length), sizeof(int64_t));
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(dataOffset), sizeof(int64_t));
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.srcOriginalLength), sizeof(int64_t));
        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.destOriginalLength), sizeof(int64_t));

        int32_t magic = (method_ == PKG_COMPRESS_METHOD_LZ4_BLOCK) ? LZ4B_MAGIC : LZ4S_MAGIC;
        WriteToFile<int32_t>(patchFile, compressionLevel_, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, magic, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, blockIndependence_, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, contentChecksumFlag_, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, blockSizeID_, sizeof(int32_t));
        WriteToFile<int32_t>(patchFile, autoFlush_, sizeof(int32_t));
        dataOffset += patchSize;
    } else {
        ret = ImageDiff::WriteHeader(patchFile, blockPatchFile, dataOffset, block);
    }
    return ret;
}

int32_t Lz4ImageDiff::TestAndSetConfig(const BlockBuffer &buffer, const std::string &fileName)
{
    const FileInfo *fileInfo = newParser_->GetFileInfo(fileName);
    PATCH_CHECK(fileInfo != nullptr, return -1, "Failed to get file info");
    Lz4FileInfo *info = reinterpret_cast<Lz4FileInfo *>(const_cast<FileInfo *>(fileInfo));
    method_ = static_cast<int32_t>(info->fileInfo.packMethod);
    compressionLevel_ = info->compressionLevel;
    blockIndependence_ = info->blockIndependence;
    contentChecksumFlag_ = info->contentChecksumFlag;
    blockSizeID_ = info->blockSizeID;
    autoFlush_ = info->autoFlush;

    BlockBuffer orgNewBuffer;
    int32_t ret = newParser_->GetPkgBuffer(orgNewBuffer);
    PATCH_CHECK(ret == 0, return -1, "Failed to get pkgbuffer");
    Lz4FileInfo lz4Info {};
    lz4Info.fileInfo.packMethod = info->fileInfo.packMethod;
    lz4Info.compressionLevel = info->compressionLevel;
    lz4Info.blockIndependence = info->blockIndependence;
    lz4Info.contentChecksumFlag = info->contentChecksumFlag;
    lz4Info.blockSizeID = info->blockSizeID;
    lz4Info.autoFlush = info->autoFlush;

    PATCH_DEBUG("TestAndSetConfig level_:%d method_:%d blockIndependence_:%d contentChecksumFlag_:%d blockSizeID_:%d",
        compressionLevel_, method_, blockIndependence_, contentChecksumFlag_, blockSizeID_);
    PATCH_DEBUG("DiffFile new info %zu %zu %zu %zu",
        fileInfo->unpackedSize, fileInfo->packedSize, fileInfo->dataOffset, fileInfo->headerOffset);
    BlockBuffer orgData = { orgNewBuffer.buffer, fileInfo->packedSize + sizeof(uint32_t) };
    PATCH_DEBUG("DiffFile new orignial hash %zu %s",
        fileInfo->packedSize + sizeof(uint32_t), GeneraterBufferHash(orgData).c_str());

    std::vector<uint8_t> data;
    for (int32_t i = 0; i <= LZ4F_MAX_BLOCKID; i++) {
        lz4Info.blockSizeID = i;
        size_t bufferSize = 0;
        ret = CompressData(&lz4Info.fileInfo, buffer, data, bufferSize);
        PATCH_CHECK(ret == 0, return -1, "Can not Compress buff ");

        if ((bufferSize == fileInfo->packedSize + sizeof(uint32_t)) &&
            memcmp(data.data(), orgNewBuffer.buffer + fileInfo->headerOffset, bufferSize) == 0) {
            blockSizeID_ = i;
            return 0;
        }
    }
    return -1;
}

int32_t CompressedImageDiff::CompressData(Hpackage::PkgManager::FileInfoPtr info,
    const BlockBuffer &buffer, std::vector<uint8_t> &outData, size_t &outSize) const
{
    Hpackage::PkgManager *pkgManager = Hpackage::PkgManager::GetPackageInstance();
    if (pkgManager == nullptr) {
        PATCH_LOGE("Can not get manager ");
        return -1;
    }
    Hpackage::PkgManager::StreamPtr stream1 = nullptr;
    pkgManager->CreatePkgStream(stream1, "gzip",
        [&outData, &outSize](const PkgBuffer &data,
            size_t size, size_t start, bool isFinish, const void *context) ->int {
            if (isFinish) {
                return 0;
            }
            outSize += size;
            if ((start + outSize) > outData.size()) {
                outData.resize(IGMDIFF_LIMIT_UNIT * ((start + outSize) / IGMDIFF_LIMIT_UNIT + 1));
            }
            return memcpy_s(outData.data() + start, outData.size(), data.buffer, size);
        }, nullptr);
    int32_t ret = pkgManager->CompressBuffer(info, {buffer.buffer, buffer.length}, stream1);
    if (ret != 0) {
        PATCH_LOGE("Can not Compress buff ");
        return -1;
    }
    PATCH_DEBUG("UpdateDiff::MakePatch totalSize: %zu", outSize);
    return 0;
}
} // namespace UpdatePatch