/*

 * 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 "zip_adapter.h"

#include <iostream>

#include <vector>

#include "zlib.h"



using namespace Hpackage;



namespace UpdatePatch {

ZipAdapter::ZipAdapter(UpdatePatchWriterPtr outStream, size_t offset, const PkgManager::FileInfoPtr fileInfo)

    : DeflateAdapter(), outStream_(outStream), offset_(offset)

{

    const Hpackage::ZipFileInfo *info = (const Hpackage::ZipFileInfo *)fileInfo;

    method_ = info->method;

    level_ = info->level;

    windowBits_ = info->windowBits;

    memLevel_ = info->memLevel;

    strategy_ = info->strategy;

}



int32_t ZipAdapter::Open()

{

    if (init_) {

        PATCH_LOGE("Has been open");

        return 0;

    }

    if (memset_s(&zstream_, sizeof(zstream_), 0, sizeof(z_stream)) != EOK) {

        PATCH_LOGE("Failed to memset stream");

        return -1;

    }

    PATCH_DEBUG("Open level_:%d method_:%d windowBits_:%d memLevel_:%d strategy_:%d",

        level_, method_, windowBits_, memLevel_, strategy_);

    int32_t ret = deflateInit2(&zstream_, level_, method_, windowBits_, memLevel_, strategy_);

    if (ret != Z_OK) {

        PATCH_LOGE("Failed to deflateInit2 ret %d", ret);

        return -1;

    }

    buffer_.resize(BUFFER_SIZE);

    init_ = true;

    return ret;

}



int32_t ZipAdapter::Close()

{

    if (!init_) {

        PATCH_LOGE("Has been close");

        return 0;

    }

    int32_t ret = deflateEnd(&zstream_);

    if (ret != Z_OK) {

        PATCH_LOGE("Failed to deflateEnd %d", ret);

        return -1;

    }

    init_ = false;

    return ret;

}



int32_t ZipAdapter::WriteData(const BlockBuffer &srcData)

{

    zstream_.next_in = srcData.buffer;

    zstream_.avail_in = static_cast<uint32_t>(srcData.length);

    zstream_.avail_out =  static_cast<uint32_t>(buffer_.size());

    zstream_.next_out = reinterpret_cast<unsigned char*>(buffer_.data());

    size_t deflateLen = 0;

    int32_t ret = Z_OK;

    do {

        ret = deflate(&zstream_, Z_NO_FLUSH);

        deflateLen = buffer_.size() -  zstream_.avail_out;

        if (deflateLen > 0) {

            ret = outStream_->Write(offset_, {buffer_.data(), deflateLen}, deflateLen);

            if (ret != 0) {

                PATCH_LOGE("Failed to deflate data");

                return -1;

            }

            offset_ += deflateLen;



            zstream_.next_out = reinterpret_cast<unsigned char*>(buffer_.data());

            zstream_.avail_out = buffer_.size();

        }

        if (zstream_.avail_in == 0) {

            break;

        }

    } while (ret == Z_OK);

    if (ret != Z_OK) {

        PATCH_LOGE("WriteData : Failed to write data ret %d", ret);

        return ret;

    }

    if (zstream_.avail_in != 0) {

        PATCH_LOGE("WriteData : Failed to write data");

        return ret;

    }

    return ret;

}



int32_t ZipAdapter::FlushData(size_t &offset)

{

    zstream_.next_in = nullptr;

    zstream_.avail_in = 0;

    zstream_.avail_out =  buffer_.size();

    zstream_.next_out = reinterpret_cast<unsigned char*>(buffer_.data());

    size_t deflateLen = 0;

    int32_t ret = Z_OK;

    do {

        ret = deflate(&zstream_, Z_FINISH);

        deflateLen = buffer_.size() -  zstream_.avail_out;

        if (deflateLen > 0) {

            ret = outStream_->Write(offset_, {buffer_.data(), deflateLen}, deflateLen);

            if (ret != 0) {

                PATCH_LOGE("Failed to deflate data");

                return 1;

            }

            offset_ += deflateLen;



            zstream_.next_out = reinterpret_cast<unsigned char*>(buffer_.data());

            zstream_.avail_out = buffer_.size();

        }

        if (ret == Z_STREAM_END) {

            ret = Z_OK;

            break;

        }

    } while (ret == Z_OK);

    if (ret != Z_OK) {

        PATCH_LOGE("FlushData : Failed to write data ret %d", ret);

        return ret;

    }

    if (zstream_.avail_in != 0) {

        PATCH_LOGE("FlushData : Failed to write data");

        return ret;

    }

    offset = offset_;

    return ret;

}

} // namespace UpdatePatch