/*
 * Copyright 2010, 2011 Canonical, Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of either or both of the following licenses:
 *
 * 1) the GNU Lesser General Public License version 3, as published by the
 * Free Software Foundation; and/or
 * 2) the GNU Lesser General Public License version 2.1, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the applicable version of the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of both the GNU Lesser General Public
 * License version 3 and version 2.1 along with this program.  If not, see
 * <http://www.gnu.org/licenses/>
 *
 * Authors:
 *    Cody Russell <crussell@canonical.com>
 */

#ifndef GRIP_GESTURE_MANAGER_H
#define GRIP_GESTURE_MANAGER_H

#include <gtk/gtk.h>
#include "gripinputdevice.h"

G_BEGIN_DECLS

/**
 * SECTION:GripGestureManager
 *
 * The Grip gesture managre is a singleton object that connections to the
 * gesture recognition engine and manages gesture subscriptions for GTK widgets.
 */

/**
 * SECTION:GripGestureEvents
 *
 * A series of one or more events are passed to the gesture callback.  Each
 * event conveys information specific to the type of gesture occurring.
 */

/**
 * GRIP_TYPE_GESTURE_MANAGER:
 *
 * Gets the #GType for the #GripGestureManager.
 */
#define GRIP_TYPE_GESTURE_MANAGER            (grip_gesture_manager_get_type ())

/**
 * GRIP_GESTURE_MANAGER:
 * @obj: The #GObject to convert.
 *
 * Performs a checked conversion of a #GObject to a #GripGestureManager.
 */
#define GRIP_GESTURE_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GRIP_TYPE_GESTURE_MANAGER, GripGestureManager))

/**
 * GRIP_GESTURE_MANAGER_CLASS:
 * @klass: The #GObjectClass to convert.
 *
 * Performes a checked conversion of a #GObjectClass to a
 * #GripGestureManagerClass.
 */
#define GRIP_GESTURE_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GRIP_TYPE_GESTURE_MANAGER, GripGestureManagerClass))

/**
 * GRIP_IS_GESTURE_MANAGER:
 * @obj: The #GObject to check.
 *
 * Checks to see if #GripGestureManagerClass is in the #GObjectClass heirarchy
 * of @obj.
 */
#define GRIP_IS_GESTURE_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GRIP_TYPE_GESTURE_MANAGER))

/**
 * GRIP_IS_GESTURE_MANAGER_CLASS:
 * @klass: The #GObjectClass to check.
 *
 * Checks to see if a #GripGestureManagerClass object is in the object heirarchy
 * of @klass.
 */
#define GRIP_IS_GESTURE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GRIP_TYPE_GESTURE_MANAGER))

/**
 * GRIP_GESTURE_MANAGER_GET_CLASS:
 * @obj: A #GObject.
 *
 * Gets the @GripGestureManagerClass object for @obj if that object is a member
 * of its #GObjectClass heirarchy.
 */
#define GRIP_GESTURE_MANAGER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GRIP_TYPE_GESTURE_MANAGER, GripGestureManagerClass))

typedef struct _GripGestureManager        GripGestureManager;
typedef struct _GripGestureManagerClass   GripGestureManagerClass;
typedef struct _GripGestureManagerPrivate GripGestureManagerPrivate;

typedef union  _GripGestureEvent       GripGestureEvent;
typedef struct _GripEventGestureAny    GripEventGestureAny;
typedef struct _GripEventGestureDrag   GripEventGestureDrag;
typedef struct _GripEventGesturePinch  GripEventGesturePinch;
typedef struct _GripEventGestureRotate GripEventGestureRotate;
typedef struct _GripEventGestureTap    GripEventGestureTap;

/**
 * GripGestureType:
 * @GRIP_GESTURE_DRAG: a drag gesture
 * @GRIP_GESTURE_PINCH: a pinch/expand gesture
 * @GRIP_GESTURE_ROTATE: a rotate gesture
 * @GRIP_GESTURE_TAP: a tap gesture
 *
 * Indicates the type of gesture for which a gesture event is being received.
 */
typedef enum {
  GRIP_GESTURE_DRAG    = 0,
  GRIP_GESTURE_PINCH   = 1,
  GRIP_GESTURE_ROTATE  = 2,
  GRIP_GESTURE_TAP     = 15
} GripGestureType;


#define GRIP_DEVICE_ALL (GRIP_DEVICE_TOUCHSCREEN | GRIP_DEVICE_TOUCHPAD | GRIP_DEVICE_INDEPENDENT)

/**
 * GripTimeType:
 * @GRIP_TIME_START: a new gesture is starting
 * @GRIP_TIME_UPDATE: an existing gesture is updating
 * @GRIP_TIME_END: a gesture is ending
 *
 * Indicates the part of the gesture stream ocuuring during the gesture event.
 */
typedef enum {
  GRIP_TIME_START,
  GRIP_TIME_UPDATE,
  GRIP_TIME_END
} GripTimeType;

/**
 * GripEventGestureAny:
 * 
 * This struct is not used.
 */
struct _GripEventGestureAny
{
  GdkEventType  type;
  GdkWindow    *window;
};

/**
 * GripEventGestureDrag:
 * @type: the #GripGestureType of the gesture
 * @id: identifies the gesture
 * @window: the #GdkWindow in which the gesture occurred
 * @root: the root #GdkWindow
 * @child: the child #GdkWindow
 * @timestamp: the time the gesture event occurred
 * @fingers: the number of touches making up the gesture
 * @focus_x: the X coordinate of the focus point of the gesture start
 * @focus_y: the Y coordinate of the focus point of the gesture start
 * @delta_x: the change in the X coordinate since the last gesture event
 * @delta_y: the change in the Y coordinate since the last gesture event
 * @velocity_x: the rate of change of the X coordinate
 * @velocity_y: the rate of change of the Y coordinate
 * @position_x: the current X coordinate of the centroid poistion of the touches
 * @position_y: the current Y coordinate of the centroid poistion of the touches
 * @input_device: the #GripInputDevice used to make the gesture
 *
 * Data associated with a drag event.  A drag is a lateral motion.
 */
struct _GripEventGestureDrag
{
  GripGestureType   type;
  guint             id;
  GdkWindow        *window;
  GdkWindow        *root;
  GdkWindow        *child;
  guint32           timestamp;
  gint              fingers;
  gdouble           focus_x;
  gdouble           focus_y;
  gint              delta_x;
  gint              delta_y;
  gdouble           velocity_x;
  gdouble           velocity_y;
  gdouble           position_x;
  gdouble           position_y;
  GripInputDevice  *input_device;
};

/**
 * GripEventGesturePinch:
 * @type: the #GripGestureType of the gesture
 * @id: identifies the gesture
 * @window: the #GdkWindow in which the gesture occurred
 * @root: the root #GdkWindow
 * @child: the child #GdkWindow
 * @timestamp: the time the gesture event occurred
 * @fingers: the number of touches making up the gesture
 * @focus_x: the X coordinate of the focus point of the gesture start
 * @focus_y: the Y coordinate of the focus point of the gesture start
 * @radius_delta: the change in the radius (in screen coordinates)
 * @radial_velocity: the rate of change of the radius
 * @radius: the current radius (in screen coordinates)
 * @position_x: the current X coordinate of the centroid poistion of the touches
 * @position_y: the current Y coordinate of the centroid poistion of the touches
 * @input_device: the #GripInputDevice used to make the gesture
 *
 * Data associated with a pinch event.  A pinch is an expand or contract motion.
 */
struct _GripEventGesturePinch
{
  GripGestureType   type;
  guint             id;
  GdkWindow        *window;
  GdkWindow        *root;
  GdkWindow        *child;
  guint32           timestamp;
  guint             fingers;
  gdouble           focus_x;
  gdouble           focus_y;
  gdouble           radius_delta;
  gdouble           radial_velocity;
  gdouble           radius;
  gfloat            position_x;
  gfloat            position_y;
  GripInputDevice  *input_device;
};

/**
 * GripEventGestureRotate:
 * @type: the #GripGestureType of the gesture
 * @id: identifies the gesture
 * @window: the #GdkWindow in which the gesture occurred
 * @root: the root #GdkWindow
 * @child: the child #GdkWindow
 * @timestamp: the time the gesture event occurred
 * @fingers: the number of touches making up the gesture
 * @focus_x: the X coordinate of the focus point of the gesture start
 * @focus_y: the Y coordinate of the focus point of the gesture start
 * @angle_delta: the change in the rotation angle (in radians) 
 * @angular_velocity: the rate of change in the rotation angle
 * @angle: the current rotation angle
 * @position_x: the current X coordinate of the centroid poistion of the touches
 * @position_y: the current Y coordinate of the centroid poistion of the touches
 * @input_device: the #GripInputDevice used to make the gesture
 *
 * Data associated with a rotate gesture event.
 */
struct _GripEventGestureRotate
{
  GripGestureType  type;
  guint             id;
  GdkWindow        *window;
  GdkWindow        *root;
  GdkWindow        *child;
  guint32           timestamp;
  guint             fingers;
  gdouble           focus_x;
  gdouble           focus_y;
  gdouble           angle_delta;
  gdouble           angular_velocity;
  gdouble           angle;
  gfloat            position_x;
  gfloat            position_y;
  GripInputDevice  *input_device;
};

/**
 * GripEventGestureTap:
 * @type: the #GripGestureType of the gesture
 * @id: identifies the gesture
 * @window: the #GdkWindow in which the gesture occurred
 * @root: the root #GdkWindow
 * @child: the child #GdkWindow
 * @timestamp: the time the gesture event occurred
 * @fingers: the number of touches making up the gesture
 * @tap_time: the duration of the tap
 * @focus_x: the X coordinate of the focus point of the gesture start
 * @focus_y: the Y coordinate of the focus point of the gesture start
 * @position_x: the current X coordinate of the centroid poistion of the touches
 * @position_y: the current Y coordinate of the centroid poistion of the touches
 * @input_device: the #GripInputDevice used to make the gesture
 *
 * Data associated with a tap gesture.
 */
struct _GripEventGestureTap
{
  GripGestureType  type;
  guint             id;
  GdkWindow        *window;
  GdkWindow        *root;
  GdkWindow        *child;
  guint32           timestamp;
  guint             fingers;
  guint32           tap_time;
  gfloat            focus_x;
  gfloat            focus_y;
  gfloat            position_x;
  gfloat            position_y;
  GripInputDevice  *input_device;
};

/**
 * GripGestureEvent:
 * @type: the #GripGestureType, selects the variant record
 * @drag: the event contains a drag gesture record
 * @pinch: the event contains a pinch gesture record
 * @rotate: the event contains a rotate gesture record
 * @tap: the event contains a tap gesture record
 *
 * This is a boxed type.
 */
union _GripGestureEvent
{
  GripGestureType        type;
  GripEventGestureAny    any;
  GripEventGestureDrag   drag;
  GripEventGesturePinch  pinch;
  GripEventGestureRotate rotate;
  GripEventGestureTap    tap;
};

/**
 * GripGestureManager:
 * @parent_instance: the parent #GObject instance.
 *
 * A singleton manager into which a window may be registered to receive
 * multitouch gesture events.
 */
struct _GripGestureManager
{
  /*< Public >*/
  GObject parent_instance;

  /*< Private >*/
  GripGestureManagerPrivate *priv;
};

/**
 * GripGestureManagerClass:
 * @parent_class: the base #GObjectClass
 *
 * The class object for a #GripGestureManager.
 */
struct _GripGestureManagerClass
{
  GObjectClass parent_class;
};

/**
 * GripGestureCallback:
 * @widget: (in): A #GtkWidget pointer
 * @time: A #GripTimeType
 * @gesture: (in): A #GripGestureEvent pointer
 * @user_data: (transfer none): user data
 *
 * The gesture callback function is registered by the
 * grip_gesture_manager_register_window() function and gets called whenever a
 * new gesture event has been received.  The same function may be registered for
 * more than one gesture type.
 *
 * The <type>GripGestureCallback</type> function is the main customization point
 * for application response to gestural input.
 **/
typedef void (* GripGestureCallback) (GtkWidget         *widget,
                                      GripTimeType       time,
                                      GripGestureEvent  *gesture,
                                      gpointer           user_data);

GType                grip_gesture_manager_get_type        (void) G_GNUC_CONST;

/**
 * grip_gesture_manager_get:
 *
 * Retrieves a #GripGestureManager pointer.
 *
 * Return value: (transfer none): A #GripGestureManager
 **/
GripGestureManager *grip_gesture_manager_get              (void);

/**
 * grip_gesture_manager_register_window:
 * @manager: self
 * @widget: A #GtkWidget on which to register the gesture
 * @gesture_type: Gesture type
 * @device_type: Type of the device producing the gesture.
 * @touch_points: Number of touch points
 * @callback: Callback
 * @user_data: (transfer none): User data
 * @destroy: (transfer none) (scope async): Destroy
 */
void                 grip_gesture_manager_register_window (GripGestureManager *manager,
                                                           GtkWidget          *widget,
                                                           GripGestureType     gesture_type,
                                                           GripDeviceType      device_type,
                                                           gint                touch_points,
                                                           GripGestureCallback callback,
                                                           gpointer            user_data,
                                                           GDestroyNotify      destroy);

void grip_gesture_manager_unregister_window (GripGestureManager  *manager,
                                             GtkWidget           *widget);

GType             grip_gesture_event_get_type (void) G_GNUC_CONST;
GripGestureEvent *grip_gesture_event_new      (GripGestureType   gesture_type);
void              grip_gesture_event_free     (GripGestureEvent *event);
GripGestureEvent *grip_gesture_event_copy     (const GripGestureEvent *event);


G_END_DECLS

#endif /* GRIP_GESTURE_MANAGER_H */
