// Copyright (c) 2013 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 UI_BASE_X_SELECTION_REQUESTOR_H_
#define UI_BASE_X_SELECTION_REQUESTOR_H_

#include <stddef.h>

#include <vector>

#include "base/callback.h"
#include "base/event_types.h"
#include "base/macros.h"
#include "base/memory/ref_counted_memory.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/base/ui_base_export.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/gfx/x/x11_types.h"

namespace ui {
class PlatformEventDispatcher;
class SelectionData;

// 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. SelectionRequestor should only deal with the X11 details; it does not
// implement per-component fast-paths.
class UI_BASE_EXPORT SelectionRequestor {
 public:
  SelectionRequestor(XDisplay* xdisplay,
                     XID xwindow,
                     PlatformEventDispatcher* dispatcher);
  ~SelectionRequestor();

  // Does the work of requesting |target| from |selection|, spinning up the
  // nested message loop, and reading the resulting data back. The result is
  // stored in |out_data|.
  // |out_data_items| is the length of |out_data| in |out_type| items.
  bool PerformBlockingConvertSelection(
      XAtom selection,
      XAtom target,
      scoped_refptr<base::RefCountedMemory>* out_data,
      size_t* out_data_items,
      XAtom* out_type);

  // Requests |target| from |selection|, passing |parameter| as a parameter to
  // XConvertSelection().
  void PerformBlockingConvertSelectionWithParameter(
      XAtom selection,
      XAtom target,
      const std::vector<XAtom>& parameter);

  // Returns the first of |types| offered by the current owner of |selection|.
  // Returns an empty SelectionData object if none of |types| are available.
  SelectionData RequestAndWaitForTypes(XAtom selection,
                                       const std::vector<XAtom>& types);

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

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

  void OnPropertyEvent(const XEvent& event);

 private:
  friend class SelectionRequestorTest;

  // A request that has been issued.
  struct Request {
    Request(XAtom selection, XAtom target, base::TimeTicks timeout);
    ~Request();

    // The target and selection requested in the XConvertSelection() request.
    // Used for error detection.
    XAtom selection;
    XAtom 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;
    size_t out_data_items;
    XAtom out_type;

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

    // The time when the request should be aborted.
    base::TimeTicks timeout;

    // Called to terminate the nested message loop.
    base::Closure quit_closure;

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

  // Aborts requests which have timed out.
  void AbortStaleRequests();

  // 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();

  // Blocks till SelectionNotify is received for the target specified in
  // |request|.
  void BlockTillSelectionNotifyForRequest(Request* request);

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

  // Our X11 state.
  XDisplay* x_display_;
  XID x_window_;

  // The property on |x_window_| set by the selection owner with the value of
  // the selection.
  XAtom x_property_;

  // Dispatcher which handles SelectionNotify and SelectionRequest for
  // |selection_name_|. PerformBlockingConvertSelection() calls the
  // dispatcher directly if PerformBlockingConvertSelection() is called after
  // the PlatformEventSource is destroyed.
  // Not owned.
  PlatformEventDispatcher* dispatcher_;

  // In progress requests. Requests are added to the list at the start of
  // PerformBlockingConvertSelection() and are removed and destroyed right
  // before the method terminates.
  std::vector<Request*> requests_;

  // The index of the currently active request in |requests_|. The active
  // request is the request for which XConvertSelection() has been
  // called and for which we are waiting for a SelectionNotify response.
  size_t current_request_index_;

  // Used to abort requests if the selection owner takes too long to respond.
  base::RepeatingTimer abort_timer_;

  X11AtomCache atom_cache_;

  DISALLOW_COPY_AND_ASSIGN(SelectionRequestor);
};

}  // namespace ui

#endif  // UI_BASE_X_SELECTION_REQUESTOR_H_
