/* -*- Mode: C; indent-tabs-mode: nil; tab-width: 2 -*-
 *
 * Copyright (C) 2009 Canonical Ltd.
 * Authors:
 *  Gustavo Sverzut Barbieri <gustavo.barbieri@canonical.com>
 *
 * This file is part of Netbook Launcher EFL.
 *
 * Netbook Launcher EFL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * Netbook Launcher EFL 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 Netbook Launcher EFL.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "netbook-launcher.h"

#include <Elementary.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>

static const char _EDJE_GROUP_ITEM[] = "e/netbook/launcher/main/places/recent/item";
static const char _EDJE_PART_CONTENTS[] = "e.box.contents";
static const char _EDJE_PART_ICON[] = "e.swallow.icon";
static const char _EDJE_PART_LABEL[] = "e.text.label";

static const char _EDJE_SIG_SRC[] = "e";
static const char _EDJE_SIG_SELECT[] = "gui,action,select";
static const char _EDJE_SIG_UNSELECT[] = "gui,action,unselect";
static const char _EDJE_SIG_FOCUS_OUT[] = "gui,action,focus,out";
static const char _EDJE_SIG_FOCUS_IN[] = "gui,action,focus,in";
static const char _EDJE_SIG_EXECUTED[] = "gui,action,executed";

struct places_recent_context
{
  Evas_Object *section;
  Evas_Object *parent;
  struct item_size item;
  GtkRecentManager *manager;
  struct
  {
    gulong changed;
  } notify;
};

struct places_recent_item_data
{
  struct places_item_common base;
  struct places_recent_context *ctxt;
  const char *name;
  const char *icon_name;
};

static void
_places_recent_calc_sizes(struct places_recent_context *ctxt)
{
  ctxt->item.icon_size = 32;
  ctxt->item.w = 50;
  ctxt->item.h = 50;
  _nl_edje_item_size_calc(&ctxt->item, ctxt->section, _EDJE_GROUP_ITEM);
}

static void
_places_recent_item_in(void *data, Evas_Object *obj, const char *signal __UNUSED__, const char *source __UNUSED__)
{
  struct places_recent_item_data *id = data;
  const char *str = edje_object_data_get(obj, "selectraise");
  if (str && (strcmp(str, "on") == 0))
    evas_object_raise(id->base.obj);
  places_item_common_select(id->ctxt->parent, &id->base);
}

static void
_places_recent_item_out(void *data, Evas_Object *obj __UNUSED__, const char *signal __UNUSED__, const char *source __UNUSED__)
{
  struct places_recent_item_data *id = data;
  places_item_common_unselect(id->ctxt->parent, &id->base);
}

static void
_places_recent_ecore_item_error(unsigned int len, const char **strings)
{
  const char *name, *message;
  if (len != 2)
  {
    ERR("2 strings expected, got %u\n", len);
    return;
  }
  name = strings[0];
  message = strings[1];
  launchfeedback_hide();
  error_show(_("Could not open '%s': %s"), name, message);
}

static void
_places_recent_glib_item_select(void *data __UNUSED__, const char *uri)
{
  GError *err = NULL;

  if (!g_app_info_launch_default_for_uri(uri, NULL, &err))
  {
    const char *strings[3];
    strings[0] = uri;
    strings[1] = err ? err->message : _("Unknown error");
    strings[2] = NULL;
    gstuff_ecore_run_string_array(_places_recent_ecore_item_error, 2, strings);
    if (err) g_error_free(err);
    return;
  }
}

static void
_places_recent_item_select(void *data, Evas_Object *obj __UNUSED__, const char *signal __UNUSED__, const char *source __UNUSED__)
{
  struct places_recent_item_data *id = data;

  DBG("folder activate: %s (%s)\n", id->name, id->base.id);
  launchfeedback_show(id->icon_name, id->name, 2.0);
  gstuff_glib_run_ptr_string
    (_places_recent_glib_item_select, id->ctxt, id->base.id);
  sound_play(id->base.obj, "button-pressed");
  sound_play(id->base.obj, "button-released");
}

static void
_places_recent_item_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
  struct places_recent_item_data *id = data;
  DBG("folder del: '%s' '%s' '%s'\n", id->name, id->icon_name, id->base.id);
  places_navigation_item_del(id->ctxt->parent, &id->base);
  eina_stringshare_del(id->name);
  eina_stringshare_del(id->icon_name);
  eina_stringshare_del(id->base.id);
  free(id);
}

static void
_places_recent_item_common_select(struct places_item_common *common)
{
  struct places_recent_item_data *id;

  edje_object_signal_emit(common->obj, _EDJE_SIG_SELECT, _EDJE_SIG_SRC);

  id = (struct places_recent_item_data *)common;
  _nl_icon_info_event_enter(id->name, id->icon_name, _("recent file"));
}

static void
_places_recent_item_common_unselect(struct places_item_common *common)
{
  edje_object_signal_emit(common->obj, _EDJE_SIG_UNSELECT, _EDJE_SIG_SRC);
  _nl_icon_info_event_leave();
}

static void
_places_recent_item_common_key_down(struct places_item_common *common, Evas_Event_Key_Down *ev)
{
  if (strcmp(ev->keyname, "Return") == 0)
    _places_recent_item_select(common, NULL, NULL, NULL);
}

static void
_places_recent_ecore_changed(void *data, unsigned int len, const char **strings)
{
  struct places_recent_context *ctxt = data;
  Evas_Object *ed;
  unsigned int i;

  ed = elm_layout_edje_get(ctxt->section);
  if (!edje_object_part_box_remove_all(ed, _EDJE_PART_CONTENTS, 1))
  {
    ERR("could not clear folders section\n");
    return;
  }
  evas_object_size_hint_min_set(ctxt->section, 0, 0);

  for (i = 0; i < len; i += 3)
  {
    Evas_Object *item, *icon, *item_ed;
    const char *name, *uri, *icon_name;
    struct places_recent_item_data *id;

    name = strings[i];
    uri = strings[i + 1];
    icon_name = strings[i + 2];

    DBG("folder add: '%s' '%s' '%s'\n", name, uri, icon_name);

    id = malloc(sizeof(*id));
    if (!id)
    {
      ERR("could not allocate places_recent_item_data\n");
      continue;
    }
    id->ctxt = ctxt;
    id->name = eina_stringshare_add(name);
    id->icon_name = eina_stringshare_add(icon_name);
    id->base.id = eina_stringshare_add(uri);
    id->base.select = _places_recent_item_common_select;
    id->base.unselect = _places_recent_item_common_unselect;
    id->base.key_down = _places_recent_item_common_key_down;

    icon = _nl_icon_load_sized(ctxt->section, icon_name, ctxt->item.icon_size);

    item = elm_layout_add(ctxt->section);
    if (!item)
      goto error;
    if (!elm_layout_file_set(item, _nl_prefs.theme, _EDJE_GROUP_ITEM))
      goto error;

    if (icon)
      elm_layout_content_set(item, _EDJE_PART_ICON, icon);

    item_ed = elm_layout_edje_get(item);
    edje_object_part_text_set(item_ed, _EDJE_PART_LABEL, name);

    evas_object_event_callback_add
      (item, EVAS_CALLBACK_FREE, _places_recent_item_del, id);

    evas_object_resize(item, ctxt->item.w, ctxt->item.h);
    evas_object_show(item);

    edje_object_signal_callback_add
      (item_ed, _EDJE_SIG_FOCUS_IN, _EDJE_SIG_SRC,
       _places_recent_item_in, id);
    edje_object_signal_callback_add
      (item_ed, _EDJE_SIG_FOCUS_OUT, _EDJE_SIG_SRC,
       _places_recent_item_out, id);
    edje_object_signal_callback_add
      (item_ed, _EDJE_SIG_EXECUTED, _EDJE_SIG_SRC,
       _places_recent_item_select, id);

    id->base.obj = item_ed;

    edje_object_part_box_append(ed, _EDJE_PART_CONTENTS, item);
    places_navigation_item_add(ctxt->parent, &id->base);
    continue;

  error:
    if (item)
      evas_object_del(item);
    if (icon)
      evas_object_del(icon);
    eina_stringshare_del(id->name);
    eina_stringshare_del(id->icon_name);
    eina_stringshare_del(id->base.id);
    free(id);
  }
}

static void
_places_recent_glib_changed(GtkRecentManager *manager, struct places_recent_context *ctxt)
{
  GList *lst, *n;
  unsigned int i, len;
  const char **strings;

  lst = gtk_recent_manager_get_items(manager);
  len = g_list_length(lst);

  strings = alloca((len * 3 + 1) * sizeof(char *));
  if (!strings)
  {
    g_critical("could not allocate strings array on stack.");
    goto end;
  }

  for (i = 0, n = lst; n != NULL; n = n->next)
  {
    GtkRecentInfo *info = n->data;
    const char *icon_name;
    unsigned int j;

    if (!gtk_recent_info_exists(info))
    {
      INF("ignore recent info: '%s' does not exist (%s)",
          gtk_recent_info_get_display_name(info),
          gtk_recent_info_get_uri(info));
      continue;
    }

    strings[i + 0] = gtk_recent_info_get_display_name(info);
    strings[i + 1] = gtk_recent_info_get_uri(info);

    icon_name = icon_mime_type_path_get
      (gtk_recent_info_get_mime_type(info), ctxt->item.icon_size);
    if (!icon_name)
      icon_name = icon_mime_type_path_get
        ("application/octet-stream", ctxt->item.icon_size);
    strings[i + 2] = icon_name;

    for (j = 0; j < 3; j++)
      if (!strings[i + j])
        strings[i + j] = "";

    i += 3;
  }
  strings[i] = NULL;

  gstuff_ecore_run_ptr_string_array
    (_places_recent_ecore_changed, ctxt, i, strings);

end:
  while (lst)
  {
    gtk_recent_info_unref(lst->data);
    lst = g_list_delete_link(lst, lst);
  }
}

static void
_places_recent_glib_start(void *data)
{
  struct places_recent_context *ctxt = data;

  ctxt->manager = gtk_recent_manager_get_default();
  if (!ctxt->manager)
  {
    g_critical("could not get recent manager.");
    return;
  }

  ctxt->notify.changed = g_signal_connect
    (ctxt->manager, "changed", G_CALLBACK(_places_recent_glib_changed), ctxt);

  _places_recent_glib_changed(ctxt->manager, ctxt);
}

static void
_places_recent_glib_stop(void *data)
{
  struct places_recent_context *ctxt = data;

  if (ctxt->notify.changed)
    g_signal_handler_disconnect(ctxt->manager, ctxt->notify.changed);

  free(ctxt);
}

static void
_places_recent_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
{
  struct places_recent_context *ctxt = data;
  ctxt->section = NULL;
  gstuff_glib_run_simple(_places_recent_glib_stop, ctxt);
}

Evas_Object *
places_recent_add(Evas_Object *parent)
{
  struct places_recent_context *ctxt;
  Evas_Object *section;

  section = places_section_add(parent, _("Recent Files"));
  if (!section)
  {
    ERR("could not create places folders section.\n");
    return NULL;
  }

  ctxt = calloc(1, sizeof(*ctxt));
  if (!ctxt)
  {
    evas_object_del(section);
    return NULL;
  }

  ctxt->section = section;
  ctxt->parent = parent;
  _places_recent_calc_sizes(ctxt);

  evas_object_event_callback_add
    (section, EVAS_CALLBACK_FREE, _places_recent_del, ctxt);

  gstuff_glib_run_simple(_places_recent_glib_start, ctxt);

  return section;
}
