/*
 * Copyright (C) 2024 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 "compress.h"

#include <fstream>

namespace Hdc {
bool Compress::AddPath(std::string path)
{
    uv_fs_t req;
    int rc = uv_fs_lstat(nullptr, &req, path.c_str(), nullptr);
    uv_fs_req_cleanup(&req);
    if (rc != 0) {
        if (Base::GetCaller() == Base::Caller::CLIENT) {
            WRITE_LOG(LOG_FATAL, "%s is not exist", path.c_str());
        } else {
            WRITE_LOG(LOG_FATAL, "%s is not exist", Hdc::MaskString(path).c_str());
        }
        return false;
    }
    if (req.statbuf.st_mode & S_IFREG) {
        return AddEntry(path);
    }
    if (!AddEntry(path)) {
        if (Base::GetCaller() == Base::Caller::CLIENT) {
            WRITE_LOG(LOG_DEBUG, "AddEntry failed dir:%s", path.c_str());
        } else {
            WRITE_LOG(LOG_DEBUG, "AddEntry failed dir:%s", Hdc::MaskString(path).c_str());
        }
        return false;
    }
    rc = uv_fs_scandir(nullptr, &req, path.c_str(), 0, nullptr);
    if (rc < 0) {
        if (Base::GetCaller() == Base::Caller::CLIENT) {
            WRITE_LOG(LOG_DEBUG, "uv_fs_scandir failed dir:%s", path.c_str());
        } else {
            WRITE_LOG(LOG_DEBUG, "uv_fs_scandir failed dir:%s", Hdc::MaskString(path).c_str());
        }
        uv_fs_req_cleanup(&req);
        return false;
    }
    uv_dirent_t dent;
    while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
        if (strncmp(dent.name, ".", 1) == 0 || strncmp(dent.name, "..", 2) == 0) {
            continue;
        }
        string subpath = path + Base::GetPathSep() + dent.name;
        if (!AddPath(subpath)) {
            uv_fs_req_cleanup(&req);
            return false;
        }
    }
    uv_fs_req_cleanup(&req);
    return true;
}

bool Compress::AddEntry(std::string path)
{
    if (this->maxcount > 0 && this->entrys.size() > this->maxcount) {
        WRITE_LOG(LOG_FATAL, "Entry.size %zu exceeded maximum %zu", entrys.size(), maxcount);
        return false;
    }
    if (this->prefix.length() > 0 && path == this->prefix) {
        WRITE_LOG(LOG_DEBUG, "Ignoring compressed root directory");
        return true;
    }
    Entry entry(this->prefix, path);
    if (Base::GetCaller() == Base::Caller::CLIENT) {
        WRITE_LOG(LOG_DEBUG, "AddEntry %s", path.c_str());
    } else {
        WRITE_LOG(LOG_DEBUG, "AddEntry %s", Hdc::MaskString(path).c_str());
    }
    entrys.push_back(entry);
    return true;
}

bool Compress::SaveToFile(std::string localPath)
{
    if (localPath.length() <= 0) {
        localPath = "tmp.tar";
    }
    uv_fs_t req;
    int rc = uv_fs_lstat(nullptr, &req, localPath.c_str(), nullptr);
    if (rc == 0 && req.statbuf.st_mode & S_IFDIR) {
        return false;
    }

    std::ofstream file(localPath.c_str(), std::ios::out | std::ios::binary);
    if (!file.is_open()) {
        if (Base::GetCaller() == Base::Caller::CLIENT) {
            WRITE_LOG(LOG_FATAL, "SaveToFile open file %s failed", localPath.c_str());
        } else {
            WRITE_LOG(LOG_FATAL, "SaveToFile open file %s failed", Hdc::MaskString(localPath).c_str());
        }
        return false;
    }
    WRITE_LOG(LOG_DEBUG, "SaveToFile entrys len : %u", entrys.size());
    for (auto& entry : entrys) {
        entry.WriteToTar(file);
    }
    file.close();
    return true;
}

void Compress::UpdatePrefix(std::string pathPrefix)
{
    this->prefix = pathPrefix;
}

void Compress::UpdataMaxCount(size_t maxCount)
{
    this->maxcount = maxCount;
}
}