// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2019, 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 Provides C++20 additions to the \<iterator\> header.
 * \author Hannes Hauswedell <hannes.hauswedell AT fu-berlin.de>
 */

#pragma once

#include <iterator>

#ifndef __cpp_lib_ranges

#include <range/v3/iterator/access.hpp>
#include <range/v3/iterator/default_sentinel.hpp>
#include <range/v3/iterator/insert_iterators.hpp>
#include <range/v3/iterator/operations.hpp>
#include <range/v3/iterator/stream_iterators.hpp>

#include <seqan3/std/concepts>

/*!\defgroup iterator iterator
 * \ingroup std
 * \brief The \<iterator\> header from C++20's standard library.
 */

namespace std
{
//!\addtogroup iterator
//!\{

/*!\brief A type trait class that provides uniform interface to the properties of types that model the
 *        WeaklyIncrementable concept.
 * \tparam t The type to query.
 * \sa https://en.cppreference.com/w/cpp/iterator/incrementable_traits
 */
template <typename t>
struct incrementable_traits
{};

//!\cond
template <typename t>
    requires std::is_object_v<t>
struct incrementable_traits<t*>
{
    using difference_type = ptrdiff_t;
};

template <typename t>
struct incrementable_traits<const t> : incrementable_traits<t>
{};

template <typename t>
    requires requires { typename t::difference_type; }
struct incrementable_traits<t>
{
    using difference_type = typename t::difference_type;
};

template <typename t>
    requires (!requires { typename t::difference_type; } &&
              requires(t const & a, t const & b) { requires Integral<decltype(a - b)>; })
struct incrementable_traits<t>
{
    using difference_type = std::make_signed_t<decltype(declval<t>() - declval<t>())>;
};
//!\endcond

//!\cond
namespace detail
{
// The type does not support incrementable concepts.
template <typename t>
struct incrementable_traits_or_iterator_traits
{};

// Use incrementable_traits<t>::difference_type if it is defined.
template <typename t>
    requires requires { typename incrementable_traits<t>::difference_type; }
struct incrementable_traits_or_iterator_traits<t>
{
    using difference_type = typename incrementable_traits<t>::difference_type;
};

// Fall back to iterstor_traits if incrementable_traits<t>::difference_type is not defined.
template <typename t>
    requires (!requires { typename incrementable_traits<t>::difference_type; } &&
               requires { typename iterator_traits<t>::difference_type; })
struct incrementable_traits_or_iterator_traits<t>
{
    using difference_type = typename iterator_traits<t>::difference_type;
};
}
//!\endcond

/*!\brief Defines the incrementable type's difference type
 * \tparam t The type to get the difference type for.
 * \sa https://en.cppreference.com/w/cpp/iterator/iter_t
 */
template <typename t>
using iter_difference_t = typename detail::incrementable_traits_or_iterator_traits<t>::difference_type;

// ------------------------------------------------------------------
// auxiliary functions
// ------------------------------------------------------------------

/*!\brief Create a std::back_insert_iterator for the argument.
 * \tparam container_t  Type of the parameter; must have a `push_back()` member function.
 * \param container     The container on which to create the iterator.
 * \returns The respective back insert iterator.
 *
 * \details
 *
 * This function delegates to ranges::back_inserter from range-v3; it is more constrained than a possibly
 * outdated one from the standard library.
 */
template <typename container_t>
//!\cond
    requires requires (container_t & v) { { v.push_back(*v.begin()) }; }
//!\endcond
constexpr auto back_inserter(container_t & container)
{
    return ::ranges::back_inserter(container);
}

// ------------------------------------------------------------------
// Concepts
// ------------------------------------------------------------------

/*!\interface std::Readable <>
 * \brief The concept Readable is satisfied by types that are readable by applying operator*, such as pointers, smart
 *        pointers, and iterators.
 * \sa https://en.cppreference.com/w/cpp/iterator/Readable
 */
//!\cond
template <typename t>
SEQAN3_CONCEPT Readable = ::ranges::Readable<t>;

//!\endcond

/*!\interface std::Writable <>
 * \brief The concept `Writable<Out, T>` specifies the requirements for writing a value whose type and value category
 *        are encoded by T into an iterator Out's referenced object.
 * \sa https://en.cppreference.com/w/cpp/iterator/Writable
 */
//!\cond
template <typename out, typename t>
SEQAN3_CONCEPT Writable = ::ranges::Writable<out, t>;
//!\endcond

/*!\interface std::WeaklyIncrementable <>
 * \extends std::Semiregular
 * \brief The concept WeaklyIncrementable specifies the requirements on a type that can be incremented (with the
 *        pre- and post-increment operators). The increment operations need not be equality-preserving, and the type
 *        need not be std::EqualityComparable.
 * \sa https://en.cppreference.com/w/cpp/iterator/WeaklyIncrementable
 */
//!\cond
template <typename t>
SEQAN3_CONCEPT WeaklyIncrementable = Semiregular<t> &&
                                   requires (t v)
{
    typename iter_difference_t<std::remove_reference_t<t>>;
    requires SignedIntegral<iter_difference_t<std::remove_reference_t<t>>>;
    requires Same<decltype(++v), t &>; /* not required to be equality preserving */
    v++; /* not required to be equality preserving */
};
//!\endcond

/*!\interface std::Incrementable <>
 * \extends std::WeaklyIncrementable
 * \extends std::Regular
 * \brief The concept Incrementable specifies the requirements on a type that can be incremented (with the pre-
 *        and post-increment operators). The increment operations (including those required by std::WeaklyIncrementable)
 *        are required to be equality-preserving, and the type is required to be std::EqualityComparable.
 * \sa https://en.cppreference.com/w/cpp/iterator/Incrementable
 */
//!\cond
template <typename i>
SEQAN3_CONCEPT Incrementable = Regular<i> && WeaklyIncrementable<i> && ::ranges::Incrementable<i>;
//!\endcond

/*!\interface std::Iterator <>
 * \extends std::WeaklyIncrementable
 * \brief The Iterator concept forms the basis of the iterator concept taxonomy; every iterator satisfies the Iterator
 *        requirements.
 * \sa https://en.cppreference.com/w/cpp/iterator/Iterator
 */
//!\cond
template <typename i>
SEQAN3_CONCEPT Iterator = WeaklyIncrementable<i> && ::ranges::Iterator<i>;
//!\endcond

/*!\interface std::Sentinel <>
 * \extends std::Semiregular
 * \extends std::Iterator
 * \brief The Sentinel concept specifies the relationship between an std::Iterator type and a std::Semiregular type
 *        whose values denote a range.
 * \sa https://en.cppreference.com/w/cpp/iterator/Sentinel
 */
//!\cond
template <typename s, typename i>
SEQAN3_CONCEPT Sentinel = Semiregular<s> && Iterator<i> && ::ranges::Sentinel<s, i>;
//!\endcond

/*!\interface std::SizedSentinel <>
 * \extends std::Sentinel
 * \brief The SizedSentinel concept specifies that an object of the iterator type I and an object of the
 *        sentinel type S can be subtracted to compute the distance between them in constant time.
 * \sa https://en.cppreference.com/w/cpp/iterator/SizedSentinel
 */
//!\cond
template <typename s, typename i>
SEQAN3_CONCEPT SizedSentinel = Sentinel<s, i> && ::ranges::SizedSentinel<s, i>;
//!\endcond

/*!\interface std::OutputIterator <>
 * \extends std::Iterator
 * \extends std::Writable
 * \brief The OutputIterator concept is a refinement of std::Iterator, adding the requirement that it can be used
 *        to write values of values of type and value category encoded by T (via std::Writable).
 *        std::EqualityComparable is not required.
 * \sa https://en.cppreference.com/w/cpp/iterator/OutputIterator
 */
//!\cond
template <typename out, typename t>
SEQAN3_CONCEPT OutputIterator = Iterator<out> && Writable<out, t> && ::ranges::OutputIterator<out, t>;
//!\endcond

/*!\interface std::InputIterator <>
 * \extends std::Iterator
 * \extends std::Readable
 * \brief The InputIterator concept is a refinement of std::Iterator, adding the requirement that the referenced
 *        values can be read (via std::Readable) and the requirement that the iterator category tag be present.
 * \sa https://en.cppreference.com/w/cpp/iterator/InputIterator
 */
//!\cond
template <typename i>
SEQAN3_CONCEPT InputIterator = Iterator<i> && Readable<i> && ::ranges::InputIterator<i>;
//!\endcond

/*!\interface std::ForwardIterator <>
 * \extends std::InputIterator
 * \extends std::Incrementable
 * \extends std::Sentinel
 * \brief The InputIterator concept is a refinement of std::Iterator, adding the requirement that the referenced
 *        values can be read (via std::Readable) and the requirement that the iterator category tag be present.
 * \sa https://en.cppreference.com/w/cpp/iterator/ForwardIterator
 */
//!\cond
template <typename i>
SEQAN3_CONCEPT ForwardIterator = InputIterator<i> &&
                               Incrementable<i> &&
                               Sentinel<i, i> &&
                               ::ranges::ForwardIterator<i>;
//!\endcond

/*!\interface std::BidirectionalIterator <>
 * \extends std::ForwardIterator
 * \brief The concept BidirectionalIterator refines std::ForwardIterator by adding the ability to move an iterator
 *        backward.
 * \sa https://en.cppreference.com/w/cpp/iterator/BidirectionalIterator
 */
//!\cond
template <typename i>
SEQAN3_CONCEPT BidirectionalIterator = ForwardIterator<i> && ::ranges::BidirectionalIterator<i>;
//!\endcond

/*!\interface std::RandomAccessIterator <>
 * \extends std::BidirectionalIterator
 * \extends std::SizedSentinel
 * \extends std::StrictTotallyOrdered
 * \brief The concept RandomAccessIterator refines std::BidirectionalIterator by adding support for constant
 *        time advancement with the +=, +, -=, and - operators, constant time computation of distance with -,
 *        and array notation with subscripting.
 * \sa https://en.cppreference.com/w/cpp/iterator/BidirectionalIterator
 */
//!\cond
template <typename i>
SEQAN3_CONCEPT RandomAccessIterator = BidirectionalIterator<i> &&
                                    StrictTotallyOrdered<i> &&
                                    SizedSentinel<i, i> &&
                                    ::ranges::RandomAccessIterator<i>;
//!\endcond

/*!\interface std::ContiguousIterator <>
 * \extends std::RandomAccessIterator
 * \brief The concept ContiguousIterator refines std::RandomAccessIterator by requiring that for every iterator
 *        `i` and distance `n` where `(a + n)` is a valid iterator `*(a + n)` is equivalent to
 *        `*(std::addressof(*a) + n)`.
 * \sa https://en.cppreference.com/w/cpp/iterator/ContiguousIterator
 */
//!\cond
template <typename i>
SEQAN3_CONCEPT ContiguousIterator = RandomAccessIterator<i> &&
                                    ::ranges::ContiguousIterator<i>;
//!\endcond

//!\} // Iterator Concepts.

}  // namespace std

namespace std::ranges
{
//!\addtogroup iterator
//!\{

/*!\typedef std::ranges::advance
* \brief Alias for ranges::advance. Advances the iterator by the given distance.
*/
using SEQAN3_DOXYGEN_ONLY(advance =) ::ranges::advance;

/*!\typedef std::ranges::distance
* \brief Alias for ranges::distance. Returns the number of hops from first to last.
*/
using SEQAN3_DOXYGEN_ONLY(distance =) ::ranges::distance;

/*!\typedef std::ranges::prev
* \brief Alias for ranges::prev. Returns the nth predecessor of the given iterator.
*/
using SEQAN3_DOXYGEN_ONLY(prev =)::ranges::prev;

/*!\typedef std::ranges::next
* \brief Alias for ranges::next. Returns the nth successor of the given iterator.
*/
using SEQAN3_DOXYGEN_ONLY(next =) ::ranges::next;

/*!\typedef std::ranges::iter_move
* \brief Alias for ranges::iter_move. Casts the result of dereferencing an object to its associated rvalue reference type.
*/
using SEQAN3_DOXYGEN_ONLY(iter_move =) ::ranges::iter_move;

/*!\typedef std::ranges::iter_swap
* \brief Alias for ranges::iter_swap. Exchanges the values denoted by its arguments.
*/
using SEQAN3_DOXYGEN_ONLY(iter_swap =) ::ranges::iter_swap;

/*!\typedef std::ranges::default_sentinel
* \brief Alias for ranges::default_sentinel. Empty sentinel object for use with iterators that know the bound of their range.
*/
using SEQAN3_DOXYGEN_ONLY(default_sentinel =) ::ranges::default_sentinel;

/*!\typedef std::ranges::default_sentinel_t
* \brief Alias for ranges::default_sentinel_t. Type of ranges::default_sentinel.
*/
using SEQAN3_DOXYGEN_ONLY(default_sentinel_t =) ::ranges::default_sentinel_t;

//!\}
} // namespace std::ranges

#endif // C++17
