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

#ifndef COMPONENTS_PERMISSIONS_PERMISSION_REQUEST_QUEUE_H_
#define COMPONENTS_PERMISSIONS_PERMISSION_REQUEST_QUEUE_H_

#include <cstddef>
#include <vector>

#include "base/containers/circular_deque.h"
#include "components/permissions/permission_request.h"

namespace permissions {

// Provides a container for holding pending PermissionRequest objects and
// provides access methods respecting the currently applicable feature flag
// configuration.
// The queue of permission requests is always held in the order of:
// High Priority Requests > Normal Priority Requests > Low Priority Requests.
// Using the |PushFront| and |PushBack| functions will push the new request in
// the front or back of the section of the queue that corresponds to that
// request's priority.

// High Priority Requests are requests that come from an Page-Embedded
// Permission Control.
// Low Priority Requests are requests for non-urgent permission types
// (notifications, geolocation) if the current platform supports the permission
// chip. If the permission chip is not supported, there are no low priority
// requests.
// Normal Priority Requests are all other requests.
class PermissionRequestQueue {
 public:
  using const_iterator = std::vector<base::circular_deque<
      std::unique_ptr<permissions::PermissionRequest>>>::const_iterator;

  // Not copyable or movable
  PermissionRequestQueue(const PermissionRequestQueue&) = delete;
  PermissionRequestQueue& operator=(const PermissionRequestQueue&) = delete;
  PermissionRequestQueue();
  ~PermissionRequestQueue();

  bool IsEmpty() const;
  bool Contains(PermissionRequest* request) const;
  size_t size() const { return size_; }

  // Push a new request into queue. This function will decide based on request
  // priority and platform whether to call |PushBack| or |PushFront|.
  void Push(std::unique_ptr<permissions::PermissionRequest> request);

  // Push a new request into the front of the section of the queue that
  // corresponds to its priority. E.g.: calling this function on a normal
  // priority |request| will put it in front of any other normal priority
  // requests, but still behind any high priority requests.
  void PushFront(std::unique_ptr<permissions::PermissionRequest> request);

  // Push a new request into the back of the section of the queue that
  // corresponds to its priority. E.g.: calling this function on a normal
  // priority |request| will put it behind any other normal priority requests,
  // but still in front of any low priority requests.
  void PushBack(std::unique_ptr<permissions::PermissionRequest> request);

  std::unique_ptr<permissions::PermissionRequest> Pop();
  PermissionRequest* Peek() const;

  // Searches queued_requests_ and returns the first matching request, or
  // nullptr if there is no match.
  PermissionRequest* FindDuplicate(PermissionRequest* request) const;

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

 private:
  enum class Priority {
    kLow,
    kMedium,
    kHigh,

    // Used to set the correct size of the |queued_requests_| vector.
    kNum,
  };

  static Priority DetermineRequestPriority(
      permissions::PermissionRequest* request);

  void PushFrontInternal(
      std::unique_ptr<permissions::PermissionRequest> request,
      Priority priority);
  void PushBackInternal(std::unique_ptr<permissions::PermissionRequest> request,
                        Priority priority);

  // Each priority has a separate deque. There is an assumption made that the
  // priorities have strictly ascending, contignous values from lowest to
  // highest.
  std::vector<
      base::circular_deque<std::unique_ptr<permissions::PermissionRequest>>>
      queued_requests_;

  size_t size_{0};
};

}  // namespace permissions

#endif  // COMPONENTS_PERMISSIONS_PERMISSION_REQUEST_QUEUE_H_
