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

#pragma once

#if __has_include(<ranges>)
#include <ranges>
#endif // __has_include(<ranges>)

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

//!\cond
#ifndef RANGES_DEEP_STL_INTEGRATION
    // including a header of the ranges-v3 library is fine as long as -DRANGES_DEEP_STL_INTEGRATION=1 is fulfilled
    // in range-v3 0.11.0 #include <range/v3/iterator/access.hpp> and #include <range/v3/iterator/traits.hpp> (that one
    // included range/v3/iterator/access.hpp) were the only header that used -DRANGES_DEEP_STL_INTEGRATION
    // We check range/v3/iterator/access.hpp and not e.g. range/v3/range_fwd.hpp, because we sometimes use utility that
    // include range-v3 header that doesn't needs the -DRANGES_DEEP_STL_INTEGRATION flag.
#   ifdef RANGES_V3_ITERATOR_ACCESS_HPP
#       pragma GCC warning "You included a range-v3 header before including `#include <seqan3/std/ranges>`, please be aware that seqan3 might fail to compile if `#define RANGES_DEEP_STL_INTEGRATION 1` isn't fulfilled."
#   endif // RANGES_V3_ITERATOR_ACCESS_HPP

#   define RANGES_DEEP_STL_INTEGRATION 1
#elif !RANGES_DEEP_STL_INTEGRATION
#   pragma GCC warning "Please be aware that seqan3 might fail to compile if `#define RANGES_DEEP_STL_INTEGRATION 1` isn't fulfilled."
#endif // RANGES_DEEP_STL_INTEGRATION
//!\endcond

#if defined(__cpp_lib_ranges) // C++20 ranges available
#include <seqan3/std/iterator>

#include <range/v3/range/concepts.hpp>

namespace ranges
{
//!\brief std::ranges::views are valid range-v3 views
template<::std::derived_from<::std::ranges::view_base> T>
inline constexpr bool enable_view<T> = true;

//!\cond
template<class T>
inline constexpr bool enable_view<::std::ranges::empty_view<T>> = true;
//!\endcond

// std::ranges::borrowed_range's are valid range-v3 borrowed_range's
//!\cond
template <::std::input_or_output_iterator I, ::std::sentinel_for<I> S, ::std::ranges::subrange_kind K>
inline constexpr bool enable_borrowed_range<::std::ranges::subrange<I, S, K>> = true;

template <class T>
inline constexpr bool enable_borrowed_range<::std::ranges::empty_view<T>> = true;

template <::std::weakly_incrementable W, ::std::semiregular Bound>
inline constexpr bool enable_borrowed_range<::std::ranges::iota_view<W, Bound>> = true;

template <class T>
inline constexpr bool enable_borrowed_range<::std::ranges::ref_view<T>> = true;

// Note: in gcc-10 enable_borrowed_range wasn't defined for the take_view and the other following views. (gcc-10.3 fixed this)
// Explictly defining this here will have the side-effect that we override the definition of
// std::ranges::enable_borrowed_range for all those views.
template <class T>
inline constexpr bool enable_borrowed_range<::std::ranges::take_view<T>> = enable_borrowed_range<T>;

template <class T>
inline constexpr bool enable_borrowed_range<::std::ranges::drop_view<T>> = enable_borrowed_range<T>;

template <class T, class Pred>
inline constexpr bool enable_borrowed_range<::std::ranges::drop_while_view<T, Pred>> = enable_borrowed_range<T>;

template <class T>
inline constexpr bool enable_borrowed_range<::std::ranges::common_view<T>> = enable_borrowed_range<T>;

template <class T>
inline constexpr bool enable_borrowed_range<::std::ranges::reverse_view<T>> = enable_borrowed_range<T>;

template <class T, size_t N>
inline constexpr bool enable_borrowed_range<::std::ranges::elements_view<T, N>> = enable_borrowed_range<T>;
//!\endcond

} // namespace ranges

namespace std::ranges
{
//!\brief range-v3 views are valid std::ranges::views
template<class T>
//!\cond
    requires ::std::derived_from<T, ::ranges::view_base>
//!\endcond
inline constexpr bool enable_view<T> = true;

//!\brief std::ranges::borrowed_range's are valid range-v3 borrowed_range's
template<class T>
//!\cond
    requires ::ranges::enable_borrowed_range<T>
//!\endcond
inline constexpr bool enable_borrowed_range<T> = true;
} // namespace std::ranges

#else // implement via range-v3

#include <range/v3/range/concepts.hpp>
#include <range/v3/iterator/insert_iterators.hpp>
#include <range/v3/view/all.hpp>
#include <range/v3/view/any_view.hpp>
#include <range/v3/view/common.hpp>
#include <range/v3/view/drop.hpp>
#include <range/v3/view/drop_while.hpp>
#include <range/v3/view/empty.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/iota.hpp>
#include <range/v3/view/istream.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/map.hpp>
#include <range/v3/view/reverse.hpp>
#include <range/v3/view/single.hpp>
#include <range/v3/view/split.hpp>
#include <range/v3/view/subrange.hpp>
#include <range/v3/view/take.hpp>
#include <range/v3/view/take_while.hpp>
#include <range/v3/view/transform.hpp>

#include <seqan3/std/concepts>
#include <seqan3/std/iterator>

// ============================================================================
//  namespace aliasing
// ============================================================================
namespace std::ranges
{

namespace
{
// https://eel.is/c++draft/ranges.syn

// [range.access], range access
using ::ranges::cpp20::begin;
using ::ranges::cpp20::end;
using ::ranges::cpp20::cbegin;
using ::ranges::cpp20::cend;
// using ::ranges::cpp20::rbegin;
// using ::ranges::cpp20::rend;
// using ::ranges::cpp20::crbegin;
// using ::ranges::cpp20::crend;
using ::ranges::cpp20::size;
// using ::ranges::cpp20::ssize;
using ::ranges::cpp20::empty;
using ::ranges::cpp20::data;
// using ::ranges::cpp20::cdata;

// [range.range], ranges
using ::ranges::cpp20::range;
// using ::ranges::cpp20::enable_borrowed_range;
using ::ranges::cpp20::borrowed_range;

using ::ranges::iterator_t;
using ::ranges::sentinel_t;
using ::ranges::range_difference_t;
using ::ranges::range_size_t;
using ::ranges::range_value_t;
using ::ranges::range_reference_t;
using ::ranges::range_rvalue_reference_t;

// [range.sized], sized ranges
// using ::ranges::cpp20::disable_sized_range;
using ::ranges::cpp20::sized_range;

// [range.view], views
using ::ranges::cpp20::enable_view;
using ::ranges::cpp20::view_base;
using ::ranges::cpp20::view;

// [range.refinements], other range refinements
using ::ranges::cpp20::output_range;
using ::ranges::cpp20::input_range;
using ::ranges::cpp20::forward_range;
using ::ranges::cpp20::bidirectional_range;
using ::ranges::cpp20::random_access_range;
using ::ranges::cpp20::contiguous_range;
using ::ranges::cpp20::common_range;
using ::ranges::cpp20::viewable_range;

// [view.interface], class template view_interface
using ::ranges::cpp20::view_interface;

// [range.utility]
using ::ranges::cpp20::subrange_kind;
using ::ranges::cpp20::subrange;

// [range.dangling], dangling iterator handling
// using ::ranges::cpp20::dangling;
// using ::ranges::cpp20::borrowed_iterator_t;
// using ::ranges::cpp20::borrowed_subrange_t;

// [range.empty], empty view
using ::ranges::cpp20::empty_view;
namespace views { template<class T> inline constexpr empty_view<T> empty{}; }

// [range.single], single view
// using ::ranges::cpp20::single_view;
namespace views { using ::ranges::cpp20::views::single; }

// [range.single], iota view
// using ::ranges::cpp20::iota_view;
namespace views { using ::ranges::cpp20::views::iota; }

// [range.istream], istream view
template <typename Val, class CharT, class Traits>
using basic_istream_view = ::ranges::istream_view<Val>;

// [range.all], all view
namespace views
{
using ::ranges::cpp20::views::all;
using ::ranges::cpp20::views::all_t;
} // namespace views
using ::ranges::cpp20::ref_view;

// [range.filter], filter view
// using ::ranges::cpp20::filter_view;
namespace views { using ::ranges::cpp20::views::filter; }

// [range.transform], transform view
// using ::ranges::cpp20::transform_view;
namespace views { using ::ranges::cpp20::views::transform; }

// [range.take], take view
// using ::ranges::cpp20::take_view;
namespace views { using ::ranges::cpp20::views::take; }

// [range.take.while], take while view
// using ::ranges::cpp20::take_while_view;
namespace views { using ::ranges::cpp20::views::take_while; }

// [range.drop], drop view
// using ::ranges::cpp20::drop_view;
namespace views { using ::ranges::cpp20::views::drop; }

// [range.drop.while], drop while view
// using ::ranges::cpp20::drop_while;
namespace views { using ::ranges::cpp20::views::drop_while; }

// [range.join], join view
// using ::ranges::cpp20::join_view;
namespace views { using ::ranges::cpp20::views::join; }

// [range.split], split view
// using ::ranges::cpp20::split_view;
namespace views { using ::ranges::cpp20::views::split; }

// [range.counted], counted view
// namespace views { using ::ranges::cpp20::views::counted; }

// [range.common], common view
// using ::ranges::cpp20::common_view;
namespace views { using ::ranges::cpp20::views::common; }

// [range.reverse], reverse view
// using ::ranges::cpp20::reverse_view;
namespace views { using ::ranges::cpp20::views::reverse; }

// [range.elements], elements view
// using ::ranges::cpp20::elements_view;
// using ::ranges::cpp20::keys_view;
// using ::ranges::cpp20::values_view;
namespace views
{
// using ::ranges::cpp20::views::elements;
using ::ranges::cpp20::views::keys;
using ::ranges::cpp20::views::values;
} // namespace views

} // anonymous namespace

} // namespace std::ranges

namespace std
{

namespace views = ::std::ranges::views;

} // namespace std::

#endif // standard header
