// -*- C++ -*-
// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2020, 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 Adaptations of concepts from the Ranges TS
 * \author Hannes Hauswedell <hannes.hauswedell AT fu-berlin.de>
 */

#pragma once

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

#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<class T>
//!\cond
    requires ::std::derived_from<T, ::std::ranges::view_base>
//!\endcond
inline constexpr bool enable_view<T> = true;

// 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;
//!\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

//!\cond
#ifndef RANGES_DEEP_STL_INTEGRATION
#define RANGES_DEEP_STL_INTEGRATION 1
#endif
//!\endcond

#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
// ============================================================================

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

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
