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

#ifndef UI_BASE_X_SELECTION_REQUESTER_H_
#define UI_BASE_X_SELECTION_REQUESTER_H_

#include <cstddef>
#include <memory>
#include <vector>

#include "base/component_export.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/weak_ptr.h"
#include "ui/gfx/x/connection.h"

namespace ui {
class SelectionData;
class XClipboardHelper;

// Requests and later receives data from the X11 server through the selection
// system.
//
// X11 uses a system called "selections" to implement clipboards and drag and
// drop. This class interprets messages from the stateful selection request
// API. SelectionRequester should only deal with the X11 details; it does not
// implement per-component fast-paths.
class COMPONENT_EXPORT(UI_BASE_X) SelectionRequester {
 public:
  using ConvertSelectionCallback = base::OnceCallback<
      void(bool success, std::vector<uint8_t> out_data, x11::Atom out_type)>;

  SelectionRequester(x11::Window xwindow, XClipboardHelper* helper);
  SelectionRequester(const SelectionRequester&) = delete;
  SelectionRequester& operator=(const SelectionRequester&) = delete;
  ~SelectionRequester();

  base::WeakPtr<SelectionRequester> GetWeakPtr();

  // Does the work of requesting |target| from |selection|. The result is
  // returned via |callback|.
  void PerformConvertSelectionAsync(x11::Atom selection,
                                    x11::Atom target,
                                    ConvertSelectionCallback callback);

  // Asynchronously returns the first of |types| offered by the current owner of
  // |selection|. Returns an empty SelectionData object if none of |types| are
  // available.
  void RequestTypesAsync(x11::Atom selection,
                         const std::vector<x11::Atom>& types,
                         base::OnceCallback<void(SelectionData)> callback);

  // It is our owner's responsibility to plumb X11 SelectionNotify events on
  // |xwindow_| to us.
  void OnSelectionNotify(const x11::SelectionNotifyEvent& event);

  // Returns true if SelectionOwner can process the XChangeProperty event,
  // |event|.
  bool CanDispatchPropertyEvent(const x11::PropertyNotifyEvent& event);

  void OnPropertyEvent(const x11::PropertyNotifyEvent& event);

 private:
  friend class SelectionRequesterTest;

  // A request that has been issued.
  struct Request {
    Request(x11::Atom selection,
            x11::Atom target,
            ConvertSelectionCallback callback);
    ~Request();

    // The target and selection requested in the XConvertSelection() request.
    // Used for error detection.
    x11::Atom selection;
    x11::Atom target;

    // Whether the result of the XConvertSelection() request is being sent
    // incrementally.
    bool data_sent_incrementally;

    // The result data for the XConvertSelection() request.
    std::vector<scoped_refptr<base::RefCountedMemory>> out_data;
    x11::Atom out_type;

    // Whether the XConvertSelection() request was successful.
    bool success;

    // True if the request is complete.
    bool completed;

    ConvertSelectionCallback callback;
  };

  // Mark |request| as completed. If the current request is completed, converts
  // the selection for the next request.
  void CompleteRequest(size_t index, bool success);

  // Converts the selection for the request at |current_request_index_|.
  void ConvertSelectionForCurrentRequest();

  // Returns the request at |current_request_index_| or NULL if there isn't any.
  Request* GetCurrentRequest();

  void RequestTypesRecursive(x11::Atom selection,
                             std::vector<x11::Atom> types,
                             base::OnceCallback<void(SelectionData)> callback);

  void OnRequestTypesAsyncResponse(
      x11::Atom selection,
      std::vector<x11::Atom> remaining_types,
      base::OnceCallback<void(SelectionData)> callback,
      bool success,
      std::vector<uint8_t> data,
      x11::Atom type);

  // Our X11 state.
  const x11::Window x_window_;

  // Not owned.
  const raw_ptr<XClipboardHelper> helper_;

  // The property on |x_window_| set by the selection owner with the value of
  // the selection.
  const x11::Atom x_property_;

  // In progress requests.
  std::vector<std::unique_ptr<Request>> requests_;

  base::WeakPtrFactory<SelectionRequester> weak_ptr_factory_{this};
};

}  // namespace ui

#endif  // UI_BASE_X_SELECTION_REQUESTER_H_
