// Copyright 2018 Bloomberg Finance L.P
//
// 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 <buildboxcommon_temporarydirectory.h>

#include <buildboxcommon_exception.h>
#include <buildboxcommon_fileutils.h>

#include <cstring>
#include <stdexcept>
#include <string>
#include <system_error>

namespace buildboxcommon {

namespace {

const char *constructTmpDirPath(const char *path)
{
    if (path == nullptr) {
        BUILDBOXCOMMON_THROW_EXCEPTION(
            std::runtime_error,
            "NULL path was passed into TemporaryDirectory");
    }
    if (strlen(path) == 0) {
        const char *tmpdir = getenv("TMPDIR");
        if (tmpdir == nullptr || strlen(tmpdir) == 0) {
            tmpdir = TempDefaults::DEFAULT_TMP_DIR;
        }

        return tmpdir;
    }
    else {
        return path;
    }
}

} // unnamed namespace

TemporaryDirectory::TemporaryDirectory(const char *prefix)
    : TemporaryDirectory("", prefix)
{
}

TemporaryDirectory::TemporaryDirectory(const char *path, const char *prefix)
    : d_name(create(constructTmpDirPath(path), prefix)),
      d_fd(open(d_name.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC))
{
    if (d_fd < 0) {
        BUILDBOXCOMMON_THROW_SYSTEM_EXCEPTION(
            std::system_error, errno, std::generic_category,
            "Failed to open temporary directory");
    }
}

std::string TemporaryDirectory::create(const char *path, const char *prefix)
{
    std::string name =
        std::string(path) + "/" + std::string(prefix) + "XXXXXX";

    // Normalize the path.
    name = FileUtils::normalizePath(name.c_str());

    if (mkdtemp(&name[0]) == nullptr) {
        BUILDBOXCOMMON_THROW_SYSTEM_EXCEPTION(std::system_error, errno,
                                              std::system_category,
                                              "Error in mkdtemp");
    }

    return name;
}

void TemporaryDirectory::setAutoRemove(bool auto_remove)
{
    d_auto_remove = auto_remove;
}

TemporaryDirectory::~TemporaryDirectory()
{
    if (this->d_fd >= 0) {
        ::close(this->d_fd);
    }
    if (d_auto_remove) {
        FileUtils::deleteDirectory(this->d_name.c_str());
    }
}

void TemporaryDirectory::close()
{
    ::close(this->d_fd);
    this->d_fd = -1;
}

// Move constructor
TemporaryDirectory::TemporaryDirectory(TemporaryDirectory &&other) noexcept
    : d_name(std::move(other.d_name)), d_fd(other.d_fd),
      d_auto_remove(other.d_auto_remove)
{
    other.d_fd = -1;
    other.d_auto_remove = false;
}

// Move assignment operator
TemporaryDirectory &
TemporaryDirectory::operator=(TemporaryDirectory &&other) noexcept
{
    if (this != &other) {
        if (this->d_fd >= 0) {
            ::close(this->d_fd);
        }
        if (d_auto_remove && !d_name.empty()) {
            ::rmdir(d_name.c_str());
        }
        d_name = std::move(other.d_name);
        d_fd = other.d_fd;
        d_auto_remove = other.d_auto_remove;
        other.d_fd = -1;
        other.d_auto_remove = false;
    }
    return *this;
}

} // namespace buildboxcommon
