// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_VALUE_ITERATORS_H_
#define BASE_VALUE_ITERATORS_H_

#include <memory>
#include <string>
#include <utility>

#include "base/containers/flat_map.h"

namespace base {

class Value;

namespace detail {

using DictStorage = base::flat_map<std::string, std::unique_ptr<Value>>;

// This iterator closely resembles DictStorage::iterator, with one
// important exception. It abstracts the underlying unique_ptr away, meaning its
// value_type is std::pair<const std::string, Value>. It's reference type is a
// std::pair<const std::string&, Value&>, so that callers have read-write
// access without incurring a copy.
class dict_iterator {
 public:
  using difference_type = DictStorage::iterator::difference_type;
  using value_type = std::pair<const std::string, Value>;
  using reference = std::pair<const std::string&, Value&>;
  using iterator_category = std::bidirectional_iterator_tag;

  class pointer {
   public:
    explicit pointer(const reference& ref);
    pointer(const pointer& ptr);
    pointer& operator=(const pointer& ptr) = delete;

    reference* operator->() { return &ref_; }

   private:
    reference ref_;
  };

  explicit dict_iterator(DictStorage::iterator dict_iter);
  dict_iterator(const dict_iterator& dict_iter);
  dict_iterator& operator=(const dict_iterator& dict_iter);
  ~dict_iterator();

  reference operator*();
  pointer operator->();

  dict_iterator& operator++();
  dict_iterator operator++(int);
  dict_iterator& operator--();
  dict_iterator operator--(int);

  friend bool operator==(const dict_iterator& lhs, const dict_iterator& rhs);
  friend bool operator!=(const dict_iterator& lhs, const dict_iterator& rhs);

 private:
  DictStorage::iterator dict_iter_;
};

// This iterator closely resembles DictStorage::const_iterator, with one
// important exception. It abstracts the underlying unique_ptr away, meaning its
// value_type is std::pair<const std::string, Value>. It's reference type is a
// std::pair<const std::string&, const Value&>, so that callers have read-only
// access without incurring a copy.
class const_dict_iterator {
 public:
  using difference_type = DictStorage::const_iterator::difference_type;
  using value_type = std::pair<const std::string, Value>;
  using reference = std::pair<const std::string&, const Value&>;
  using iterator_category = std::bidirectional_iterator_tag;

  class pointer {
   public:
    explicit pointer(const reference& ref);
    pointer(const pointer& ptr);
    pointer& operator=(const pointer& ptr) = delete;

    const reference* operator->() const { return &ref_; }

   private:
    const reference ref_;
  };

  explicit const_dict_iterator(DictStorage::const_iterator dict_iter);
  const_dict_iterator(const const_dict_iterator& dict_iter);
  const_dict_iterator& operator=(const const_dict_iterator& dict_iter);
  ~const_dict_iterator();

  reference operator*() const;
  pointer operator->() const;

  const_dict_iterator& operator++();
  const_dict_iterator operator++(int);
  const_dict_iterator& operator--();
  const_dict_iterator operator--(int);

  friend bool operator==(const const_dict_iterator& lhs,
                         const const_dict_iterator& rhs);
  friend bool operator!=(const const_dict_iterator& lhs,
                         const const_dict_iterator& rhs);

 private:
  DictStorage::const_iterator dict_iter_;
};

// This class wraps the various |begin| and |end| methods of the underlying
// DictStorage in dict_iterators and const_dict_iterators. This allows callers
// to use this class for easy iteration over the underlying values, granting
// them either read-only or read-write access, depending on the
// const-qualification.
class dict_iterator_proxy {
 public:
  using key_type = DictStorage::key_type;
  using mapped_type = DictStorage::mapped_type::element_type;
  using value_type = std::pair<key_type, mapped_type>;
  using key_compare = DictStorage::key_compare;
  using size_type = DictStorage::size_type;
  using difference_type = DictStorage::difference_type;

  using iterator = dict_iterator;
  using const_iterator = const_dict_iterator;
  using reverse_iterator = std::reverse_iterator<iterator>;
  using const_reverse_iterator = std::reverse_iterator<const_iterator>;

  explicit dict_iterator_proxy(DictStorage* storage);

  iterator begin();
  const_iterator begin() const;
  iterator end();
  const_iterator end() const;

  reverse_iterator rbegin();
  const_reverse_iterator rbegin() const;
  reverse_iterator rend();
  const_reverse_iterator rend() const;

  const_dict_iterator cbegin() const;
  const_dict_iterator cend() const;
  const_reverse_iterator crbegin() const;
  const_reverse_iterator crend() const;

 private:
  DictStorage* storage_;
};

// This class wraps the various const |begin| and |end| methods of the
// underlying DictStorage in const_dict_iterators. This allows callers to use
// this class for easy iteration over the underlying values, granting them
// either read-only access.
class const_dict_iterator_proxy {
 public:
  using key_type = const DictStorage::key_type;
  using mapped_type = const DictStorage::mapped_type::element_type;
  using value_type = std::pair<key_type, mapped_type>;
  using key_compare = DictStorage::key_compare;
  using size_type = DictStorage::size_type;
  using difference_type = DictStorage::difference_type;

  using iterator = const_dict_iterator;
  using const_iterator = const_dict_iterator;
  using reverse_iterator = std::reverse_iterator<iterator>;
  using const_reverse_iterator = std::reverse_iterator<const_iterator>;

  explicit const_dict_iterator_proxy(const DictStorage* storage);

  const_iterator begin() const;
  const_iterator end() const;

  const_reverse_iterator rbegin() const;
  const_reverse_iterator rend() const;

  const_iterator cbegin() const;
  const_iterator cend() const;
  const_reverse_iterator crbegin() const;
  const_reverse_iterator crend() const;

 private:
  const DictStorage* storage_;
};
}  // namespace detail

}  // namespace base

#endif  // BASE_VALUE_ITERATORS_H_
