// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___FLAT_MAP_KEY_VALUE_ITERATOR_H
#define _LIBCPP___FLAT_MAP_KEY_VALUE_ITERATOR_H

#include <__compare/three_way_comparable.h>
#include <__concepts/convertible_to.h>
#include <__config>
#include <__iterator/iterator_traits.h>
#include <__memory/addressof.h>
#include <__ranges/access.h>
#include <__type_traits/conditional.h>
#include <__type_traits/maybe_const.h>
#include <__utility/move.h>
#include <__utility/pair.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#  pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

#if _LIBCPP_STD_VER >= 23

_LIBCPP_BEGIN_NAMESPACE_STD

/**
 * __key_value_iterator is a proxy iterator which zips the underlying
 * _KeyContainer::iterator and the underlying _MappedContainer::iterator.
 * The two underlying iterators will be incremented/decremented together.
 * And the reference is a pair of the const key reference and the value reference.
 */
template <class _Owner, class _KeyContainer, class _MappedContainer, bool _Const>
struct __key_value_iterator {
private:
  using __key_iterator    = ranges::iterator_t<const _KeyContainer>;
  using __mapped_iterator = ranges::iterator_t<__maybe_const<_Const, _MappedContainer>>;
  using __reference       = _If<_Const, typename _Owner::const_reference, typename _Owner::reference>;

  struct __arrow_proxy {
    __reference __ref_;
    _LIBCPP_HIDE_FROM_ABI __reference* operator->() { return std::addressof(__ref_); }
  };

  __key_iterator __key_iter_;
  __mapped_iterator __mapped_iter_;

  friend _Owner;

  template <class, class, class, bool>
  friend struct __key_value_iterator;

public:
  using iterator_concept = random_access_iterator_tag;
  // `__key_value_iterator` only satisfy "Cpp17InputIterator" named requirements, because
  // its `reference` is not a reference type.
  // However, to avoid surprising runtime behaviour when it is used with the
  // Cpp17 algorithms or operations, iterator_category is set to random_access_iterator_tag.
  using iterator_category = random_access_iterator_tag;
  using value_type        = typename _Owner::value_type;
  using difference_type   = typename _Owner::difference_type;

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator() = default;

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator(__key_value_iterator<_Owner, _KeyContainer, _MappedContainer, !_Const> __i)
    requires _Const && convertible_to<ranges::iterator_t<_KeyContainer>, __key_iterator> &&
                 convertible_to<ranges::iterator_t<_MappedContainer>, __mapped_iterator>
      : __key_iter_(std::move(__i.__key_iter_)), __mapped_iter_(std::move(__i.__mapped_iter_)) {}

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator(__key_iterator __key_iter, __mapped_iterator __mapped_iter)
      : __key_iter_(std::move(__key_iter)), __mapped_iter_(std::move(__mapped_iter)) {}

  _LIBCPP_HIDE_FROM_ABI __reference operator*() const { return __reference(*__key_iter_, *__mapped_iter_); }
  _LIBCPP_HIDE_FROM_ABI __arrow_proxy operator->() const { return __arrow_proxy{**this}; }

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator++() {
    ++__key_iter_;
    ++__mapped_iter_;
    return *this;
  }

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator operator++(int) {
    __key_value_iterator __tmp(*this);
    ++*this;
    return __tmp;
  }

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator--() {
    --__key_iter_;
    --__mapped_iter_;
    return *this;
  }

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator operator--(int) {
    __key_value_iterator __tmp(*this);
    --*this;
    return __tmp;
  }

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator+=(difference_type __x) {
    __key_iter_ += __x;
    __mapped_iter_ += __x;
    return *this;
  }

  _LIBCPP_HIDE_FROM_ABI __key_value_iterator& operator-=(difference_type __x) {
    __key_iter_ -= __x;
    __mapped_iter_ -= __x;
    return *this;
  }

  _LIBCPP_HIDE_FROM_ABI __reference operator[](difference_type __n) const { return *(*this + __n); }

  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
  operator==(const __key_value_iterator& __x, const __key_value_iterator& __y) {
    return __x.__key_iter_ == __y.__key_iter_;
  }

  _LIBCPP_HIDE_FROM_ABI friend bool operator<(const __key_value_iterator& __x, const __key_value_iterator& __y) {
    return __x.__key_iter_ < __y.__key_iter_;
  }

  _LIBCPP_HIDE_FROM_ABI friend bool operator>(const __key_value_iterator& __x, const __key_value_iterator& __y) {
    return __y < __x;
  }

  _LIBCPP_HIDE_FROM_ABI friend bool operator<=(const __key_value_iterator& __x, const __key_value_iterator& __y) {
    return !(__y < __x);
  }

  _LIBCPP_HIDE_FROM_ABI friend bool operator>=(const __key_value_iterator& __x, const __key_value_iterator& __y) {
    return !(__x < __y);
  }

  _LIBCPP_HIDE_FROM_ABI friend auto operator<=>(const __key_value_iterator& __x, const __key_value_iterator& __y)
    requires three_way_comparable<__key_iterator>
  {
    return __x.__key_iter_ <=> __y.__key_iter_;
  }

  _LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator+(const __key_value_iterator& __i, difference_type __n) {
    auto __tmp = __i;
    __tmp += __n;
    return __tmp;
  }

  _LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator+(difference_type __n, const __key_value_iterator& __i) {
    return __i + __n;
  }

  _LIBCPP_HIDE_FROM_ABI friend __key_value_iterator operator-(const __key_value_iterator& __i, difference_type __n) {
    auto __tmp = __i;
    __tmp -= __n;
    return __tmp;
  }

  _LIBCPP_HIDE_FROM_ABI friend difference_type
  operator-(const __key_value_iterator& __x, const __key_value_iterator& __y) {
    return difference_type(__x.__key_iter_ - __y.__key_iter_);
  }
};

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP_STD_VER >= 23

_LIBCPP_POP_MACROS

#endif // _LIBCPP___FLAT_MAP_KEY_VALUE_ITERATOR_H
