/*
 * 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 UPDATE_PATCH_H
#define UPDATE_PATCH_H
#include <fstream>
#include <iostream>
#include "package/pkg_manager.h"
#include "openssl/sha.h"

namespace UpdatePatch {
struct PatchParam {
    uint8_t* oldBuff;
    size_t oldSize;
    uint8_t* patch;
    size_t patchSize;
};

struct PatchBuffer {
    uint8_t *buffer;
    size_t start;
    size_t length;
};

using BlockBuffer = Hpackage::PkgBuffer;

class UpdatePatchWriter {
public:
    UpdatePatchWriter() = default;
    virtual ~UpdatePatchWriter() {}

    virtual int32_t Init() = 0;
    virtual int32_t Write(size_t start, const BlockBuffer &buffer, size_t len) = 0;
    virtual int32_t Finish() = 0;
};

using UpdatePatchWriterPtr = UpdatePatchWriter *;

class UpdateApplyPatch {
public:
    using ImageProcessor = std::function<int(size_t start, const BlockBuffer &data, size_t size)>;

    static int32_t ApplyImagePatch(const PatchParam &param, const std::vector<uint8_t> &bonusData,
        ImageProcessor writer, const std::string& expected);
    static int32_t ApplyImagePatch(const PatchParam &param,
        UpdatePatchWriterPtr writer, const std::vector<uint8_t> &bonusData);
    static bool PreCheck(const PatchParam &param, const UpdatePatchWriterPtr writer);
    static int32_t ApplyBlockPatch(const PatchBuffer &patchInfo,
        const BlockBuffer &oldInfo, UpdatePatchWriterPtr writer);
    static int32_t ApplyBlockPatch(const PatchBuffer &patchInfo,
        const BlockBuffer &oldInfo, ImageProcessor writer, const std::string& expected);
    static int32_t ApplyBlockPatch(const PatchBuffer &patchInfo,
        const BlockBuffer &oldInfo, std::vector<uint8_t> &newData);
    static int32_t ApplyBlockPatch(const PatchBuffer &patchInfo,
        Hpackage::PkgManager::StreamPtr stream, UpdatePatchWriterPtr writer);
    static int32_t ApplyPatch(const std::string &patchName, const std::string &oldfile, const std::string &newFile);
};

class FilePatchWriter : public UpdatePatchWriter {
public:
    FilePatchWriter(const std::string &newFileName) : UpdatePatchWriter(), newFileName_(newFileName) {}
    ~FilePatchWriter() override {}

    int32_t Init() override;
    int32_t Write(size_t start, const BlockBuffer &buffer, size_t len) override;
    int32_t Finish() override;
private:
    bool init_ { false };
    std::string newFileName_;
    std::ofstream stream_;
};

class ImagePatchWriter : public UpdatePatchWriter {
public:
    ImagePatchWriter(UpdateApplyPatch::ImageProcessor writer,
        const std::string &expected, const std::string &partitionName) : UpdatePatchWriter(),
        writer_(writer), expected_(expected), partitionName_(partitionName) {}
    ~ImagePatchWriter() override {}

    int32_t Init() override;
    int32_t Write(size_t start, const BlockBuffer &buffer, size_t len) override;
    int32_t Finish() override;
private:
    bool init_ { false };
    SHA256_CTX sha256Ctx_ {};
    UpdateApplyPatch::ImageProcessor writer_;
    std::string expected_;
    std::string partitionName_;
};
} // namespace UpdatePatch
#endif // UPDATE_PATCH_H
