/*
 * Copyright (C) 2009 Intel Corp.
 * Author: Forrest Zhao <forrest.zhao@intel.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <clutter/clutter.h>
#include "math.h"
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <GL/gl.h>
#include "engine.h"
#include "../clutter-gesture/clutter-gesture.h"
#include "stroke.h"
#include "plugin.h"
#include "gesture_recog.h"

typedef enum
{
  ACTOR_STATE_IDLE = 0,
  ACTOR_STATE_SINGLE_GESTURE,
  ACTOR_STATE_SINGLE_TOUCH,
  ACTOR_STATE_2_FINGER_GESTURE,
  ACTOR_STATE_IN_EMIT_EVENT
} actor_state_t;

struct engine_core
{
  GSList               *actors;
  recognize_callback_t cb;
  void                 *data;
  void                 *s_plugin_handle;
};

struct actor_core
{
  ClutterActor  *handle;
  guint         mask;

  actor_state_t state;
  GSList        *events;
  guint         num_events;
  guint         timer;
  guint         touch_hold_interval;
  guint         touch_hold_radius;
  guint         touch_hold_timer;
  gfloat        touch_hold_start_x;
  gfloat        touch_hold_start_y;
  gfloat        touch_hold_end_x;
  gfloat        touch_hold_end_y;
  guint32       touch_hold_time;
  gint          dev_id;
  gint          dev_id_second;
  struct coord  position;
  struct coord  position_second;
  struct coord  position_last;
  struct coord  position_second_last;
  gboolean      cached;
  GSList        *cached_events;

  recognize_callback_t cb;
  void                 *data;
};

static gfloat pixels;

static void set_actor_state(struct actor_core *actor_core, actor_state_t state)
{
  actor_core->state = state;
}

static void release_cached_events(struct actor_core *actor_core)
{
  GSList *l;

  for (l = actor_core->events; l; l = l->next)
    {
      ClutterEvent *event;

      event = l->data;
      clutter_event_free(event);
    }

  g_slist_free(actor_core->events);
  actor_core->num_events = 0;
  actor_core->events = NULL;
}

static void release_point_list(GSList *list)
{
  GSList *l;

  for (l = list; l; l = l->next)
    {
      g_free(l->data);
    }

  g_slist_free(list);
}

static gboolean gesture_recognize_timeout(struct actor_core *actor_core)
{
  GSList *l;

  /* release cached_events */
  if (actor_core->cached_events)
    {
      for (l = actor_core->cached_events; l; l = l->next)
        {
          ClutterEvent *event;

          event = l->data;
          clutter_event_free(event);
        }
      g_slist_free(actor_core->cached_events);
    }

  /* point cached_events to events */
  actor_core->cached_events = actor_core->events;

  if (actor_core->events)
    {
      engine_debug("<333\n");
      clutter_event_get_coords(g_slist_nth_data(actor_core->events, actor_core->num_events - 1),
                                          &(actor_core->position.x), &(actor_core->position.y));
      engine_debug("333>\n");
    }

  actor_core->num_events = 0;
  actor_core->events = NULL;

  return TRUE;
}

static ClutterGestureHoldEvent *new_gesture_touch_hold_event(struct actor_core *actor_core,
                                                      ClutterGestureTouchholdAction action)
{
  ClutterGestureHoldEvent *hold_event;

  hold_event = g_new0(ClutterGestureHoldEvent, 1);

  hold_event->type = GESTURE_HOLD;
  hold_event->time = actor_core->touch_hold_time;
  hold_event->source = actor_core->handle;
  hold_event->stage = CLUTTER_STAGE(clutter_actor_get_stage(hold_event->source));
  hold_event->action = action;

  if (!actor_core->touch_hold_end_x && !actor_core->touch_hold_end_y)
    {
      hold_event->x = actor_core->touch_hold_start_x;
      hold_event->y = actor_core->touch_hold_start_y;
    }
  else
    {
      hold_event->x = actor_core->touch_hold_end_x;
      hold_event->x = actor_core->touch_hold_end_y;
    }

  return hold_event;
}

static gboolean emit_touch_hold_event(struct actor_core *actor_core, ClutterGestureTouchholdAction action)
{
  void *gesture_event = NULL;
  GSList *gesture_event_list = NULL;
  gboolean result;

  gesture_event = new_gesture_touch_hold_event(actor_core, action);
  gesture_event_list = g_slist_append(gesture_event_list, gesture_event);
  result = actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, 1,
                 actor_core->data);

  g_free(gesture_event);
  g_slist_free(gesture_event_list);

  return result;
}

static gboolean touch_hold_timeout(struct actor_core *actor_core)
{
  GSList *l;

  /* TODO: emit touch_hold event */
  emit_touch_hold_event(actor_core, LONG_PRESS_ACTION);

  /* stop ST gesture recognition */
  if (actor_core->timer)
    {
      g_source_remove(actor_core->timer);
      actor_core->timer = 0;
    }

  if (actor_core->cached_events)
    {
      for (l = actor_core->cached_events; l ; l = l->next)
        {
          ClutterEvent *event;

          event = l->data;
          clutter_event_free(event);
        }
        g_slist_free(actor_core->cached_events);
        actor_core->cached_events = NULL;
    }

  if (actor_core->events)
    {
      clutter_event_get_coords(g_slist_nth_data(actor_core->events, actor_core->num_events - 1),
                                           &(actor_core->position.x), &(actor_core->position.y));
      release_cached_events(actor_core);
    }

  actor_core->touch_hold_start_x = 0;
  actor_core->touch_hold_start_y = 0;
  actor_core->touch_hold_end_x = 0;
  actor_core->touch_hold_end_y = 0;
  actor_core->touch_hold_timer = 0;
  actor_core->touch_hold_time = 0;

  actor_core->state = ACTOR_STATE_SINGLE_TOUCH;

  return FALSE;
}

ClutterGestureSlideEvent *new_gesture_slide_event(struct actor_core *actor_core,
                            ClutterGestureSlideDirection direction, guint speed)
{
  ClutterGestureSlideEvent *slide_event;
  ClutterEvent *event;

  slide_event = g_new0(ClutterGestureSlideEvent, 1);

  slide_event->type = GESTURE_SLIDE;

  event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1);
  clutter_event_get_coords(event, &slide_event->x_end, &slide_event->y_end);
  slide_event->time = clutter_event_get_time(event);
  slide_event->stage = clutter_event_get_stage(event);
  /* TODO: ref/unref actor_core->handle */
  slide_event->source = actor_core->handle;

  event = g_slist_nth_data(actor_core->events, 0);
  clutter_event_get_coords(event, &slide_event->x_start, &slide_event->y_start);
  slide_event->start_time = clutter_event_get_time(event);
  slide_event->device_id = clutter_event_get_device_id(event);

  slide_event->direction = direction;

  return slide_event;
}

/*static guint calculate_speed(struct actor_core *actor_core,
                            ClutterGestureSlideDirection direction)
{
  gfloat x, y, x1, y1;
  guint32 time, time1;
  ClutterEvent *event;
  double distance, speed;

  event = g_slist_nth_data(actor_core->events, 0);
  clutter_event_get_coords(event, &x, &y);
  time = clutter_event_get_time(event);

  event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1);
  clutter_event_get_coords(event, &x1, &y1);
  time1 = clutter_event_get_time(event);

  if (direction == SLIDE_UP || direction == SLIDE_DOWN)
    {
      distance = y1 - y;
    }
  else
    {
      distance = x1 - x;
    }
  speed = distance/(time1 - time);
  speed = speed * 10;
  if (speed < 0)
    {
      speed = -speed;
    }

  if (speed > 10)
    speed = 10;
  engine_debug("y1 is %f, y is %f, time1 is %u, time is %u, the speed is %f\n",
         y1, y, time1, time, speed);

  return speed;
}
*/

/* send out GESTURE_EVENT if a gesture is recognize,
 * otherwise replay the cached CLUTTER_EVENT */
static void gesture_recognize(struct engine_core *engine,
                              struct actor_core *actor_core)
{
  GSList *l, *point_list = NULL;
  ClutterGestureSlideDirection direction;
  void *gesture_event = NULL;
  GSList *gesture_event_list = NULL;

  /* TODO: recognize the gesture */
  for (l = actor_core->events; l; l = l->next)
    {
      ClutterEvent *event;
      struct coord *point;

      point = g_new0(struct coord, 1);
      event = l->data;
      clutter_event_get_coords(event, &(point->x), &(point->y));
      point_list = g_slist_append(point_list, point);
      engine_debug("x/y is %f, %f, event type is %d\n", point->x, point->y,
                                                 clutter_event_type(event));
    }

  direction = st_gesture_process(engine->s_plugin_handle, point_list, actor_core->num_events);

  if (direction)
    {
      guint speed = 0;

      engine_debug("this is a match!!, direction is %d\n", direction);
      /* calculate speed */
//      speed = calculate_speed(actor_core, direction);
      engine_debug("the speed is %d\n", speed);
      /* send out GESTURE_EVENT */
      gesture_event = new_gesture_slide_event(actor_core, direction, speed);
      gesture_event_list = g_slist_append(gesture_event_list, gesture_event);
      actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list,
                     1, actor_core->data);
      goto done;
    }

done:
  if (gesture_event_list)
  {
    g_free(gesture_event);
    g_slist_free(gesture_event_list);
  }
  release_cached_events(actor_core);
  release_point_list(point_list);
  set_actor_state(actor_core, ACTOR_STATE_IDLE);
}

static struct actor_core *find_actor(GSList *list, ClutterActor *actor)
{
  GSList *l;

  for (l = list; l; l = l->next)
    {
      struct actor_core *actor_core = l->data;

      if (actor_core->handle == actor)
        return actor_core;
    }

  return NULL;
}

handle_t engine_init(recognize_callback_t cb, void *data)
{
  struct engine_core *engine;
  ClutterUnits units;

  engine_debug("engine init is called\n");

  engine = g_new0(struct engine_core, 1);
  engine->cb = cb;
  engine->data = data;

  engine->s_plugin_handle = s_plugin_init();

  clutter_units_from_mm(&units, 5);
  pixels = clutter_units_to_pixels(&units);

  return engine;
}

void engine_shutdown(handle_t handle)
{
  struct engine_core *engine = handle;
  GSList *l;

  s_plugin_shutdown(engine->s_plugin_handle);

  /* release actor_cores */
  for (l = engine->actors; l; l = l->next)
    {
      struct actor_core *actor_core;

      actor_core = l->data;
      g_object_unref(actor_core->handle);
      release_cached_events(actor_core);
      if (actor_core->timer)
        {
          g_source_remove(actor_core->timer);
          actor_core->timer = 0;
        }
    }
  g_slist_free(engine->actors);

  g_free(engine);

  return;
}

/* Return value: %0 if success, less than 0 if fail */
gint set_gesture_mask(handle_t handle, ClutterActor *target_actor, guint mask)
{
  struct engine_core *engine = handle;
  struct actor_core *actor_core;

  if (!mask)
    {
      actor_core = find_actor(engine->actors, target_actor);
      if (!actor_core)
        return -ENOENT;
      engine->actors = g_slist_remove(engine->actors, actor_core);
      g_object_unref(target_actor);
      /* release events, stop timer */
      release_cached_events(actor_core);

      if (actor_core->timer)
        {
          g_source_remove(actor_core->timer);
          actor_core->timer = 0;
        }

      g_free(actor_core);
      return 0;
    }

  actor_core = find_actor(engine->actors, target_actor);
  if (!actor_core)
    {
      g_object_ref(target_actor);
      actor_core = g_new0(struct actor_core, 1);
      actor_core->handle = target_actor;
      actor_core->mask = mask;
      set_actor_state(actor_core, ACTOR_STATE_IDLE);
      actor_core->num_events = 0;
      actor_core->timer = 0;
      actor_core->touch_hold_start_x = 0;
      actor_core->touch_hold_start_y = 0;
      actor_core->touch_hold_end_x = 0;
      actor_core->touch_hold_end_y = 0;
      actor_core->touch_hold_timer = 0;
      actor_core->touch_hold_interval = DEFAULT_TOUCH_HOLD_INTERVAL;
      actor_core->touch_hold_radius = DEFAULT_TOUCH_HOLD_RADIUS;
      actor_core->touch_hold_time = 0;
      actor_core->dev_id = -1;
      actor_core->cb = engine->cb;
      actor_core->data = engine->data;
      engine->actors = g_slist_append(engine->actors, actor_core);
    }
  else
    {
      actor_core->mask = mask;
    }

  return 0;
}

gint get_gesture_mask(handle_t handle, ClutterActor *target_actor, guint *mask)
{
  struct engine_core *engine = handle;
  struct actor_core *actor_core;

  actor_core = find_actor(engine->actors, target_actor);
  if (!actor_core)
    {
      *mask = 0;
      return -ENOENT;
    }

  *mask = actor_core->mask;

  return 0;
}

gboolean set_hold_timeout(handle_t handle, ClutterActor *target_actor, guint interval)
{
  struct engine_core *engine = handle;
  struct actor_core *actor_core;

  actor_core = find_actor(engine->actors, target_actor);
  if (!actor_core)
      return -ENOENT;

  actor_core->touch_hold_interval = interval;

  return 0;
}

gboolean get_hold_timeout(handle_t handle, ClutterActor *target_actor, guint *interval)
{
  struct engine_core *engine = handle;
  struct actor_core *actor_core;

  actor_core = find_actor(engine->actors, target_actor);
  if (!actor_core)
    {
      *interval = 0;
      return -ENOENT;
    }

  *interval = actor_core->touch_hold_interval;

  return 0;
}

gboolean set_hold_radius(handle_t handle, ClutterActor *target_actor, guint radius)
{
  struct engine_core *engine = handle;
  struct actor_core *actor_core;

  actor_core = find_actor(engine->actors, target_actor);
  if (!actor_core)
    return -ENOENT;

  actor_core->touch_hold_radius = radius;

  return 0;
}

gboolean get_hold_radius(handle_t handle, ClutterActor *target_actor, guint *radius)
{
  struct engine_core *engine = handle;
  struct actor_core *actor_core;

  actor_core = find_actor(engine->actors, target_actor);
  if (!actor_core)
    {
      *radius = 0;
      return -ENOENT;
    }

  *radius = actor_core->touch_hold_radius;

  return 0;
}

static gboolean state_idle_handle_event(struct engine_core *engine,
                struct actor_core *actor_core, ClutterEvent *event)
{
  ClutterButtonEvent *buttev;
  struct coord point;

  if (clutter_event_type(event) != CLUTTER_BUTTON_PRESS)
    return FALSE;

  buttev = (ClutterButtonEvent *)event;

  /* "single left click" triggers the gesture recognition */
  if (clutter_event_get_button(event) != 1 || buttev->click_count != 1)
    return FALSE;

//  struct coord point;
  clutter_event_get_coords(event, &(point.x), &(point.y));

  /* start the timer */
  actor_core->timer = g_timeout_add(100,
                      (GSourceFunc) gesture_recognize_timeout, actor_core);
  
  actor_core->events = g_slist_append(actor_core->events,
                                      clutter_event_copy(event));


  actor_core->num_events++;
  actor_core->dev_id = clutter_event_get_device_id(event);
  actor_core->cached_events = NULL;

  /* start touch_hold timer */
  if ((actor_core->mask & GESTURE_MASK_HOLD) && (actor_core->handle == event->any.source))
    {
      gboolean result;

      clutter_event_get_coords(event, &actor_core->touch_hold_start_x, &actor_core->touch_hold_start_y);
      actor_core->touch_hold_time = clutter_event_get_time(event);

      result = emit_touch_hold_event(actor_core, LONG_PRESS_QUERY);

      if (result == TRUE)
        {
          actor_core->touch_hold_timer = g_timeout_add(actor_core->touch_hold_interval,
                      (GSourceFunc) touch_hold_timeout, actor_core);
        }
      else
        {
          actor_core->touch_hold_start_x = 0;
          actor_core->touch_hold_start_y = 0;
          actor_core->touch_hold_time = 0;
        }
    }

  set_actor_state(actor_core, ACTOR_STATE_SINGLE_GESTURE);

  return FALSE;
}

ClutterGesturePinchEvent *new_gesture_pinch_event(struct actor_core *actor_core,
                        gint x_start_1, gint y_start_1,
                        gint x_end_1, gint y_end_1, gint x_start_2, gint y_start_2,
                        gint x_end_2, gint y_end_2)
{
  ClutterGesturePinchEvent *pinch_event;
//  ClutterEvent *event;

  pinch_event = g_new0(ClutterGesturePinchEvent, 1);

  pinch_event->type = GESTURE_PINCH;

//  event = g_slist_nth_data(actor_core->events, 0);
//  pinch_event->start_time = clutter_event_get_time(event);

//  event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1);
//  pinch_event->time = clutter_event_get_time(event);
//  pinch_event->stage = clutter_event_get_stage(event);
  pinch_event->source = actor_core->handle;
  pinch_event->x_start_1 = x_start_1;
  pinch_event->y_start_1 = y_start_1;
  pinch_event->x_end_1 = x_end_1;
  pinch_event->y_end_1 = y_end_1;
  pinch_event->x_start_2 = x_start_2;
  pinch_event->y_start_2 = y_start_2;
  pinch_event->x_end_2 = x_end_2;
  pinch_event->y_end_2 = y_end_2;

  return pinch_event;
}

ClutterGestureRotateEvent *new_gesture_rotate_event(struct actor_core *actor_core,
                        gint x_start_1, gint y_start_1,
                        gint x_end_1, gint y_end_1, gint x_start_2, gint y_start_2,
                        gint x_end_2, gint y_end_2)
{
  ClutterGestureRotateEvent *rotate_event;
//  ClutterEvent *event;

  rotate_event = g_new0(ClutterGestureRotateEvent, 1);

  rotate_event->type = GESTURE_ROTATE;

//  event = g_slist_nth_data(actor_core->events, 0);
//  rotate_event->start_time = clutter_event_get_time(event);

//  event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1);
//  rotate_event->time = clutter_event_get_time(event);
//  rotate_event->stage = clutter_event_get_stage(event);
  rotate_event->source = actor_core->handle;
  rotate_event->x_start_1 = x_start_1;
  rotate_event->y_start_1 = y_start_1;
  rotate_event->x_end_1 = x_end_1;
  rotate_event->y_end_1 = y_end_1;
  rotate_event->x_start_2 = x_start_2;
  rotate_event->y_start_2 = y_start_2;
  rotate_event->x_end_2 = x_end_2;
  rotate_event->y_end_2 = y_end_2;

  return rotate_event;
}

ClutterGestureNavigateEvent *new_gesture_navigate_event(struct actor_core *actor_core,
                        gint x_start_1, gint y_start_1,
                        gint x_end_1, gint y_end_1, gint x_start_2, gint y_start_2,
                        gint x_end_2, gint y_end_2)
{
  ClutterGestureNavigateEvent *navigate_event;
//  ClutterEvent *event;

  navigate_event = g_new0(ClutterGestureNavigateEvent, 1);

  navigate_event->type = GESTURE_NAVIGATE;

//  event = g_slist_nth_data(actor_core->events, 0);
//  navigate_event->start_time = clutter_event_get_time(event);

//  event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1);
//  navigate_event->time = clutter_event_get_time(event);
//  navigate_event->stage = clutter_event_get_stage(event);
  navigate_event->source = actor_core->handle;
  navigate_event->x_start_1 = x_start_1;
  navigate_event->y_start_1 = y_start_1;
  navigate_event->x_end_1 = x_end_1;
  navigate_event->y_end_1 = y_end_1;
  navigate_event->x_start_2 = x_start_2;
  navigate_event->y_start_2 = y_start_2;
  navigate_event->x_end_2 = x_end_2;
  navigate_event->y_end_2 = y_end_2;

  return navigate_event;
}

#if 0
static void mt_gesture_recognize(struct engine_core *engine,
                              struct actor_core *actor_core)
{
  GSList *l, *list1 = NULL, *list2 = NULL;
  struct coord *point;
  gesture_type g_type;
  void *gesture_event = NULL;
  GSList *gesture_event_list = NULL;
  gesture_info *gesture_info_p = NULL;

  actor_core->cached = FALSE;

  if (!actor_core->events)
    return;

  if ((actor_core->position.x != 0) || (actor_core->position.y != 0))
    {
      point = g_new0(struct coord, 1);
      point->x = actor_core->position.x;
      point->y = actor_core->position.y;
      list1 = g_slist_append(list1, point);
    }
  if ((actor_core->position_second.x != 0) || (actor_core->position_second.y != 0))
    {
      point = g_new0(struct coord, 1);
      point->x = actor_core->position_second.x;
      point->y = actor_core->position_second.y;
      list2 = g_slist_append(list2, point);
    }

  for (l = actor_core->events; l; l = l->next)
    {
      ClutterEvent *event;

      point = g_new0(struct coord, 1);
      event = l->data;
      clutter_event_get_coords(event, &(point->x), &(point->y));
      if (clutter_event_get_device_id(event) == actor_core->dev_id)
        {
          list1 = g_slist_append(list1, point);
          actor_core->position.x = point->x;
          actor_core->position.y = point->y;
        }
      else
        {
          list2 = g_slist_append(list2, point);
          actor_core->position_second.x = point->x;
          actor_core->position_second.y = point->y;
        }
    }

  /* release events if MT MASK is not set */
  if (!((actor_core->mask) & (GESTURE_MASK_ROTATE | GESTURE_MASK_PINCH)))
    {
      actor_core->cb(actor_core->handle, CLUTTER_EVENT, 0, actor_core->events,
                                     actor_core->num_events, actor_core->data);
      goto done;
    }
  /* TODO: call weian's algorithm */
  gesture_info_p = g_new0(gesture_info, 1);
  gesture_process (list1, list2, gesture_info_p);
  g_type = gesture_info_p->g_type;
      engine_debug ("\ngesture type is:");
    if (g_type == GESTURE_PINCH_IN)
        engine_debug ("--------Pitch Zoom In--------\n");
    else if (g_type == GESTURE_PINCH_OUT)
        engine_debug ("--------Pitch Zoom Out--------\n");

    else if (g_type == GESTURE_ROTATE_CKW)
        engine_debug ("--------Rotate Clockwise--------\n");
    else if (g_type == GESTURE_ROTATE_ACKW)
        engine_debug ("--------Rotate Anti - Clockwise--------\n");

    else if (g_type == GESTURE_MT_NAVIGATE)
        engine_debug ("--------Slide--------\n");
    else
        engine_debug ("--------Unknow--------\n");
    engine_debug ("\n");

  if ((g_type == GESTURE_PINCH_IN) || (g_type == GESTURE_PINCH_OUT))
    {
      struct coord *point1, *point2;
//      void *gesture_event = NULL;
//      GSList *gesture_event_list = NULL;

      point1 = g_slist_nth_data(list1, 0);
      point2 = g_slist_nth_data(list2, 0);
      gesture_event = new_gesture_pinch_event(actor_core, point1->x, point1->y,
                                actor_core->position.x, actor_core->position.y,
                                point2->x, point2->y,
                                actor_core->position_second.x,
                                actor_core->position_second.y);
      gesture_event_list = g_slist_append(gesture_event_list, gesture_event);
      actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list,
                     1, actor_core->data);
    }
  else if ((g_type == GESTURE_ROTATE_CKW) || (g_type == GESTURE_ROTATE_ACKW))
    {
      struct coord *point1, *point2;

      point1 = g_slist_nth_data(list1, 0);
      point2 = g_slist_nth_data(list2, 0);
      gesture_event = new_gesture_rotate_event(actor_core, point1->x, point1->y,
                                actor_core->position.x, actor_core->position.y,
                                point2->x, point2->y,
                                actor_core->position_second.x,
                                actor_core->position_second.y);
      gesture_event_list = g_slist_append(gesture_event_list, gesture_event);
      actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list,
                     1, actor_core->data);

    }
  else if (g_type == GESTURE_MT_NAVIGATE)
    {
      struct coord *point1, *point2;

      point1 = g_slist_nth_data(list1, 0);
      point2 = g_slist_nth_data(list2, 0);
      gesture_event = new_gesture_navigate_event(actor_core, point1->x, point1->y,
                                actor_core->position.x, actor_core->position.y,
                                point2->x, point2->y,
                                actor_core->position_second.x,
                                actor_core->position_second.y);
      gesture_event_list = g_slist_append(gesture_event_list, gesture_event);
      actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list,
                    1, actor_core->data);
    }
  else
    {
      actor_core->cb(actor_core->handle, CLUTTER_EVENT, IN_MT_GESTURE, actor_core->events,
                                                actor_core->num_events, actor_core->data);
    }

  g_free(gesture_info_p);

done:
  if (gesture_event_list)
    {
      g_free(gesture_event);
      g_slist_free(gesture_event_list);
    }
  release_cached_events(actor_core);
  release_point_list(list1);
  release_point_list(list2);
}
#endif

/*
static gboolean mt_gesture_recognize_timeout(struct actor_core *actor_core)
{
  guint32 list1 = 0, list2 = 0;
  GSList *l;

  if (actor_core->cached)
    {
      mt_gesture_recognize(NULL, actor_core);
      return TRUE;
    }

  if ((actor_core->position.x != 0) || (actor_core->position.y != 0))
    {
      list1++;
    }
  if ((actor_core->position_second.x != 0) || (actor_core->position_second.y != 0))
    {
      list2++;
    }

  for (l = actor_core->events; l; l = l->next)
    {
      ClutterEvent *event;

      event = l->data;
      if (clutter_event_get_device_id(event) == actor_core->dev_id)
        {
          list1++;
        }
      else
        {
          list2++;
        }
    }

  if ((list1 <= 2) && (list2 <= 2))
    {
      actor_core->cached = TRUE;
      return TRUE;
    }
  mt_gesture_recognize(NULL, actor_core);

  return TRUE;
}
*/

static void state_single_gesture_reset_state(struct actor_core *actor_core, ClutterEvent *event)
{
  GSList *l;

  /* stop the timer */
  if (actor_core->timer)
    {
      g_source_remove(actor_core->timer);
      actor_core->timer = 0;
    }

  /* reset the dev_id */
  actor_core->dev_id = -1;

  if (actor_core->touch_hold_timer)
    {
      clutter_event_get_coords(event, &actor_core->touch_hold_end_x, &actor_core->touch_hold_end_y);
      actor_core->touch_hold_time = clutter_event_get_time(event);
      emit_touch_hold_event(actor_core, LONG_PRESS_CANCEL);

      actor_core->touch_hold_start_x = 0;
      actor_core->touch_hold_start_y = 0;
      actor_core->touch_hold_end_x = 0;
      actor_core->touch_hold_end_y = 0;
      g_source_remove(actor_core->touch_hold_timer);
      actor_core->touch_hold_timer = 0;
      actor_core->touch_hold_time = 0;
    }

  if (actor_core->cached_events)
      {
        for (l = actor_core->cached_events; l; l = l->next)
          {
            ClutterEvent *current_event = l->data;
            clutter_event_free(current_event);
          }
        g_slist_free(actor_core->cached_events);
        actor_core->cached_events = NULL;
      }
    release_cached_events(actor_core);
    set_actor_state(actor_core, ACTOR_STATE_IDLE);

}

static gboolean state_single_gesture_handle_event(struct engine_core *engine,
                struct actor_core *actor_core, ClutterEvent *event)
{
  ClutterEvent *start_event;
  gfloat start_x, start_y, end_x, end_y;
  GSList *l;

  if (clutter_event_type(event) == CLUTTER_MOTION &&
      actor_core->dev_id == clutter_event_get_device_id(event))
    {
      gfloat x, y;

      actor_core->events = g_slist_append(actor_core->events,
                                          clutter_event_copy(event));
      actor_core->num_events++;

      if (actor_core->touch_hold_timer == 0)
        return FALSE;

      /* if motion event out of circle, stop timer */
      clutter_event_get_coords(event, &x, &y);
      if ((abs(actor_core->touch_hold_start_x - x) < actor_core->touch_hold_radius) &&
          (abs(actor_core->touch_hold_start_y - y) < actor_core->touch_hold_radius))
        {
          actor_core->touch_hold_end_x = x;
          actor_core->touch_hold_end_y = y;
          actor_core->touch_hold_time = clutter_event_get_time(event);
        }
      else
        {
           actor_core->touch_hold_end_x = x;
           actor_core->touch_hold_end_y = y;
           actor_core->touch_hold_time = clutter_event_get_time(event);

           emit_touch_hold_event(actor_core, LONG_PRESS_CANCEL);

          actor_core->touch_hold_start_x = 0;
          actor_core->touch_hold_start_y = 0;
          actor_core->touch_hold_end_x = 0;
          actor_core->touch_hold_end_y = 0;
          if (actor_core->touch_hold_timer)
            g_source_remove(actor_core->touch_hold_timer);
          actor_core->touch_hold_timer = 0;
          actor_core->touch_hold_time = 0;
        }

      return FALSE;
    }

  if (clutter_event_type(event) == CLUTTER_BUTTON_PRESS &&
      actor_core->dev_id != clutter_event_get_device_id(event))
    {
      ClutterButtonEvent *buttev = (ClutterButtonEvent *)event;

      if (clutter_event_get_button(event) != 1 || buttev->click_count != 1)
        return FALSE;

      actor_core->dev_id_second = clutter_event_get_device_id(event);

      if (actor_core->timer)
        {
          g_source_remove(actor_core->timer);
          actor_core->timer = 0;
        }

      if (actor_core->touch_hold_timer)
        {
          actor_core->touch_hold_start_x = 0;
          actor_core->touch_hold_start_y = 0;
          actor_core->touch_hold_end_x = 0;
          actor_core->touch_hold_end_y = 0;
          g_source_remove(actor_core->touch_hold_timer);
          actor_core->touch_hold_timer = 0;
          actor_core->touch_hold_time = 0;
        }

      if (actor_core->cached_events)
        {
          for (l = actor_core->cached_events; l; l = l->next)
            {
              ClutterEvent *event;

              event = l->data;
              clutter_event_free(event);
            }
          g_slist_free(actor_core->cached_events);
          actor_core->cached_events = NULL;
        }
//      actor_core->cb(actor_core->handle, CLUTTER_EVENT, actor_core->events,
//                             actor_core->num_events, actor_core->data);

      if (actor_core->events)
        {
          engine_debug("<111\n");
          clutter_event_get_coords(g_slist_nth_data(actor_core->events, actor_core->num_events - 1),
                                              &(actor_core->position.x), &(actor_core->position.y));
          engine_debug("111>\n");
          release_cached_events(actor_core);
        }

//      actor_core->events = g_slist_append(actor_core->events,
//                                          clutter_event_copy(event));
//      actor_core->num_events++;

        clutter_event_get_coords(event, &(actor_core->position_second.x), &(actor_core->position_second.y));

//      actor_core->timer = g_timeout_add(100,
//                      (GSourceFunc) mt_gesture_recognize_timeout, actor_core);

      set_actor_state(actor_core, ACTOR_STATE_2_FINGER_GESTURE);
      return TRUE;
    }

  /* Reset the state to idle */
  if (clutter_event_type(event) == CLUTTER_LEAVE &&
      actor_core->dev_id == clutter_event_get_device_id(event) &&
      actor_core->handle == event->any.source)
    {
      state_single_gesture_reset_state(actor_core, event);
      return FALSE;
    }

  if (clutter_event_type(event) != CLUTTER_BUTTON_RELEASE ||
      actor_core->dev_id != clutter_event_get_device_id(event))
    {
      return FALSE;
    }

  /* release events if GESTURE_MASK_SLIDE is not set */
  if (!((actor_core->mask) & GESTURE_MASK_SLIDE))
    {
      state_single_gesture_reset_state(actor_core, event);
      return FALSE;
    }

  /* add the last CLUTTER_BUTTON_RELEASE event to event queue */
  actor_core->events = g_slist_append(actor_core->events,
                                          clutter_event_copy(event));
  actor_core->num_events++;

  /* stop the timer and start recognition */
  if (actor_core->timer)
    {
      g_source_remove(actor_core->timer);
      actor_core->timer = 0;
    }

  /* reset the dev_id */
  actor_core->dev_id = -1;

  if (actor_core->touch_hold_timer)
    {
      clutter_event_get_coords(event, &actor_core->touch_hold_end_x, &actor_core->touch_hold_end_y);
      actor_core->touch_hold_time = clutter_event_get_time(event);
      emit_touch_hold_event(actor_core, LONG_PRESS_CANCEL);

      actor_core->touch_hold_start_x = 0;
      actor_core->touch_hold_start_y = 0;
      actor_core->touch_hold_end_x = 0;
      actor_core->touch_hold_end_y = 0;
      g_source_remove(actor_core->touch_hold_timer);
      actor_core->touch_hold_timer = 0;
      actor_core->touch_hold_time = 0;
    }

  if (actor_core->cached_events)
    {
      actor_core->cached_events = g_slist_reverse(actor_core->cached_events);
      for (l = actor_core->cached_events; l; l = l->next)
        {
          ClutterEvent *current_event = l->data;
          if ((clutter_event_get_time(event) - clutter_event_get_time(current_event)) <= 100)
            {
              actor_core->events = g_slist_prepend(actor_core->events, current_event);
              actor_core->num_events++;
            }
          else
            {
              clutter_event_free(current_event);
            }
        }
      g_slist_free(actor_core->cached_events);
      actor_core->cached_events = NULL;
    }

  start_event = g_slist_nth_data(actor_core->events, 0);
  engine_debug("<222\n");
  clutter_event_get_coords(start_event, &start_x, &start_y);
  clutter_event_get_coords(event, &end_x, &end_y);
  engine_debug("222>\n");
  if (((start_x == end_x) && (start_y == end_y)) ||
      (actor_core->num_events <= 3) || ((abs(start_x - end_x) < pixels) && abs(start_y - end_y) < pixels))
    {
        release_cached_events(actor_core);
        set_actor_state(actor_core, ACTOR_STATE_IDLE);
    }
  else
    {
      gesture_recognize(engine, actor_core);
    }

  return FALSE;
}

static gboolean state_single_touch_handle_event(struct engine_core *engine,
                struct actor_core *actor_core, ClutterEvent *event)
{
  if (clutter_event_type(event) == CLUTTER_BUTTON_RELEASE &&
      actor_core->dev_id == clutter_event_get_device_id(event))
    {
      actor_core->dev_id = -1;
      actor_core->position.x = 0;
      actor_core->position.y = 0;
      set_actor_state(actor_core, ACTOR_STATE_IDLE);
    }

  if (clutter_event_type(event) == CLUTTER_MOTION &&
      actor_core->dev_id == clutter_event_get_device_id(event))
    {
      clutter_event_get_coords(event, &(actor_core->position.x), &(actor_core->position.y));
    }

  if (clutter_event_type(event) == CLUTTER_BUTTON_PRESS &&
      actor_core->dev_id != clutter_event_get_device_id(event))
    {
      ClutterButtonEvent *buttev = (ClutterButtonEvent *)event;

      if (clutter_event_get_button(event) != 1 || buttev->click_count != 1)
        return FALSE;

      actor_core->dev_id_second = clutter_event_get_device_id(event);
      clutter_event_get_coords(event, &(actor_core->position_second.x), &(actor_core->position_second.y));
//      actor_core->events = g_slist_append(actor_core->events,
//                                          clutter_event_copy(event));
//      actor_core->num_events++;
//      actor_core->timer = g_timeout_add(100,
//                      (GSourceFunc) mt_gesture_recognize_timeout, actor_core);
      set_actor_state(actor_core, ACTOR_STATE_2_FINGER_GESTURE);
      return TRUE;
    }

  return FALSE;
}

#define PI 3.1415926535
#define angle(r) (180.0*r/PI)
static gboolean is_pinch(struct coord center, struct coord point1, struct coord point2)
{
  float frt;
  float dx, dy;
  float fago, fagn;

  dx = point1.x - center.x;
  dy = point1.y - center.y;

  if (dx ==0)
    {
      if (dy > 0)
        fago = 90;
      else
        fago = -90;
    }
  else
    {
      fago = angle(atan(dy/dx));
    }

  dx = point2.x - center.x;
  dy = point2.y - center.y;

  if (dx ==0)
    {
      if (dy > 0)
        fagn = 90;
      else
        fagn = -90;
    }
  else
    {
      fagn = angle(atan(dy/dx));
    }

  frt = fago - fagn;

  if (frt < 0.3 && frt > -0.3)
    return TRUE;
  else
    return FALSE;
}

static gboolean state_2_finger_gesture_handle_event(struct engine_core *engine,
                struct actor_core *actor_core, ClutterEvent *event)
{
  if (clutter_event_type(event) == CLUTTER_BUTTON_RELEASE)
    {
      if (actor_core->timer) 
        {
          g_source_remove(actor_core->timer);
          actor_core->timer = 0;
        }

      actor_core->events = g_slist_append(actor_core->events,
                                          clutter_event_copy(event));
      actor_core->num_events++;
      /*TODO: recognize MT gestures */
//      mt_gesture_recognize(engine, actor_core);
      if (clutter_event_get_device_id(event) == actor_core->dev_id)
        {
          actor_core->dev_id = actor_core->dev_id_second;
          actor_core->position.x = actor_core->position_second.x;
          actor_core->position.y = actor_core->position_second.y;
        }
      actor_core->position_second.x = 0;
      actor_core->position_second.y = 0;

      actor_core->position_last.x = 0;
      actor_core->position_last.y = 0;
      actor_core->position_second_last.x = 0;
      actor_core->position_second_last.y = 0;

      actor_core->cached_events = NULL;
      actor_core->timer = g_timeout_add(100,
                      (GSourceFunc) gesture_recognize_timeout, actor_core);
      set_actor_state(actor_core, ACTOR_STATE_SINGLE_GESTURE);
      return TRUE;
    }

  if (clutter_event_type(event) == CLUTTER_MOTION)
    {
      struct coord point;
      gboolean pinch;
      void *gesture_event = NULL;
      GSList *gesture_event_list = NULL;
//      actor_core->events = g_slist_append(actor_core->events,
//                                          clutter_event_copy(event));
//      actor_core->num_events++;
      clutter_event_get_coords(event, &(point.x), &(point.y));
      if (clutter_event_get_device_id(event) == actor_core->dev_id)
        {
          pinch = is_pinch(actor_core->position_second, actor_core->position, point);
        }
      else
        {
          pinch = is_pinch(actor_core->position, actor_core->position_second, point);
        }

      if (pinch)
        {
           if (clutter_event_get_device_id(event) == actor_core->dev_id)
             {
               gesture_event = new_gesture_pinch_event(actor_core, actor_core->position.x, actor_core->position.y, point.x, point.y, actor_core->position_second.x, actor_core->position_second.y, actor_core->position_second.x, actor_core->position_second.y);
//               printf ("actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y, %f, %f, %f, %f", );
               actor_core->position_last.x = actor_core->position.x;
               actor_core->position_last.y = actor_core->position.y;
               actor_core->position.x = point.x;
               actor_core->position.y = point.y;
               engine_debug ("actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y, %f, %f, %f, %f\n", actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y);

             }
           else
             {
               gesture_event = new_gesture_pinch_event(actor_core, actor_core->position.x, actor_core->position.y, actor_core->position.x, actor_core->position.y, actor_core->position_second.x, actor_core->position_second.y, point.x, point.y);
               actor_core->position_second_last.x = actor_core->position_second.x;
               actor_core->position_second_last.y = actor_core->position_second.y;
               actor_core->position_second.x = point.x;
               actor_core->position_second.y = point.y;
             }

//          gesture_event_list = g_slist_append(gesture_event_list, gesture_event);
//          actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list,
//                     1, actor_core->data);
        }
      else
        {
           if (clutter_event_get_device_id(event) == actor_core->dev_id)
             {
               gesture_event = new_gesture_rotate_event(actor_core, actor_core->position.x, actor_core->position.y, point.x, point.y, actor_core->position_second.x, actor_core->position_second.y, actor_core->position_second.x, actor_core->position_second.y);
               actor_core->position_last.x = actor_core->position.x;
               actor_core->position_last.y = actor_core->position.y;
               actor_core->position.x = point.x;
               actor_core->position.y = point.y;
             }
           else
             {
               gesture_event = new_gesture_rotate_event(actor_core, actor_core->position.x, actor_core->position.y, actor_core->position.x, actor_core->position.y, actor_core->position_second.x, actor_core->position_second.y, point.x, point.y);
               actor_core->position_second_last.x = actor_core->position_second.x;
               actor_core->position_second_last.y = actor_core->position_second.y;
               actor_core->position_second.x = point.x;
               actor_core->position_second.y = point.y;

             }
        }

          ((ClutterGestureEvent *)gesture_event)->time = clutter_event_get_time(event);
          ((ClutterGestureEvent *)gesture_event)->stage = clutter_event_get_stage(event);
          gesture_event_list = g_slist_append(gesture_event_list, gesture_event);
          actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list,
                     1, actor_core->data);

          if (gesture_event_list)
            {
              g_free(gesture_event);
              g_slist_free(gesture_event_list);
              gesture_event_list = NULL;
            }

          if  ((actor_core->position_last.x == 0 && actor_core->position_last.y == 0) ||
               (actor_core->position_second_last.x == 0 && actor_core->position_second_last.y == 0))
            return TRUE;

          if ((actor_core->position_last.x == actor_core->position.x && actor_core->position_last.y == actor_core->position.y) ||
              (actor_core->position_second_last.x == actor_core->position_second.x && actor_core->position_second_last.y == actor_core->position_second.y)) 
            return TRUE;

          if ((actor_core->position_last.y - actor_core->position.y > 0) &&
              (actor_core->position_second_last.y - actor_core->position_second.y) < 0)
           return TRUE;

         if ((actor_core->position_last.y - actor_core->position.y <= 0) &&
             (actor_core->position_second_last.y - actor_core->position_second.y) > 0)
           return TRUE;

         if ((actor_core->position_last.x - actor_core->position.x > 0) &&
             (actor_core->position_second_last.x - actor_core->position_second.x) < 0)
           return TRUE;

         if ((actor_core->position_last.x - actor_core->position.x < 0) &&
             (actor_core->position_second_last.x - actor_core->position_second.x) > 0)
           return TRUE;

          engine_debug("new_gesture_navigate_event is called, x,x`, y,y`, x1,x1`, y1, y1`, %f,%f,%f,%f,%f,%f,%f,%f\n", actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y, actor_core->position_second_last.x, actor_core->position_second_last.y, actor_core->position_second.x, actor_core->position_second.y);
          gesture_event = new_gesture_navigate_event(actor_core, actor_core->position_last.x, actor_core->position_last.y,
                                                     actor_core->position.x, actor_core->position.y,
                                                     actor_core->position_second_last.x, actor_core->position_second_last.y,
                                                     actor_core->position_second.x, actor_core->position_second.y);
          ((ClutterGestureEvent *)gesture_event)->time = clutter_event_get_time(event);
          ((ClutterGestureEvent *)gesture_event)->stage = clutter_event_get_stage(event);
          gesture_event_list = g_slist_append(gesture_event_list, gesture_event);
          actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list,
                     1, actor_core->data);

          actor_core->position_last.x = 0;
          actor_core->position_last.y = 0;
          actor_core->position_second_last.x = 0;
          actor_core->position_second_last.y = 0;

         if (gesture_event_list)
            {
              g_free(gesture_event);
              g_slist_free(gesture_event_list);
            }

          return TRUE;
    }

  return FALSE;
}

/* Return value: %TRUE if event is accepted by engine; %FALSE otherwise */
gboolean fill_event(handle_t handle, ClutterActor *target_actor,
			ClutterEvent *event)
{
  struct engine_core *engine = handle;
  struct actor_core *actor_core;
  gboolean ret = FALSE;

  actor_core = find_actor(engine->actors, target_actor);
  if (!actor_core)
    {
      engine_debug("in fill_event(), actor_core not found\n");
      return FALSE;
    }

  switch (actor_core->state)
    {
    case ACTOR_STATE_IDLE:
      ret = state_idle_handle_event(engine, actor_core, event);
      break;
    case ACTOR_STATE_SINGLE_GESTURE:
      ret = state_single_gesture_handle_event(engine, actor_core, event);
      break;
    case ACTOR_STATE_SINGLE_TOUCH:
      ret = state_single_touch_handle_event(engine, actor_core, event);
      break;
    case ACTOR_STATE_2_FINGER_GESTURE:
      ret = state_2_finger_gesture_handle_event(engine, actor_core, event);
      break;
    default:
      break;
    }

  return ret;
}
