/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * Copyright 2009 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/>
 *
 * Authored by: Gordon Allott <gord.allott@canonical.com>
 *
 */
#if HAVE_CONFIG_H
#include <config.h>
#endif

#include "ctk-menu-item.h"
#include <glib.h>
#include <math.h>
G_DEFINE_TYPE (CtkMenuItem, ctk_menu_item, CTK_TYPE_BIN);

#define CTK_MENU_ITEM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
  CTK_TYPE_MENU_ITEM, \
  CtkMenuItemPrivate))

struct _CtkMenuItemPrivate
{
  ClutterActor *text;
  ClutterColor color;

  gboolean entered;
  gboolean pressed;
  gint32 last_press_time;
};

enum
{
  PROP_0,

  PROP_LABEL
};

enum
{
  ACTIVATED,

  LAST_SIGNAL
};
static guint menu_item_signals[LAST_SIGNAL] = { 0 };

/* Forwards */
static gboolean on_button_release        (ClutterActor         *actor,
                                          ClutterButtonEvent   *event);
static gboolean on_button_press          (ClutterActor         *actor,
                                          ClutterButtonEvent   *event);
static gboolean on_enter          (ClutterActor         *actor,
                                   ClutterCrossingEvent *event);
static gboolean on_leave          (ClutterActor         *actor,
                                   ClutterCrossingEvent *event);


static void
ctk_menu_item_init (CtkMenuItem *object)
{
  CtkMenuItemPrivate *priv;

  priv = object->priv = CTK_MENU_ITEM_GET_PRIVATE (object);
  priv->text = clutter_text_new ();
  clutter_text_set_text (CLUTTER_TEXT (priv->text),
                         ctk_menu_item_get_label (object));

  clutter_container_add_actor (CLUTTER_CONTAINER (object), priv->text);
  clutter_actor_show (priv->text);
  clutter_actor_set_reactive (CLUTTER_ACTOR (object), TRUE);

  priv->color.red = priv->color.green = priv->color.blue = 0xFF;
  priv->color.alpha = 0xFF;
  clutter_text_set_color (CLUTTER_TEXT (priv->text), &priv->color);

  priv->entered = FALSE;
  priv->pressed = FALSE;

  ClutterActor *sel_bg = clutter_rectangle_new ();
  clutter_rectangle_set_color (CLUTTER_RECTANGLE (sel_bg), &priv->color);
  ctk_actor_set_background_for_state (CTK_ACTOR (object), CTK_STATE_PRELIGHT,
                                      sel_bg);
}

static void
ctk_menu_item_finalize (GObject *object)
{
  CtkMenuItemPrivate *priv = CTK_MENU_ITEM (object)->priv;

  if (priv->text)
    {
      clutter_actor_unparent (priv->text);
      priv->text = NULL;
    }

  G_OBJECT_CLASS (ctk_menu_item_parent_class)->finalize (object);
}

static void
ctk_menu_item_set_property (GObject *object, guint prop_id, const GValue *value,
                            GParamSpec *pspec)
{
  g_return_if_fail (CTK_IS_MENU_ITEM (object));

  switch (prop_id)
  {
  case PROP_LABEL:
    ctk_menu_item_set_label (CTK_MENU_ITEM (object), g_value_dup_string(value));
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    break;
  }
}

static void
ctk_menu_item_get_property (GObject *object, guint prop_id, GValue *value,
                            GParamSpec *pspec)
{
  g_return_if_fail (CTK_IS_MENU_ITEM (object));

  switch (prop_id)
  {
  case PROP_LABEL:
    {
      const gchar *str;
      str = ctk_menu_item_get_label (CTK_MENU_ITEM (object));
      g_value_set_string(value, str);
    }
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    break;
  }
}

static void
ctk_menu_item_class_init (CtkMenuItemClass *klass)
{
  GObjectClass* object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = ctk_menu_item_finalize;
  object_class->set_property = ctk_menu_item_set_property;
  object_class->get_property = ctk_menu_item_get_property;

  ClutterActorClass *act_class = CLUTTER_ACTOR_CLASS (klass);

  act_class->button_release_event = on_button_release;
  act_class->button_press_event   = on_button_press;
  act_class->enter_event          = on_enter;
  act_class->leave_event          = on_leave;

  g_object_class_install_property (object_class,
                                   PROP_LABEL,
                                   g_param_spec_string ("label",
                                                        "label",
                                                        "The label for the menu item",
                                                        "",
                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));

  menu_item_signals[ACTIVATED] =
    g_signal_new ("activated",
                  G_OBJECT_CLASS_TYPE (klass),
                  0,
                  G_STRUCT_OFFSET (CtkMenuItemClass, activated),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);


  g_type_class_add_private (klass, sizeof (CtkMenuItemPrivate));
}

/*
 * Private methods
 */
static gboolean
on_button_release (ClutterActor         *actor,
                   ClutterButtonEvent   *event)
{
#define CLICK_TIMEOUT 500
  CtkMenuItemPrivate *priv;

  g_return_val_if_fail (CTK_IS_MENU_ITEM (actor), FALSE);
  priv = CTK_MENU_ITEM (actor)->priv;

  priv->pressed = FALSE;
  ctk_actor_set_state (CTK_ACTOR (actor), CTK_STATE_PRELIGHT);

  if (event->button == 1
      && ((event->time - priv->last_press_time) < CLICK_TIMEOUT))
    {
      g_signal_emit (actor, menu_item_signals[ACTIVATED], 0);
      return TRUE;
    }

  return FALSE;
}

static gboolean
on_button_press (ClutterActor       *actor,
                 ClutterButtonEvent *event)
{
  CtkMenuItemPrivate *priv;

  g_return_val_if_fail (CTK_IS_MENU_ITEM (actor), FALSE);
  priv = CTK_MENU_ITEM (actor)->priv;

  priv->pressed = TRUE;

  ctk_actor_set_state (CTK_ACTOR (actor), CTK_STATE_ACTIVE);

  if (event->button == 1)
    {
      priv->last_press_time = event->time;
    }

  return FALSE;
}

static gboolean
on_enter (ClutterActor         *actor,
          ClutterCrossingEvent *event)
{
  CtkMenuItemPrivate *priv;

  g_return_val_if_fail (CTK_IS_MENU_ITEM (actor), FALSE);
  priv = CTK_MENU_ITEM (actor)->priv;

  CLUTTER_ACTOR_CLASS (ctk_menu_item_parent_class)->enter_event (actor, event);

  priv->entered = TRUE;

  ctk_actor_set_state (CTK_ACTOR (actor), CTK_STATE_PRELIGHT);
  ClutterColor new_color = {0x00, 0x00, 0x00, 0xFF};
  clutter_actor_animate (priv->text, CLUTTER_EASE_OUT_SINE, 160,
                         "color", &new_color,
                         NULL);

  return FALSE;
}

static gboolean
on_leave (ClutterActor         *actor,
          ClutterCrossingEvent *event)
{
  CtkMenuItemPrivate *priv;

  g_return_val_if_fail (CTK_IS_MENU_ITEM (actor), FALSE);
  priv = CTK_MENU_ITEM (actor)->priv;

  CLUTTER_ACTOR_CLASS (ctk_menu_item_parent_class)->leave_event (actor, event);

  priv->entered = FALSE;

  ctk_actor_set_state (CTK_ACTOR (actor), CTK_STATE_NORMAL);
  clutter_actor_animate (priv->text, CLUTTER_EASE_OUT_SINE, 160,
                         "color", &priv->color,
                         NULL);

  return FALSE;
}

/* constructors */
CtkMenuItem *
ctk_menu_item_new (void)
{
  CtkMenuItem *item;
  item = g_object_new (CTK_TYPE_MENU_ITEM,
                       "label", "",
                       NULL);
  return item;
}

CtkMenuItem *
ctk_menu_item_new_with_label (const gchar *label)
{
  CtkMenuItem *item;
  item = g_object_new (CTK_TYPE_MENU_ITEM,
                       "label", label,
                       NULL);
  return item;
}


void
ctk_menu_item_set_label (CtkMenuItem *item, const gchar *label)
{
  g_return_if_fail (CTK_IS_MENU_ITEM (item));

  if (item->priv->text)
    {
      clutter_text_set_text (CLUTTER_TEXT (item->priv->text), label);
      clutter_actor_show (item->priv->text);
      clutter_actor_queue_relayout (CLUTTER_ACTOR (item));

      g_object_notify (G_OBJECT (item), "label");
    }
}

const gchar *
ctk_menu_item_get_label (CtkMenuItem *item)
{
  g_return_val_if_fail (CTK_IS_MENU_ITEM (item), NULL);

  if (item->priv->text)
    return clutter_text_get_text (CLUTTER_TEXT (item->priv->text));

  return NULL;
}
