// Copyright 2014 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 CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_DISPOSITION_GESTURE_FILTER_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_DISPOSITION_GESTURE_FILTER_H_

#include <queue>
#include <set>

#include "content/browser/renderer_host/input/gesture_event_packet.h"
#include "content/common/content_export.h"
#include "content/port/common/input_event_ack_state.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"

namespace content {

// Interface with which the |TouchDispositionGestureFilter| forwards gestures
// for a given touch event.
class CONTENT_EXPORT TouchDispositionGestureFilterClient {
 public:
  virtual void ForwardGestureEvent(const blink::WebGestureEvent&) = 0;
};

// Given a stream of touch-derived gesture packets, produces a refined gesture
// sequence based on the ack dispositions of the generating touch events.
class CONTENT_EXPORT TouchDispositionGestureFilter {
 public:
  explicit TouchDispositionGestureFilter(
      TouchDispositionGestureFilterClient* client);
  ~TouchDispositionGestureFilter();

  // To be called upon production of touch-derived gestures by the platform,
  // *prior* to the generating touch being forward to the renderer.  In
  // particular, |packet| contains [0, n] gestures that correspond to a given
  // touch event. It is imperative that a single packet is received for
  // *each* touch event, even those that did not produce a gesture.
  enum PacketResult {
    SUCCESS,              // Packet successfully queued.
    INVALID_PACKET_ORDER, // Packets were received in the wrong order, i.e.,
                          // TOUCH_BEGIN should always precede other packets.
    INVALID_PACKET_TYPE,  // Packet had an invalid type.
  };
  PacketResult OnGestureEventPacket(const GestureEventPacket& packet);

  // To be called upon receipt of *all* touch event acks.
  void OnTouchEventAck(InputEventAckState ack_state);

  // Whether there are any active gesture sequences still queued in the filter.
  bool IsEmpty() const;

 private:
  // Utility class for tracking gesture events and dispositions for a single
  // gesture sequence. A single sequence corresponds to all gestures created
  // between the first finger down and the last finger up, including gestures
  // generated by timeouts from a statinoary finger.
  class GestureSequence {
   public:
    struct GestureHandlingState {
      GestureHandlingState();
      // True iff the sequence has had at least one touch acked.
      bool seen_ack;
      // True iff the sequence has had any touch down event consumed.
      bool start_consumed;
      // True iff the first ack received for this sequence reported that no
      // consumer exists.
      bool no_consumer;
    };

    GestureSequence();
    ~GestureSequence();

    void Push(const GestureEventPacket& packet);
    void Pop();
    const GestureEventPacket& Front() const;
    void UpdateState(GestureEventPacket::GestureSource gesture_source,
                     InputEventAckState ack_state);
    bool IsEmpty() const;
    const GestureHandlingState& state() const { return state_; };

   private:
    std::queue<GestureEventPacket> packets_;
    GestureHandlingState state_;
  };
  bool IsGesturePrevented(blink::WebInputEvent::Type type,
                          InputEventAckState current,
                          const GestureSequence::GestureHandlingState& state)
      const;

  void UpdateAndDispatchPackets(GestureSequence* sequence,
                                InputEventAckState ack_result);

  void FilterAndSendPacket(
      const GestureEventPacket& packet,
      const GestureSequence::GestureHandlingState& sequence_state,
      InputEventAckState ack_state);

  void SendGesture(const blink::WebGestureEvent& gesture);
  void CancelTapIfNecessary();
  void CancelFlingIfNecessary();
  GestureSequence& Head();
  GestureSequence& Tail();

  TouchDispositionGestureFilterClient* client_;
  std::queue<GestureSequence> sequences_;

  // If the previous gesture of a given type was dropped instead of being
  // dispatched, its type will occur in this set. Cleared when a new touch
  // sequence begins to be acked.
  std::set<blink::WebInputEvent::Type> last_event_of_type_dropped_;

  // Bookkeeping for inserting synthetic Gesture{Tap,Fling}Cancel events
  // when necessary, e.g., GestureTapCancel when scrolling begins, or
  // GestureFlingCancel when a user taps following a GestureFlingStart.
  bool needs_tap_ending_event_;
  bool needs_fling_ending_event_;

  DISALLOW_COPY_AND_ASSIGN(TouchDispositionGestureFilter);
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_DISPOSITION_GESTURE_FILTER_H_
