/*

 * 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.

 */



#ifndef IMGAE_DIFF_H

#define IMGAE_DIFF_H

#include <iostream>

#include <memory>

#include <vector>

#include "blocks_diff.h"

#include "update_diff.h"



namespace UpdatePatch {

struct ImageBlock {

    int32_t type;

    PatchBuffer newInfo;

    PatchBuffer oldInfo;

    size_t srcOriginalLength;

    size_t destOriginalLength;

    std::vector<uint8_t> patchData;

    std::vector<uint8_t> srcOriginalData;

    std::vector<uint8_t> destOriginalData;

};



class ImageDiff {

public:

    ImageDiff() = delete;

    ImageDiff(size_t limit, UpdateDiff::ImageParserPtr newParser, UpdateDiff::ImageParserPtr oldParser)

        : limit_(limit), newParser_(newParser), oldParser_(oldParser)  {}

    virtual ~ImageDiff()

    {

        updateBlocks_.clear();

    }



    virtual int32_t MakePatch(const std::string &patchName);

    virtual int32_t WriteHeader(std::ofstream &patchFile,

        std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const;



protected:

    int32_t SplitImage(const PatchBuffer &oldInfo, const PatchBuffer &newInfo);

    int32_t DiffImage(const std::string &patchName);

    int32_t MakeBlockPatch(ImageBlock &block, std::fstream &blockPatchFile,

        const BlockBuffer &newInfo, const BlockBuffer &oldInfo, size_t &patchSize) const;

    int32_t WritePatch(std::ofstream &patchFile, std::fstream &blockPatchFile);



    size_t limit_;

    std::vector<ImageBlock> updateBlocks_ {};

    UpdateDiff::ImageParserPtr newParser_ {nullptr};

    UpdateDiff::ImageParserPtr oldParser_ {nullptr};

    bool usePatchFile_ { false };

};



class CompressedImageDiff : public ImageDiff {

public:

    CompressedImageDiff(size_t limit,

        UpdateDiff::ImageParserPtr newParser, UpdateDiff::ImageParserPtr oldParser, int32_t type)

        : ImageDiff(limit, newParser, oldParser), type_(type) {}

    ~CompressedImageDiff() override {}



    int32_t MakePatch(const std::string &patchName) override;



protected:

    virtual int32_t TestAndSetConfig(const BlockBuffer &buffer, const std::string &fileName) = 0;

    int32_t DiffFile(const std::string &fileName, size_t &oldOffset, size_t &newOffset);

    void UpdateBlocks(const BlockBuffer &orgNewBuffer, const Hpackage::FileInfo *newFileInfo,

        const BlockBuffer &orgOldBuffer, const Hpackage::FileInfo *oldFileInfo,

        std::pair<std::vector<uint8_t>, std::vector<uint8_t>> &buffer);

    int32_t CompressData(Hpackage::PkgManager::FileInfoPtr info,

        const BlockBuffer &buffer, std::vector<uint8_t> &outData, size_t &outSize) const;

    int32_t type_;

};



class ZipImageDiff : public CompressedImageDiff {

public:

    ZipImageDiff(size_t limit, UpdateDiff::ImageParserPtr newParser, UpdateDiff::ImageParserPtr oldParser)

        : CompressedImageDiff(limit, newParser, oldParser, BLOCK_DEFLATE) {}

    ~ZipImageDiff() override {}



    int32_t WriteHeader(std::ofstream &patchFile,

        std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const override;



protected:

    int32_t TestAndSetConfig(const BlockBuffer &buffer, const std::string &fileName) override;



    int32_t method_ = 0;

    int32_t level_ = 0;

    int32_t windowBits_ = 0;

    int32_t memLevel_ = 0;

    int32_t strategy_ = 0;

};



class Lz4ImageDiff : public CompressedImageDiff {

public:

    Lz4ImageDiff(size_t limit, UpdateDiff::ImageParserPtr newParser, UpdateDiff::ImageParserPtr oldParser)

        : CompressedImageDiff(limit, newParser, oldParser, BLOCK_LZ4) {}

    ~Lz4ImageDiff() override {}



    int32_t WriteHeader(std::ofstream &patchFile,

        std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const override;



protected:

    int32_t TestAndSetConfig(const BlockBuffer &buffer, const std::string &fileName) override;



private:

    int32_t method_ {0};

    int32_t compressionLevel_ {0};

    int32_t blockIndependence_ {0};

    int32_t contentChecksumFlag_ {0};

    int32_t blockSizeID_ {0};

    int32_t autoFlush_ {1};

};



class GZipImageDiff : public ZipImageDiff {

public:

    GZipImageDiff(size_t limit, UpdateDiff::ImageParserPtr newParser, UpdateDiff::ImageParserPtr oldParser)

        : ZipImageDiff(limit, newParser, oldParser) {}

    ~GZipImageDiff() override {}

};

} // namespace UpdatePatch

#endif // IMGAE_DIFF_H