#ifndef ENTT_STL_ITERATOR_HPP
#define ENTT_STL_ITERATOR_HPP

/*! @cond ENTT_INTERNAL */
#if __has_include(<entt/ext/stl/iterator.hpp>)
#    include <entt/ext/stl/iterator.hpp>
#else
#    include <iterator>
#    include <version>
#    include "../config/config.h"

namespace entt::stl {

using std::advance;
using std::bidirectional_iterator_tag;
using std::distance;
using std::forward_iterator_tag;
using std::input_iterator_tag;
using std::iterator_traits;
using std::make_reverse_iterator;
using std::random_access_iterator_tag;
using std::reverse_iterator;

#    ifndef ENTT_FORCE_STL
#        if defined(__cpp_lib_ranges)
#            define ENTT_HAS_ITERATOR_CONCEPTS

using std::bidirectional_iterator;
using std::forward_iterator;
using std::input_iterator;
using std::input_or_output_iterator;
using std::output_iterator;
using std::random_access_iterator;
using std::sentinel_for;

#        endif
#    endif

#    ifndef ENTT_HAS_ITERATOR_CONCEPTS
#        include <concepts>
#        include <utility>

namespace internal {

template<typename It>
concept has_iterator_category = requires {
    typename std::iterator_traits<It>::iterator_category;
};

template<typename It>
concept has_iterator_concept = has_iterator_category<It> && requires {
    typename It::iterator_concept;
};

template<has_iterator_category It>
struct iterator_tag {
    using type = typename std::iterator_traits<It>::iterator_category;
};

template<has_iterator_concept It>
struct iterator_tag<It> {
    using type = typename It::iterator_concept;
};

template<typename It, typename Tag>
concept has_iterator_tag = std::derived_from<typename iterator_tag<It>::type, Tag>;

} // namespace internal

// Bare minimum definitions to support broken platforms like PS4.
// EnTT does not provide full featured definitions for iterator concepts.

template<typename It>
concept input_or_output_iterator = requires(It it) {
    *it;
    { ++it } -> std::same_as<It &>;
    it++;
};

template<typename It>
concept input_iterator = input_or_output_iterator<It> && internal::has_iterator_tag<It, std::input_iterator_tag>;

template<typename It, typename Type>
concept output_iterator = input_or_output_iterator<It> && requires(It it, Type &&value) {
    *it++ = std::forward<Type>(value);
};

template<typename It>
concept forward_iterator = input_iterator<It> && internal::has_iterator_tag<It, std::forward_iterator_tag>;

template<typename It>
concept bidirectional_iterator = forward_iterator<It> && internal::has_iterator_tag<It, std::bidirectional_iterator_tag>;

template<typename It>
concept random_access_iterator = bidirectional_iterator<It> && internal::has_iterator_tag<It, std::random_access_iterator_tag>;

template<class Sentinel, typename It>
concept sentinel_for = input_or_output_iterator<It> && requires(Sentinel sentinel, It it) {
    { it == sentinel } -> std::same_as<bool>;
};

#    endif

} // namespace entt::stl
#endif
/*! @endcond */

#undef ENTT_HAS_ITERATOR_CONCEPTS

#endif
