// -*- C++ -*-
// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
// -----------------------------------------------------------------------------------------------------

/*!\file
 * \brief The [\<filesystem\> header](https://en.cppreference.com/w/cpp/header/filesystem) from C++17's standard
 *        library.
 * \author Vitor C. Piro <pirov AT zedat.fu-berlin.de>
 * \author Enrico Seiler <enricoseiler AT fu-berlin.de>
 */

#pragma once

#if __has_include(<filesystem>)
#include <filesystem>
#else
#include <experimental/filesystem>

/*!\defgroup std_filesystem filesystem
 * \ingroup std
 * \brief The [\<filesystem\> header](https://en.cppreference.com/w/cpp/header/filesystem) from C++17's standard
 *        library.
 */

//!\cond
namespace std::experimental::filesystem
{

enum class perm_options : uint32_t
{
    replace = 1u,
    add = 1u<<1,
    remove = 1u<<2,
    nofollow = 1u<<3
};

constexpr perm_options operator& (perm_options lhs, perm_options rhs) noexcept
{
    using underlying_t = std::underlying_type_t<perm_options>;
    return static_cast<perm_options>(static_cast<underlying_t>(lhs) & static_cast<underlying_t>(rhs));
}

constexpr perm_options operator| (perm_options lhs, perm_options rhs) noexcept
{
    using underlying_t = std::underlying_type_t<perm_options>;
    return static_cast<perm_options>(static_cast<underlying_t>(lhs) | static_cast<underlying_t>(rhs));
}

constexpr perm_options operator^ (perm_options lhs, perm_options rhs) noexcept
{
    using underlying_t = std::underlying_type_t<perm_options>;
    return static_cast<perm_options>(static_cast<underlying_t>(lhs) ^ static_cast<underlying_t>(rhs));
}

constexpr perm_options operator~ (perm_options lhs) noexcept
{
    using underlying_t = std::underlying_type_t<perm_options>;
    return static_cast<perm_options>(~static_cast<underlying_t>(lhs));
}

constexpr perm_options & operator&= (perm_options & lhs, perm_options rhs) noexcept
{
    lhs = lhs & rhs;
    return lhs;
}

constexpr perm_options & operator|= (perm_options & lhs, perm_options rhs) noexcept
{
    lhs = lhs | rhs;
    return lhs;
}

constexpr perm_options & operator^= (perm_options & lhs, perm_options rhs) noexcept
{
    lhs = lhs ^ rhs;
    return lhs;
}

inline void permissions(path const & p,
                        perms prms,
                        perm_options opts,
                        error_code & ec)
{
    auto follow_symlinks = (static_cast<bool>(opts & perm_options::nofollow)) ? perms::symlink_nofollow : perms::none;

    switch (opts)
    {
        case perm_options::replace :
            permissions(p, perms::remove_perms | follow_symlinks | perms::all, ec);
            permissions(p, perms::add_perms | follow_symlinks | prms, ec);
            break;
        case perm_options::add :
            permissions(p, perms::add_perms | follow_symlinks | prms, ec);
            break;
        case perm_options::remove :
            permissions(p, perms::remove_perms | follow_symlinks | prms, ec);
            break;
        default :
            break; // Actually UB
    }
}

inline void permissions(path const & p,
                        perms prms,
                        perm_options opts)
{
    error_code ec;
    permissions(p, prms, opts, ec);
    if (ec.value())
        throw filesystem_error("Cannot set permissions", p, ec);
}

} // namespace std::experimental::filesystem

namespace std
{

namespace filesystem = std::experimental::filesystem;

} // namespace std

#endif // __has_include(experimental/filesystem)
//!\endcond
