/*
 * Copyright (C) 2008 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 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 warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authored by Neil Jagdish Patel <neil.patel@canonical.com>
 *
 */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib/gi18n.h>
#include <string.h>

#include <pango/pango.h>
#include <math.h>

#include <clutter-gtk/gtk-clutter-util.h>

#include "launcher-icon.h"

#include "clutter-drag-server.h"

#include "launcher-config.h"
#include "launcher-defines.h"
#include "launcher-iconview.h"
#include "launcher-notify.h"
#include "launcher-startup.h"
#include "launcher-util.h"

#include "tidy-texture-frame.h"

G_DEFINE_TYPE (LauncherIcon, launcher_icon, CLUTTER_TYPE_GROUP);

#define LAUNCHER_ICON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
  LAUNCHER_TYPE_ICON, \
  LauncherIconPrivate))

#define DRAG_RADIUS 7

static ClutterActor *bg_texture = NULL;
static ClutterColor orange = { 221, 119, 27, 255 };
static ClutterColor white  = { 0xff, 0xff, 0xff, 0xff };


struct _LauncherIconPrivate
{
  LauncherMenuApplication *app;
  
  ClutterTimeline       *time;
  ClutterEffectTemplate *temp;

  ClutterTimeline       *fast_time;
  ClutterEffectTemplate *fast_temp;
  ClutterTimeline       *current;
  
  ClutterActor *label;
  ClutterActor *orange;
  ClutterActor *texture;
  ClutterActor *clone;

  gint x;
  gint y;

  guint32 initial_motion_time;
  gint   initial_click_x;
  gint   initial_click_y;

  gboolean can_fav;
};

static inline gint
diff (gint x, gint y)
{
  if (x > y)
    return x - y;
  else
    return -1 * (x - y);
}

static gboolean
on_motion (ClutterActor       *actor,
           ClutterMotionEvent *event,
           LauncherIcon       *icon)
{
  LauncherIconPrivate *priv;
  ClutterActor *clone;
  
  g_return_val_if_fail (LAUNCHER_IS_ICON (icon), FALSE);
  priv = icon->priv;

  if (CLUTTER_IS_ACTOR (priv->clone))
  {
    g_debug ("Drag already initiated");
    return FALSE;
  }

  if (event->modifier_state & CLUTTER_BUTTON1_MASK 
        && !CLUTTER_IS_ACTOR (priv->clone))
  {
    if (event->time - priv->initial_motion_time > 1000)
    {
      priv->initial_motion_time = event->time;
      priv->initial_click_x = event->x;
      priv->initial_click_y = event->y;
      return TRUE;
    }
    else
    {
      if (diff (event->x, priv->initial_click_x) >= DRAG_RADIUS
          || diff (event->y, priv->initial_click_y) >= DRAG_RADIUS)
      {
        priv->initial_motion_time = 0;
      }
      else
      {
        return TRUE;
      }
    }
    
    priv->clone = clone = clutter_clone_texture_new 
                                             (CLUTTER_TEXTURE (priv->texture));
    clutter_actor_set_size (priv->clone,CAW(priv->texture), CAH(priv->texture));
    clutter_container_add_actor (CLUTTER_CONTAINER(clutter_stage_get_default()),
                               priv->clone);  
    clutter_actor_set_position (clone, event->x, event->y);
    clutter_actor_show (clone);

    clutter_drag_server_begin_drag (clutter_drag_server_get_default(),
                                    clone,
                                    priv->app);
   return TRUE;
  }

  return FALSE;
}


static void
position_func (GtkMenu  *menu, 
               gint     *x,
               gint     *y, 
               gboolean *push_in,
               gpointer  data)
{
  LauncherIconPrivate *priv;

  priv = LAUNCHER_ICON_GET_PRIVATE (data);

  *x = priv->x;
  *y = priv->y;
  *push_in = TRUE;
}

static void
launch_app (GtkMenuItem *item, LauncherIcon *icon)
{
  LauncherIconPrivate *priv;
  LauncherConfig *cfg = launcher_config_get_default ();
  
  g_return_if_fail (LAUNCHER_IS_ICON (icon));
  priv = icon->priv;

  if (launcher_startup_launch_app (launcher_startup_get_default (),
                                   priv->app))
  {
    launcher_notify_popup (launcher_notify_get_default (),
                           launcher_menu_application_get_name (priv->app),
                           CLUTTER_TEXTURE (priv->texture),
                           launcher_menu_application_get_pid (priv->app));
  }

  clutter_effect_rotate (priv->temp,
                         CLUTTER_ACTOR (icon),
                         CLUTTER_Y_AXIS,
                         0.0,
                         cfg->icon_width/2, 0, 0, 
                         CLUTTER_ROTATE_CW,
                         NULL, NULL);
}

static void
launch_new_app (LauncherIcon *icon)
{
  launcher_menu_application_set_pid (icon->priv->app, -1);
  launch_app (NULL, icon);
}

static void
remove_app (GtkMenuItem *item, LauncherIcon *icon)
{
  LauncherIconPrivate *priv;

  g_return_if_fail (LAUNCHER_IS_ICON (icon));
  priv = icon->priv;

  launcher_util_remove_favorite (priv->app);
}

static void
fav_app (GtkMenuItem *item, LauncherIcon *icon)
{
  LauncherIconPrivate *priv;

  g_return_if_fail (LAUNCHER_IS_ICON (icon));
  priv = icon->priv;

  launcher_util_create_favorite (priv->app);
}

static void
show_menu (LauncherIcon *icon, ClutterButtonEvent *event)
{
  LauncherIconPrivate *priv;
  GtkWidget *menu, *item;
  LauncherMenuCategory *cat;
  const gchar *icon_name;
  
  g_return_if_fail (LAUNCHER_IS_ICON (icon));
  priv = icon->priv;

  priv->x = event->x;
  priv->y = event->y;  
 
  menu = gtk_menu_new ();

  item = gtk_image_menu_item_new_from_stock (GTK_STOCK_EXECUTE, NULL);
  gtk_widget_show (item);
  g_signal_connect (item, "activate", G_CALLBACK (launch_app), icon);
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);

  cat = launcher_menu_application_get_category (priv->app);
  icon_name = launcher_menu_category_get_icon_name (cat);
  if (icon_name && strcmp (icon_name, FAVCATNAME) == 0)
  {
    item = gtk_image_menu_item_new_from_stock (GTK_STOCK_REMOVE, NULL);
    gtk_widget_show (item);
    g_signal_connect (item, "activate", G_CALLBACK (remove_app), icon);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
  }
  else if (!priv->can_fav)
  {

  }
  else
  {
    item = gtk_image_menu_item_new_with_label (_("Add to Favorites"));
    g_object_set (item, "image", gtk_image_new_from_stock (GTK_STOCK_ADD,
                                                           GTK_ICON_SIZE_MENU),
                  NULL);
    gtk_widget_show (item);
    g_signal_connect (item, "activate", G_CALLBACK (fav_app), icon);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);  
  }

  gtk_menu_popup (GTK_MENU (menu),
                  NULL, NULL, 
                  position_func, icon,
                  3, event->time); 
}

static gboolean
on_app_clicked (ClutterActor       *actor,
                ClutterButtonEvent *event,
                LauncherIcon       *icon)
{
  LauncherIconPrivate *priv;
  
  g_return_val_if_fail (LAUNCHER_IS_ICON (icon), FALSE);
  priv = icon->priv;

  if (CLUTTER_IS_ACTOR (priv->clone))
  {
    clutter_drag_server_finish_drag (clutter_drag_server_get_default ());
    return FALSE;
  }

  if (event->button == 3)
  {
    show_menu (icon, event);
    return TRUE;
  }
  else if (event->button == 2)
  {
    launch_new_app (icon);
    return TRUE;
  }
  else if (event->button == 1)
  {
    launch_app (NULL, icon);
    return TRUE;
  }

  return FALSE;
}

static gboolean
on_enter (ClutterActor          *actor,
          ClutterCrossingEvent  *event,
          LauncherIcon          *icon)
{
  LauncherIconPrivate *priv;
  LauncherConfig *cfg = launcher_config_get_default ();

  g_return_val_if_fail (LAUNCHER_IS_ICON (icon), FALSE);
  priv = icon->priv;

  if (CLUTTER_IS_TIMELINE (priv->current) 
      && clutter_timeline_is_playing (priv->current))
  {
    clutter_timeline_stop (priv->current);
    if (G_IS_OBJECT (priv->current)) 
      g_object_unref (priv->current);
  } 

  clutter_label_set_color (CLUTTER_LABEL (priv->label), &orange);  
  
  if (cfg->low_graphics)
    clutter_actor_set_scale (priv->texture, 1.2, 1.2);
  else
    priv->current = clutter_effect_scale (priv->fast_temp, priv->texture, 
                                          1.2, 1.2, NULL, NULL);
  return TRUE;
}


static gboolean
on_leave (ClutterActor          *actor,
          ClutterCrossingEvent  *event,
          LauncherIcon          *icon)
{
  LauncherIconPrivate *priv;
  LauncherConfig *cfg = launcher_config_get_default ();

  g_return_val_if_fail (LAUNCHER_IS_ICON (icon), FALSE);
  priv = icon->priv;

  if (CLUTTER_IS_TIMELINE (priv->current) 
      && clutter_timeline_is_playing (priv->current))
  {
    clutter_timeline_stop (priv->current);
    if (G_IS_OBJECT (priv->current)) 
      g_object_unref (priv->current);
  } 
 
  clutter_label_set_color (CLUTTER_LABEL (priv->label), &white);

  if (cfg->low_graphics)
    clutter_actor_set_scale (priv->texture, 1.0, 1.0);
  else
    priv->current = clutter_effect_scale (priv->fast_temp, priv->texture, 
                                        1.0, 1.0, NULL, NULL);

  return TRUE;
}

/*
 * Make sure the label doesn't span more the two lines...the nasty way!
 * Blame http://bugzilla.openedhand.com/show_bug.cgi?id=918
 */
static gboolean
clip_label (ClutterLabel *label, const gchar *text, gint len, gint real_width)
{
  return FALSE;
  PangoLayout *layout = clutter_label_get_layout (label);
  gint lines;
  gint max_lines = 3;
  gboolean touched = FALSE; 

  gchar buf[len];

  memcpy (buf, text, len+1);
    
  lines = pango_layout_get_line_count (layout);  
  while (lines > max_lines)
  {
    *g_utf8_prev_char (&buf[len]) = '\0';
    clutter_label_set_text (label, buf);
    len -= 4;
    layout = clutter_label_get_layout (label);
    lines = pango_layout_get_line_count (layout);
    touched = TRUE;
  }

  return touched;
}


void 
launcher_icon_set_application (LauncherIcon            *icon, 
                               LauncherMenuApplication *application)
{
  LauncherIconPrivate *priv;
  LauncherConfig *cfg = launcher_config_get_default ();
  GdkPixbuf *pixbuf;
  ClutterActor *texture;
  ClutterActor *label, *new;;
  gint width = cfg->icon_width;
  //gint height = cfg->icon_height;
  gint padding = width * 0.1;
 
  g_return_if_fail (LAUNCHER_IS_ICON (icon));
  priv = icon->priv;

  priv->app = application;
  priv->can_fav = application->can_fav;

  /* Icon */
  pixbuf = launcher_menu_application_get_icon (application);
  texture = priv->texture = gtk_clutter_texture_new_from_pixbuf (pixbuf);
  clutter_actor_set_size (texture, 
                          width-(6*padding), 
                          width-(6*padding));
  clutter_actor_set_anchor_point_from_gravity (texture, CLUTTER_GRAVITY_CENTER);
  clutter_actor_set_position (texture, width/2, padding/1.5 + CAH (texture)/2);
  clutter_container_add_actor (CLUTTER_CONTAINER (icon), texture);
  clutter_actor_show (texture);

  /* Text label */
  label = priv->label = clutter_label_new ();
  clutter_label_set_font_name (CLUTTER_LABEL (label), cfg->font_name);
  clutter_label_set_color (CLUTTER_LABEL (label), &white);
  clutter_label_set_text (CLUTTER_LABEL (label),
                          launcher_menu_application_get_name (application));

  clutter_label_set_line_wrap (CLUTTER_LABEL (label), TRUE);
  clutter_label_set_alignment (CLUTTER_LABEL (label), PANGO_ALIGN_CENTER);
  //clutter_label_set_justify (CLUTTER_LABEL (label), TRUE);
  if (CAW (label) > width)
    clutter_actor_set_width (label, width);
  clutter_container_add_actor (CLUTTER_CONTAINER (icon), label);
  clutter_actor_set_position (label, 
                              (gint)((width/2)-(CAW (label)/2)),
                     (gint)(clutter_actor_get_y (texture) + CAH(texture)/1.5));
  clutter_actor_show (label);

  clip_label (CLUTTER_LABEL (label),
              launcher_menu_application_get_name (application),
              strlen (launcher_menu_application_get_name (application)),
              width + padding);

  /* 'New' indicator */
  if (launcher_menu_application_is_new (application))
  {
    new = launcher_util_texture_new_from_named_icon ("emblem-new");
    clutter_container_add_actor (CLUTTER_CONTAINER (icon), new);
    clutter_actor_set_size (new, 
                            2*padding,
                            2*padding);
    clutter_actor_set_position (new, (width*0.75) - (2*padding), padding/2);
    clutter_actor_show (new);
  }


  clutter_actor_set_reactive (CLUTTER_ACTOR (icon), TRUE);
  g_signal_connect (icon, "button-release-event",
                    G_CALLBACK (on_app_clicked), icon);
  g_signal_connect (icon, "enter-event",
                    G_CALLBACK (on_enter), icon);
  g_signal_connect (icon, "leave-event",
                    G_CALLBACK (on_leave), icon);
  clutter_actor_set_reactive (priv->texture, TRUE);

  if (application->can_fav)
    g_signal_connect (icon, "motion-event", 
                      G_CALLBACK (on_motion), icon);
}

void 
launcher_icon_set_focus (LauncherIcon *icon, gboolean has_focus)
{
  LauncherIconPrivate *priv;
    
  g_return_if_fail (LAUNCHER_IS_ICON (icon));
  priv = icon->priv;

  if (has_focus)
  {
    clutter_label_set_color (CLUTTER_LABEL (priv->label), &orange);
  }
  else
  {
    clutter_label_set_color (CLUTTER_LABEL (priv->label), &white);
  }
}

void 
launcher_icon_launch (LauncherIcon *icon)
{
  launch_app (NULL, icon);
}


/* GObject stuff */
static void
launcher_icon_finalize (GObject *icon)
{
  LauncherIconPrivate *priv;

  g_return_if_fail (LAUNCHER_IS_ICON (icon));
  priv = LAUNCHER_ICON_GET_PRIVATE (icon);

  G_OBJECT_CLASS (launcher_icon_parent_class)->finalize (icon);
}

static void
launcher_icon_class_init (LauncherIconClass *klass)
{
  GObjectClass        *obj_class = G_OBJECT_CLASS (klass);
  
  obj_class->finalize = launcher_icon_finalize;

  g_type_class_add_private (obj_class, sizeof (LauncherIconPrivate));
}

      
static void
launcher_icon_init (LauncherIcon *icon)
{
  LauncherIconPrivate *priv;
  
  priv = icon->priv = LAUNCHER_ICON_GET_PRIVATE (icon);

  if (!CLUTTER_IS_ACTOR (bg_texture))
  {
    GdkPixbuf *temp;

    temp = gdk_pixbuf_new_from_file (PKGDATADIR"/icon.png", NULL);
    if (temp)
    {
        bg_texture = gtk_clutter_texture_new_from_pixbuf (temp);
        clutter_actor_realize (bg_texture);
        g_object_unref (temp);
    }
  }

  priv->time = clutter_timeline_new_for_duration (SLOW_TIME);
  priv->temp = clutter_effect_template_new (priv->time, 
                                            clutter_sine_inc_func);

  priv->fast_time = clutter_timeline_new_for_duration (MID_TIME);
  priv->fast_temp = clutter_effect_template_new (priv->fast_time,
                                                 clutter_sine_inc_func);
}

ClutterActor *
launcher_icon_new (void)

{
  ClutterActor *icon = NULL;

  icon = g_object_new (LAUNCHER_TYPE_ICON, 
                           NULL);

  return icon;
}
