#include "e_mod_main.h"

/*
  TODO
  - update on icon changes

*/

static void      _box_fill           (Ngi_Box *box);
static void      _app_change_cb      (void *data, E_Order *eo);

static void      _item_new           (Ngi_Box *box, Efreet_Desktop *dekstop, int instant, Ngi_Item_Launcher *after);
static void      _item_fill          (Ngi_Item_Launcher *it);

static void      _item_cb_free       (Ngi_Item *it);
static void      _item_cb_drag_start (Ngi_Item *it);
static void      _item_cb_mouse_up   (Ngi_Item *it, Ecore_Event_Mouse_Button *ev);
static void      _item_cb_mouse_down (Ngi_Item *it, Ecore_Event_Mouse_Button *ev);

static void      _cb_drop_enter      (void *data, const char *type, void *event_info);
static void      _cb_drop_leave      (void *data, const char *type, void *event_info);
static void      _cb_drop_move       (void *data, const char *type, void *event_info);
static void      _cb_drop_end        (void *data, const char *type, void *event_info);
static Eina_Bool  _cb_icons_update(void *data, int type, void *event);


void
ngi_launcher_new(Ng *ng, Config_Box *cfg)
{
   char buf[4096];
   Ecore_Event_Handler *h;
   Ngi_Box *box = ngi_box_new(ng);

   box->cfg = cfg;
   cfg->box = box;

   const char *drop[] =
     { "enlightenment/desktop",
       "enlightenment/border",
       "text/uri-list" };

   box->drop_handler = e_drop_handler_add
     (ng->win->drop_win, box,
      _cb_drop_enter, _cb_drop_move,
      _cb_drop_leave, _cb_drop_end,
      drop, 3, 0, 0, 0, 0);

   h = ecore_event_handler_add(E_EVENT_CONFIG_ICON_THEME, _cb_icons_update, box);
   if (h) box->handlers = eina_list_append(box->handlers, h);

   if (!cfg->launcher_app_dir || strlen(cfg->launcher_app_dir) == 0)
     return;

   if (cfg->launcher_app_dir[0] != '/')
     {
        snprintf(buf, sizeof(buf), "%s/.e/e/applications/bar/%s/.order",
                 e_user_homedir_get(), cfg->launcher_app_dir);
     }
   else
     snprintf(buf, sizeof(buf), "%s", cfg->launcher_app_dir);

   box->apps = e_order_new(buf);
   e_order_update_callback_set(box->apps, _app_change_cb, box);

   _box_fill(box);
}

void
ngi_launcher_remove(Ngi_Box *box)
{
   Ecore_Event_Handler *h;
   
   e_drop_handler_del(box->drop_handler);

   if (box->apps)
     {
        e_order_update_callback_set(box->apps, NULL, NULL);
        e_object_del(E_OBJECT(box->apps));
     }

   EINA_LIST_FREE(box->handlers, h)
     ecore_event_handler_del(h);

   ngi_box_free(box);
}

static void
_box_fill(Ngi_Box *box)
{
   Efreet_Desktop *desktop;
   Eina_List *l;

   if (!box->apps)
     return;

   ngi_freeze(box->ng);

   EINA_LIST_FOREACH( box->apps->desktops, l, desktop)
     _item_new(box, desktop, 1, NULL);

   ngi_thaw(box->ng);
}

static Eina_Bool
_cb_icons_update(void *data, int type, void *event)
{
   Ngi_Box *box = (Ngi_Box *)data;
   Eina_List *l;
   Ngi_Item_Launcher *it;
   printf(">>>>>>>> icons update %s\n", e_config->icon_theme);

   EINA_LIST_FOREACH(box->items, l, it)
     _item_fill(it); 
   
   return ECORE_CALLBACK_PASS_ON;
}

static void
_app_change_cb(void *data, E_Order *eo)
{
   Ngi_Box *box = (Ngi_Box *)data;

   if (box->item_drop)
     return;

   while(box->items)
     ngi_item_free(box->items->data);

   _box_fill(box);
}

/* ******************************* LAUNCHER DROP HANDLER ******************** */

static Ngi_Item *
_drop_item_new(Ngi_Box *box)
{
   Ngi_Item *it;

   it = (Ngi_Item*)E_NEW(Ngi_Item_Launcher, 1);
   it->box = box;
   ngi_item_show(it, 0);
   it->delete_me = EINA_TRUE;
   
   return it;
}

static void
_drop_handle_move(Ngi_Box *box, int x, int y)
{
   Ngi_Item *item;
   Ngi_Item_Launcher *it, *it2;
   Eina_List *l;

   if (box->ng->horizontal)
     box->ng->pos = x + box->ng->size/2 - box->ng->zone->x;
   else
     box->ng->pos = y + box->ng->size/2 - box->ng->zone->y;

   item = ngi_item_at_position_get(box->ng);

   if (item && (item->box != box))
     {
	l = eina_list_last(box->items);
	if (l && (it = l->data) && (!it->app)) return;

	ngi_item_remove(box->item_drop);

   	box->item_drop = _drop_item_new(box);
   	box->items = eina_list_append(box->items, box->item_drop);
     }
   else if (item && item->box == box)
     {
	it = (Ngi_Item_Launcher*)item;
	l = eina_list_data_find_list(box->items, it);

   	if (l) it2 = eina_list_data_get(l->prev);

   	if (it->app && (!it2 || it2->app))
   	  {
	     ngi_item_remove(box->item_drop);

   	     box->item_drop = _drop_item_new(box);
   	     box->items = eina_list_prepend_relative(box->items, box->item_drop, it);
   	  }
     }
}

static void
_cb_drop_enter(void *data, const char *type, void *event_info)
{
   E_Event_Dnd_Enter *ev = (E_Event_Dnd_Enter *)event_info;
   Ngi_Box *box = (Ngi_Box *)data;

   if (!strcmp(type, "text/uri-list"))
     return;

   _drop_handle_move(box, ev->x, ev->y);

   box->ng->dnd = 1;
   ngi_mouse_in(box->ng);
}

static void
_cb_drop_move(void *data, const char *type, void *event_info)
{
   E_Event_Dnd_Move *ev = (E_Event_Dnd_Move *)event_info;
   Ngi_Box *box = (Ngi_Box *)data;

   if (!strcmp(type, "text/uri-list"))
     return;

   _drop_handle_move(box, ev->x, ev->y);

   ngi_thaw(box->ng);
}

static void
_cb_drop_leave(void *data, const char *type, void *event_info)
{
   Ngi_Box *box = (Ngi_Box *)data;

   if (!strcmp(type, "text/uri-list"))
     return;

   if (box->item_drop)
     ngi_item_remove(box->item_drop);
   box->item_drop = NULL;

   ngi_reposition(box->ng);
   ngi_input_extents_calc(box->ng);
   box->ng->dnd = 0;
   ngi_mouse_out(box->ng);
}

static void
_cb_drop_end(void *data, const char *type, void *event_info)
{
   E_Event_Dnd_Drop *ev = (E_Event_Dnd_Drop *)event_info;
   Ngi_Box *box = data;
   Efreet_Desktop *app = NULL;
   Ngi_Item_Launcher *it = NULL;

   Eina_List *l;
   if (!strcmp(type, "enlightenment/desktop"))
     {
        app = (Efreet_Desktop *)ev->data;
     }
   else if (!strcmp(type, "enlightenment/border"))
     {
        E_Border *bd = (E_Border *)ev->data;
        app = bd->desktop;

        if (!app)
          {
             app = e_desktop_border_create(bd);
             efreet_desktop_save(app);
             e_desktop_edit(e_container_current_get(e_manager_current_get()), app);
          }
     }
   else if (!strcmp(type, "text/uri-list"))
     return;

   if (!app)
     return;

   l = eina_list_data_find_list(box->items, box->item_drop);
   if (l) it = eina_list_data_get(l->next);

   if (box->item_drop)
     ngi_item_free(box->item_drop);

   if (it && it->app)
     e_order_prepend_relative(box->apps, app, it->app);
   else
     e_order_append(box->apps, app);
   /* _save_order(box); */

   _item_new(box, app, 1, it);

   box->ng->dnd = 0;
   ngi_reposition(box->ng);
   ngi_input_extents_calc(box->ng);
}

/* ******************************* LAUNCHER ITEM ********************************** */

static void
_item_new(Ngi_Box *box, Efreet_Desktop *desktop, int instant, Ngi_Item_Launcher *after)
{
   Ngi_Item_Launcher *it;

   it = E_NEW(Ngi_Item_Launcher, 1);
   it->base.box = box;
   ngi_item_init_defaults((Ngi_Item*)it);

   it->base.cb_free = _item_cb_free;
   it->base.cb_mouse_down = _item_cb_mouse_down;
   it->base.cb_mouse_up = _item_cb_mouse_up;
   it->base.cb_drag_start = _item_cb_drag_start;

   efreet_desktop_ref(desktop);
   it->app = desktop;

   _item_fill(it);

   if (after)
     box->items = eina_list_prepend_relative(box->items, it, after);
   else
     box->items = eina_list_append(box->items, it);

   ngi_item_show((Ngi_Item*)it, instant);
}

static void
_item_cb_free(Ngi_Item *item)
{
   Ngi_Item_Launcher *it = (Ngi_Item_Launcher *) item;

   if (it->app)
     efreet_desktop_unref(it->app);

   if (it->o_icon)
     evas_object_del(it->o_icon);

   if (it->o_proxy)
     evas_object_del(it->o_proxy);

   E_FREE(it);
}

static void
_item_fill(Ngi_Item_Launcher *it)
{
   Evas_Object *o;
   Evas *e = it->base.box->ng->evas;

   if (it->o_icon)
     {
	edje_object_part_unswallow(it->base.obj, it->o_icon);
	evas_object_del(it->o_icon);
     }
   if (it->o_proxy)
     {
	edje_object_part_unswallow(it->base.obj, it->o_proxy);
	evas_object_del(it->o_proxy);
     }

   o = e_util_desktop_icon_add(it->app, 128, e);
   if (o)
     {
	edje_object_part_swallow(it->base.obj, "e.swallow.content", o);
	evas_object_show(o);
	it->o_icon = o;
	
	o = evas_object_image_filled_add(e);
	evas_object_image_fill_set(o, 0, 0, 1, 1);
	evas_object_image_source_set(o, it->base.obj);

	edje_object_part_swallow(it->base.over, "e.swallow.content", o);
	evas_object_show(o);
	it->o_proxy = o;
     }
   
   if (it->app->name && it->app->name[0])
     ngi_item_label_set((Ngi_Item*)it, it->app->name);
   else if (it->app->generic_name && it->app->generic_name[0])
     ngi_item_label_set((Ngi_Item*)it, it->app->generic_name);
}

static void
_cb_drag_del(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Ngi_Box *box = data;

   ngi_bar_lock(box->ng, 0);
   ngi_thaw(box->ng);
}

static void
_cb_drag_finished(E_Drag *d, int dropped)
{
   efreet_desktop_unref((Efreet_Desktop*)d->data);
}

static void
_item_cb_drag_start(Ngi_Item *item)
{
   Ngi_Item_Launcher *it = (Ngi_Item_Launcher*) item;
   E_Drag *d;
   Evas_Object *o;
   Evas_Coord x, y, w, h, px, py;
   Ngi_Box *box = item->box;

   const char *drag_types[] = { "enlightenment/desktop" };

   if (item->box->cfg->launcher_lock_dnd)
     return;

   evas_object_geometry_get(it->o_icon, &x, &y, &w, &h);

   if (!ngi_config->use_composite)
     {
	x -= box->ng->win->rect.x;
	y -= box->ng->win->rect.y;
     }

   d = e_drag_new(box->ng->zone->container, x, y, drag_types, 1,
                  it->app, -1, NULL,
		  _cb_drag_finished);
   efreet_desktop_ref(it->app);

   o = e_util_desktop_icon_add(it->app, MIN(w, h), e_drag_evas_get(d));
   e_drag_object_set(d, o);
   e_drag_resize(d, w, h);
   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _cb_drag_del, box);

   e_order_remove(item->box->apps, it->app);

   evas_object_hide(it->base.obj);
   ngi_item_remove(&(it->base));

   ecore_x_pointer_xy_get(box->ng->win->input, &px, &py);
   e_drag_start(d, px, py);

   box->ng->item_drag = NULL;

   ngi_bar_lock(box->ng, 1);
   ngi_thaw(box->ng);
}

static void
_menu_cb_lock_dnd(void *data, E_Menu *m, E_Menu_Item *mi)
{
   Ngi_Box *box = (Ngi_Box *)data;

   if (box) box->cfg->launcher_lock_dnd = e_menu_item_toggle_get(mi);
}

static void
_menu_cb_edit_icon(void *data, E_Menu *m, E_Menu_Item *mi)
{
   Ngi_Item_Launcher *it = data;

   if (it) e_desktop_edit(e_container_current_get(e_manager_current_get()), it->app);
}

static void
_menu_cb_configure_bar(void *data, E_Menu *m, E_Menu_Item *mi)
{
   Ng *ng = (Ng *)data;

   if (ng) ngi_configure_module(ng->cfg);
}

static void
_menu_cb_configure_launcher(void *data, E_Menu *m, E_Menu_Item *mi)
{
   Ngi_Box *box = (Ngi_Box *)data;
   char path[4096];

   if (!box)
     return;

   snprintf(path, sizeof(path), "%s/.e/e/applications/bar/%s/.order",
            e_user_homedir_get(), box->cfg->launcher_app_dir);

   e_configure_registry_call("internal/ibar_other",
                             e_container_current_get(e_manager_current_get()),
                             path);
}

static void
_menu_cb_menu_end(void *data, E_Menu *m)
{
   Ng *ng = (Ng *)data;

   e_object_del(E_OBJECT(m));
   ngi_thaw(ng);
}

static void
_item_cb_mouse_down(Ngi_Item *item, Ecore_Event_Mouse_Button *ev)
{
   Ngi_Item_Launcher *it = (Ngi_Item_Launcher *) item;
   Evas_Coord x, y, w, h;
   E_Menu *m;
   E_Menu_Item *mi;
   Ngi_Box *box = item->box;

   if (!(ev->buttons == 3))
     {
	item->mouse_down = 1;
	return;
     }

   if (!it->app)
     return;

   evas_object_geometry_get(item->obj, &x, &y, &w, &h);

   x += box->ng->win->popup->x + box->ng->zone->x;
   y += box->ng->win->popup->y + box->ng->zone->y;

   int dir = E_MENU_POP_DIRECTION_AUTO;

   switch(box->ng->cfg->orient)
     {
      case E_GADCON_ORIENT_TOP:
	 dir = E_MENU_POP_DIRECTION_DOWN;
	 y += h;
	 break;

      case E_GADCON_ORIENT_BOTTOM:
	 dir = E_MENU_POP_DIRECTION_UP;
	 break;

      case E_GADCON_ORIENT_LEFT:
	 dir = E_MENU_POP_DIRECTION_RIGHT;
	 x += w;
	 break;

      case E_GADCON_ORIENT_RIGHT:
	 dir = E_MENU_POP_DIRECTION_LEFT;
	 break;
     }

   m = e_menu_new();

   mi = e_menu_item_new(m);
   e_menu_item_label_set(mi, D_("Configure Bar"));
   e_menu_item_callback_set(mi, _menu_cb_configure_bar, box->ng);

   mi = e_menu_item_new(m);
   e_menu_item_label_set(mi, D_("Lock Dragging"));
   e_menu_item_check_set(mi, 1);
   e_menu_item_toggle_set(mi, box->cfg->launcher_lock_dnd);
   e_menu_item_callback_set(mi, _menu_cb_lock_dnd, box);

   mi = e_menu_item_new(m);
   e_menu_item_separator_set(mi, 1);

   mi = e_menu_item_new(m);
   e_menu_item_label_set(mi, D_("Applications"));
   e_menu_item_callback_set(mi, _menu_cb_configure_launcher, box);

   mi = e_menu_item_new(m);
   e_menu_item_label_set(mi, D_("Edit Icon"));
   e_menu_item_callback_set(mi, _menu_cb_edit_icon, it);

   e_menu_post_deactivate_callback_set(m, _menu_cb_menu_end, box->ng);

   e_menu_activate_mouse(m, box->ng->zone, x, y, 1, 1,
			 dir, ev->timestamp);
}

static void
_item_cb_mouse_up(Ngi_Item *item, Ecore_Event_Mouse_Button *ev)
{
   Ngi_Item_Launcher *it = (Ngi_Item_Launcher *) item;

   if (!item->mouse_down)
     return;

   item->mouse_down = 0;

   if (ev->buttons == 1)
     {
	e_exec(e_util_zone_current_get(e_manager_current_get()), it->app, NULL, NULL, NULL);

	ngi_item_signal_emit((Ngi_Item*)it, "e,action,start");
     }
}

