// -*- 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 [\<type_traits\> header](https://en.cppreference.com/w/cpp/header/type_traits) from C++20's standard
 *        library.
 * \author Hannes Hauswedell <hannes.hauswedell AT fu-berlin.de>
 */

#pragma once

#include <type_traits>

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

/*!\brief A workaround for __cpp_lib_remove_cvref for gcc version >=9.0 and <9.4 (in C++17 mode).
 *        Those versions implemented std::remove_cvref_t, but did not define that feature detection macro.
 * \ingroup std_type_traits
 */
#ifdef SEQAN3_DOXYGEN_ONLY // needed as seqan3/core/platform.hpp might not be included
#if SEQAN3_DOXYGEN_ONLY(1)0
// documentation-only
#define SEQAN3_CPP_LIB_REMOVE_CVREF IMPLEMENTATION_DEFINED
#undef SEQAN3_CPP_LIB_REMOVE_CVREF
#endif // SEQAN3_DOXYGEN_ONLY(1)0
#endif // SEQAN3_DOXYGEN_ONLY

#ifndef SEQAN3_CPP_LIB_REMOVE_CVREF
#   if defined(__cpp_lib_remove_cvref)
#       define SEQAN3_CPP_LIB_REMOVE_CVREF 1
#   elif defined(__GNUC__) && ((__GNUC__ == 9) && (__GNUC_MINOR__ < 4)) && __cplusplus > 201703L
#       define SEQAN3_CPP_LIB_REMOVE_CVREF 1
#   endif
#endif

namespace std
{

// ----------------------------------------------------------------------------
// remove_cvref_t
// ----------------------------------------------------------------------------
#ifndef SEQAN3_CPP_LIB_REMOVE_CVREF
/*!\brief Return the input type with `const`, `volatile` and references removed.
 * \tparam t The type to operate on.
 * \ingroup std_type_traits
 */
template <typename t>
struct remove_cvref;

//!\cond
template <typename t>
    requires true
struct remove_cvref<t> // needed for gcc-9, it defines std::remove_cvref but does not populate __cpp_lib_remove_cvref
{
    using type = std::remove_cv_t<std::remove_reference_t<t>>;
};
//!\endcond

/*!\brief Return the input type with `const`, `volatile` and references removed (transformation_trait shortcut).
 * \tparam t The type to operate on.
 * \ingroup std_type_traits
 */
template <typename t>
using remove_cvref_t = typename remove_cvref<t>::type;
#endif // SEQAN3_CPP_LIB_REMOVE_CVREF

// ----------------------------------------------------------------------------
// type_identity
// ----------------------------------------------------------------------------
#ifndef __cpp_lib_type_identity

/*!\brief The identity transformation (a transformation_trait that returns the input).
 * \tparam t The type to operate on.
 * \see https://en.cppreference.com/w/cpp/types/type_identity
 * \ingroup std_type_traits
 */
template <typename t>
struct type_identity;

//!\cond
template <typename t>
    requires true
struct type_identity<t>
{
    //!\brief The return type (which is the argument).
    using type = t;
};
//!\endcond

/*!\brief A shortcut for std::type_identity.
 * \tparam t The type to operate on.
 * \ingroup std_type_traits
 */
template <typename t>
using type_identity_t = typename type_identity<t>::type;
#endif // __cpp_lib_type_identity

} // namespace std
