/*
 * 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 PKG_ALGORITHM_H
#define PKG_ALGORITHM_H

#include "pkg_algo_digest.h"
#include "pkg_algo_sign.h"
#include "pkg_stream.h"

namespace Hpackage {
struct PkgContextOffset {
    size_t srcOffset;
    size_t destOffset;
    PkgContextOffset(size_t srcOffset, size_t destOffset)
    {
        this->srcOffset = srcOffset;
        this->destOffset = destOffset;
    }
};

struct PkgContextSize {
    size_t packedSize = 0;
    size_t unpackedSize = 0;
    PkgContextSize(size_t packedSize, size_t unpackedSize)
    {
        this->packedSize = packedSize;
        this->unpackedSize = unpackedSize;
    }
};

struct PkgAlgorithmContext {
    size_t srcOffset;
    size_t destOffset;
    size_t packedSize = 0;
    size_t unpackedSize = 0;
    uint32_t crc = 0;
    uint8_t digestMethod = 0;
    uint8_t digest[DIGEST_MAX_LEN] = {};

    PkgAlgorithmContext(PkgContextOffset offset, PkgContextSize size, uint32_t crc, uint8_t digestMethod)
    {
        this->srcOffset = offset.srcOffset;
        this->destOffset = offset.destOffset;
        this->packedSize = size.packedSize;
        this->unpackedSize = size.unpackedSize;
        this->crc = crc;
        this->digestMethod = digestMethod;
    }
};

class PkgAlgorithm {
public:
    using PkgAlgorithmPtr = std::shared_ptr<PkgAlgorithm>;
    using VerifyFunction = std::function<int32_t(PkgBuffer &buffer, size_t len, size_t offset)>;

    PkgAlgorithm() {}

    virtual ~PkgAlgorithm() {}

    virtual int32_t Pack(const PkgStreamPtr inStream, const PkgStreamPtr outStream,
        PkgAlgorithmContext &context);
    virtual int32_t Unpack(const PkgStreamPtr inStream, const PkgStreamPtr outStream,
        PkgAlgorithmContext &context);

    virtual void UpdateFileInfo(PkgManager::FileInfoPtr info) const
    {
        (void)info;
    }

    int32_t UnpackWithVerify(const PkgStreamPtr inStream, const PkgStreamPtr outStream,
        PkgAlgorithmContext &context, VerifyFunction verifier = nullptr);
protected:
    int32_t FinalDigest(DigestAlgorithm::DigestAlgorithmPtr algorithm,
        PkgAlgorithmContext &context, bool check) const;
    int32_t ReadData(const PkgStreamPtr inStream,
        size_t offset, PkgBuffer &buffer, size_t &remainSize, size_t &readLen) const;
};

class PkgAlgorithmFactory {
public:
    static PkgAlgorithm::PkgAlgorithmPtr GetAlgorithm(const PkgManager::FileInfoPtr config = nullptr);

    static DigestAlgorithm::DigestAlgorithmPtr GetDigestAlgorithm(uint8_t type);

    static SignAlgorithm::SignAlgorithmPtr GetSignAlgorithm(const std::string &path, uint8_t signMethod,
        uint8_t type);

    static SignAlgorithm::SignAlgorithmPtr GetVerifyAlgorithm(const std::string &path, uint8_t type);
};
} // namespace Hpackage
#endif
