// Copyright (c) 2012 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_EVENTS_GESTURES_GESTURE_SEQUENCE_H_
#define UI_EVENTS_GESTURES_GESTURE_SEQUENCE_H_

#include "base/timer/timer.h"
#include "ui/events/event_constants.h"
#include "ui/events/gesture_event_details.h"
#include "ui/events/gestures/gesture_point.h"
#include "ui/events/gestures/gesture_recognizer.h"
#include "ui/gfx/rect.h"

namespace ui {
class TouchEvent;
class GestureEvent;

// Gesture state.
enum GestureState {
  GS_NO_GESTURE,
  GS_PENDING_SYNTHETIC_CLICK,
  // One finger is down: tap could occur, but scroll cannot until the number of
  // active touch points changes.
  GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL,
  // One finger is down: no gestures can occur until the number of active touch
  // points changes.
  GS_SYNTHETIC_CLICK_ABORTED,
  GS_SCROLL,
  GS_PINCH,
  GS_PENDING_TWO_FINGER_TAP,
  GS_PENDING_TWO_FINGER_TAP_NO_PINCH,
  GS_PENDING_PINCH,
  GS_PENDING_PINCH_NO_PINCH,
};

enum ScrollType {
  ST_FREE,
  ST_HORIZONTAL,
  ST_VERTICAL,
};

enum IsFirstScroll {
  FS_FIRST_SCROLL,
  FS_NOT_FIRST_SCROLL,
};

// Delegates dispatch of gesture events for which the GestureSequence does not
// have enough context to dispatch itself.
class EVENTS_EXPORT GestureSequenceDelegate {
 public:
  virtual void DispatchPostponedGestureEvent(GestureEvent* event) = 0;

 protected:
  virtual ~GestureSequenceDelegate() {}
};

// A GestureSequence recognizes gestures from touch sequences.
class EVENTS_EXPORT GestureSequence {
 public:
  // Maximum number of points in a single gesture.
  static const int kMaxGesturePoints = 12;

  explicit GestureSequence(GestureSequenceDelegate* delegate);
  virtual ~GestureSequence();

  typedef GestureRecognizer::Gestures Gestures;

  // Invoked for each touch event that could contribute to the current gesture.
  // Returns list of  zero or more GestureEvents identified after processing
  // TouchEvent.
  // Caller would be responsible for freeing up Gestures.
  virtual Gestures* ProcessTouchEventForGesture(const TouchEvent& event,
                                                EventResult status);
  const GesturePoint* points() const { return points_; }
  int point_count() const { return point_count_; }

  const gfx::PointF& last_touch_location() const {
    return last_touch_location_;
  }

 protected:
  virtual base::OneShotTimer<GestureSequence>* CreateTimer();
  base::OneShotTimer<GestureSequence>* GetLongPressTimer();
  base::OneShotTimer<GestureSequence>* GetShowPressTimer();

 private:
  // Recreates the axis-aligned bounding box that contains all the touch-points
  // at their most recent position.
  void RecreateBoundingBox();

  void ResetVelocities();

  GesturePoint& GesturePointForEvent(const TouchEvent& event);

  // Do a linear scan through points_ to find the GesturePoint
  // with id |point_id|.
  GesturePoint* GetPointByPointId(int point_id);

  bool IsSecondTouchDownCloseEnoughForTwoFingerTap();

  // Creates a gesture event with the specified parameters. The function
  // includes some common information (e.g. number of touch-points in the
  // gesture etc.) in the gesture event as well.
  GestureEvent* CreateGestureEvent(const GestureEventDetails& details,
                                   const gfx::PointF& location,
                                   int flags,
                                   base::Time timestamp,
                                   unsigned int touch_id_bitmask);

  // Functions to be called to add GestureEvents, after successful recognition.

  // Tap gestures.
  void AppendTapDownGestureEvent(const GesturePoint& point, Gestures* gestures);
  void PrependTapCancelGestureEvent(const GesturePoint& point,
                                   Gestures* gestures);
  void AppendBeginGestureEvent(const GesturePoint& point, Gestures* gestures);
  void AppendEndGestureEvent(const GesturePoint& point, Gestures* gestures);
  void AppendClickGestureEvent(const GesturePoint& point,
                               int tap_count,
                               Gestures* gestures);
  void AppendDoubleClickGestureEvent(const GesturePoint& point,
                                     Gestures* gestures);
  void AppendLongPressGestureEvent();
  void AppendShowPressGestureEvent();
  void AppendLongTapGestureEvent(const GesturePoint& point,
                                 Gestures* gestures);

  // Scroll gestures.
  void AppendScrollGestureBegin(const GesturePoint& point,
                                const gfx::PointF& location,
                                Gestures* gestures);
  void AppendScrollGestureEnd(const GesturePoint& point,
                              const gfx::PointF& location,
                              Gestures* gestures,
                              float x_velocity,
                              float y_velocity);
  void AppendScrollGestureUpdate(GesturePoint& point,
                                 Gestures* gestures,
                                 IsFirstScroll is_first_scroll);

  // Pinch gestures.
  void AppendPinchGestureBegin(const GesturePoint& p1,
                               const GesturePoint& p2,
                               Gestures* gestures);
  void AppendPinchGestureEnd(const GesturePoint& p1,
                             const GesturePoint& p2,
                             float scale,
                             Gestures* gestures);
  void AppendPinchGestureUpdate(const GesturePoint& point,
                                float scale,
                                Gestures* gestures);
  void AppendSwipeGesture(const GesturePoint& point,
                          int swipe_x,
                          int swipe_y,
                          Gestures* gestures);
  void AppendTwoFingerTapGestureEvent(Gestures* gestures);

  void set_state(const GestureState state) { state_ = state; }

  // Various GestureTransitionFunctions for a signature.
  // There is, 1:many mapping from GestureTransitionFunction to Signature
  // But a Signature have only one GestureTransitionFunction.
  bool Click(const TouchEvent& event,
             const GesturePoint& point,
             Gestures* gestures);
  bool ScrollStart(const TouchEvent& event,
                   GesturePoint& point,
                   Gestures* gestures);
  void BreakRailScroll(const TouchEvent& event,
                       GesturePoint& point,
                       Gestures* gestures);
  bool ScrollUpdate(const TouchEvent& event,
                    GesturePoint& point,
                    Gestures* gestures,
                    IsFirstScroll is_first_scroll);
  bool TouchDown(const TouchEvent& event,
                 const GesturePoint& point,
                 Gestures* gestures);
  bool TwoFingerTouchDown(const TouchEvent& event,
                          const GesturePoint& point,
                          Gestures* gestures);
  bool TwoFingerTouchMove(const TouchEvent& event,
                          const GesturePoint& point,
                          Gestures* gestures);
  bool TwoFingerTouchReleased(const TouchEvent& event,
                              const GesturePoint& point,
                              Gestures* gestures);
  bool ScrollEnd(const TouchEvent& event,
                 GesturePoint& point,
                 Gestures* gestures);
  bool PinchStart(const TouchEvent& event,
                  const GesturePoint& point,
                  Gestures* gestures);
  bool PinchUpdate(const TouchEvent& event,
                   GesturePoint& point,
                   Gestures* gestures);
  bool PinchEnd(const TouchEvent& event,
                const GesturePoint& point,
                Gestures* gestures);
  bool MaybeSwipe(const TouchEvent& event,
                  const GesturePoint& point,
                  Gestures* gestures);

  void TwoFingerTapOrPinch(const TouchEvent& event,
                           const GesturePoint& point,
                           Gestures* gestures);

  void StopTimersIfRequired(const TouchEvent& event);

  void StartRailFreeScroll(const GesturePoint& point, Gestures* gestures);

  // Current state of gesture recognizer.
  GestureState state_;

  // ui::EventFlags.
  int flags_;

  // We maintain the smallest axis-aligned rectangle that contains all the
  // current touch-points. This box is updated after every touch-event.
  gfx::RectF bounding_box_;

  // The center of the bounding box used in the latest multi-finger scroll
  // update gesture.
  gfx::PointF latest_multi_scroll_update_location_;

  // The last scroll update prediction offset. This is removed from the scroll
  // distance on the next update since the page has already been scrolled this
  // distance.
  gfx::Vector2dF last_scroll_prediction_offset_;

  // For pinch, the 'distance' represents the diagonal distance of
  // |bounding_box_|.

  // The distance between the two points at PINCH_START.
  float pinch_distance_start_;

  // This distance is updated after each PINCH_UPDATE.
  float pinch_distance_current_;

  // This is the time when second touch down was received. Used for determining
  // if a two finger double tap has happened.
  base::TimeDelta second_touch_time_;

  ScrollType scroll_type_;
  scoped_ptr<base::OneShotTimer<GestureSequence> > long_press_timer_;
  scoped_ptr<base::OneShotTimer<GestureSequence> > show_press_timer_;

  GesturePoint points_[kMaxGesturePoints];
  int point_count_;

  // Location of the last touch event.
  gfx::PointF last_touch_location_;

  GestureSequenceDelegate* delegate_;

  DISALLOW_COPY_AND_ASSIGN(GestureSequence);
};

}  // namespace ui

#endif  // UI_EVENTS_GESTURES_GESTURE_SEQUENCE_H_