#include <e.h>
#include <Elementary.h>

#include "desktop.h"
#include "desktop_page.h"
#include "elfe_config.h"
#include "utils.h"
#include "dock.h"
#include "allapps.h"

#define ELFE_DESKTOP_NUM 5
#define ELFE_DESKTOP_PADDING_W 10
#define ELFE_DESKTOP_PADDING_H 10

#define  LONGPRESS_THRESHOLD 16

typedef struct _Elfe_Desktop Elfe_Desktop;

struct _Elfe_Desktop
{
   Evas_Object *layout;
   Evas_Object *sc;
   Evas_Object *dock;
   Evas_Object *allapps;
   Evas_Object *floating_icon;
   Efreet_Menu *selected_app;
   const char *selected_gadget;
   Eina_List *pads;
   Eina_List *gadgets;
   Evas_Object *selector;
   Eina_List *selector_objs;
   int current_desktop;
   Ecore_Timer *longpress_timer;
   Eina_Bool on_hold;
   Eina_Bool edit_mode;
   Eina_List *overs;
   Evas_Coord dx, dy;
};


static Elfe_Desktop *desk;


static void
_scroller_scroll_anim_stop_cb(void *data , Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
   Elfe_Desktop *desk = data;
   Evas_Coord x, y, w, h;
   int i;


   elm_scroller_region_get(desk->sc, &x, &y, &w, &h);


   for (i = 0; i < ELFE_DESKTOP_NUM; i++)
     {
	Evas_Object *o_edje = elm_layout_edje_get(eina_list_nth(desk->selector_objs, i));
	if ( (x < ((i+1) * w)) && (x >= ( i * w)))
	  {
              desk->current_desktop = i;
	     edje_object_signal_emit(o_edje, "select", "elfe");
	  }
	else
	  {
	      edje_object_signal_emit(o_edje, "unselect", "elfe");
	  }
     }
}

static Eina_Bool
_longpress_timer_cb(void *data)
{
   Elfe_Desktop *desk = data;
   Evas_Object *gad;

   gad = eina_list_nth(desk->gadgets, desk->current_desktop);
   /* Set edit mode only when not in edit mode */
   if (!desk->edit_mode)
     {
	desk->edit_mode = EINA_TRUE;
	elfe_desktop_page_edit_mode_set(gad, EINA_TRUE);
	elfe_dock_edit_mode_set(desk->dock, EINA_TRUE);
	desk->longpress_timer = NULL;
	evas_object_smart_callback_call(desk->layout, "editmode,on", NULL);
     }


   return ECORE_CALLBACK_CANCEL;
}

static void
_scroller_mouse_move_cb(void *data,Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
{
   Elfe_Desktop *desk = data;
   Evas_Coord dx, dy;
   Evas_Event_Mouse_Move *ev = event_info;

   if (!desk->on_hold)
     {
	desk->on_hold = EINA_FALSE;
     }

   if (desk->longpress_timer)
     {
        desk->dx += ev->cur.output.x - ev->prev.output.x;
        desk->dy += ev->cur.output.y - ev->prev.output.y;
        if (abs(desk->dx) > LONGPRESS_THRESHOLD || abs(desk->dy) > LONGPRESS_THRESHOLD)
          {
             ecore_timer_del(desk->longpress_timer);
             desk->longpress_timer = NULL;
             desk->dx = 0;
             desk->dy = 0;
          }
     }
}


static void
_scroller_mouse_up_cb(void *data,Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
   Elfe_Desktop *desk = data;

   desk->on_hold = EINA_FALSE;
   if (desk->longpress_timer)
     {
	ecore_timer_del(desk->longpress_timer);
	desk->longpress_timer = NULL;
        desk->dx = 0;
        desk->dy = 0;
     }
}


static void
_scroller_mouse_down_cb(void *data,Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
   Elfe_Desktop *desk = data;

   if (!desk->edit_mode)
     {
	desk->on_hold = EINA_TRUE;
	if (desk->longpress_timer)
	  ecore_timer_del(desk->longpress_timer);
	desk->longpress_timer = ecore_timer_add(1.0, _longpress_timer_cb, desk);
        desk->dx = 0;
        desk->dy = 0;
     }
}

static void
_cb_object_resize(void *data , Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
{
   Elfe_Desktop *desk = data;
   Evas_Coord x, y, w, h;
   Eina_Array *pad;
   Eina_List *l;
   Evas_Object *gad;

   evas_object_geometry_get(obj, &x, &y, &w, &h);
   EINA_LIST_FOREACH(desk->pads, l, pad)
     {
	evas_object_size_hint_min_set(eina_array_data_get(pad, 0), w - 2 * ELFE_DESKTOP_PADDING_W , ELFE_DESKTOP_PADDING_H);
	evas_object_size_hint_min_set(eina_array_data_get(pad, 1), w - 2 * ELFE_DESKTOP_PADDING_W , ELFE_DESKTOP_PADDING_H);
	evas_object_size_hint_min_set(eina_array_data_get(pad, 2), ELFE_DESKTOP_PADDING_W , h - 2 * ELFE_DESKTOP_PADDING_H);
	evas_object_size_hint_min_set(eina_array_data_get(pad, 3), ELFE_DESKTOP_PADDING_W ,h - 2 * ELFE_DESKTOP_PADDING_H);

     }
   EINA_LIST_FOREACH(desk->gadgets, l, gad)
     {
	evas_object_size_hint_min_set(gad, w - 2 * ELFE_DESKTOP_PADDING_W,  h - 2 * ELFE_DESKTOP_PADDING_H);
     }

}


static void
_icon_mouse_move_cb(void *data,Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
{
   Elfe_Desktop *desk = data;
   Evas_Event_Mouse_Move *ev = event_info;

   evas_object_move(desk->floating_icon, ev->cur.output.x - 92 / 2, ev->cur.output.y - 92 / 2);

}


static void
_icon_mouse_up_cb(void *data __UNUSED__, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
{

   Evas_Event_Mouse_Up *ev = event_info;

   evas_object_del(desk->floating_icon);
   evas_object_event_callback_del(desk->layout, EVAS_CALLBACK_MOUSE_MOVE, _icon_mouse_move_cb);
   evas_object_event_callback_del(desk->layout, EVAS_CALLBACK_MOUSE_UP, _icon_mouse_up_cb);
   elfe_desktop_edit_mode_set(desk->layout, EINA_FALSE);
   elfe_dock_place_mode_set(desk->dock, EINA_FALSE);
   if (desk->selected_app)
     elfe_desktop_app_add(desk->layout, desk->selected_app, ev->output.x, ev->output.y);
   else if (desk->selected_gadget)
     elfe_desktop_gadget_add(desk->layout, desk->selected_gadget, ev->output.x, ev->output.y);

}

static void
_app_longpressed_cb(void *data , Evas_Object *obj, void *event_info)
{
   Elfe_Desktop *desk = data;
   Efreet_Menu *entry = event_info;
   Evas_Object *ic;
   Evas_Coord x, y;
   Evas_Object *o_edje;
   Evas_Coord ow, oh;
   Evas_Coord size = elfe_home_cfg->icon_size;


   evas_object_geometry_get(desk->layout, NULL, NULL, &ow, &oh);

   elfe_desktop_edit_mode_set(desk->layout, EINA_TRUE);
   elfe_dock_place_mode_set(desk->dock, EINA_TRUE);

   o_edje = elm_layout_edje_get(desk->layout);
   edje_object_signal_emit(o_edje, "allapps,toggle", "elfe");

   ic = elfe_utils_fdo_icon_add(o_edje, entry->icon, size);
   evas_object_show(ic);
   evas_pointer_canvas_xy_get(evas_object_evas_get(obj), &x, &y);
   evas_object_resize(ic, size, size);
   evas_object_move(ic, x - size / 2, y - size /2);
   desk->floating_icon = ic;

   evas_object_del(desk->allapps);
   desk->allapps = NULL;
   evas_object_pass_events_set(ic, EINA_TRUE);

   desk->selected_app = entry;

   evas_object_event_callback_add(desk->layout, EVAS_CALLBACK_MOUSE_MOVE, _icon_mouse_move_cb, desk);
   evas_object_event_callback_add(desk->layout, EVAS_CALLBACK_MOUSE_UP, _icon_mouse_up_cb, desk);
}


static void
_gadget_longpressed_cb(void *data , Evas_Object *obj, void *event_info)
{
   Elfe_Desktop *desk = data;
   const char *name = event_info;
   Evas_Coord x, y;
   Evas_Object *o_edje;
   Evas_Coord ow, oh;
   Evas_Coord size = 0;
   Evas_Object *ic;
   E_Gadcon_Client_Class *gcc = NULL;

   gcc = elfe_utils_gadcon_client_class_from_name(name);
   if (!gcc)
     {
         printf("error : unable to find gadcon client class from name : %s\n", name);
         return;
     }

   evas_object_geometry_get(desk->layout, NULL, NULL, &ow, &oh);

   size = MIN(ow, oh) / 5;

   elfe_desktop_edit_mode_set(desk->layout, EINA_TRUE);
   elfe_dock_place_mode_set(desk->dock, EINA_TRUE);

   o_edje = elm_layout_edje_get(desk->layout);
   edje_object_signal_emit(o_edje, "allapps,toggle", "elfe");

   ic = gcc->func.icon(gcc, evas_object_evas_get(obj));
   if (!ic)
     ic = elfe_utils_fdo_icon_add(obj, NULL, 64);

   evas_object_show(ic);
   evas_pointer_canvas_xy_get(evas_object_evas_get(obj), &x, &y);
   evas_object_resize(ic, size, size);
   evas_object_move(ic, x - size / 2, y - size /2);
   desk->floating_icon = ic;

   evas_object_del(desk->allapps);
   desk->allapps = NULL;
   evas_object_pass_events_set(ic, EINA_TRUE);

   desk->selected_app = NULL;
   desk->selected_gadget = name;

   evas_object_event_callback_add(desk->layout, EVAS_CALLBACK_MOUSE_MOVE, _icon_mouse_move_cb, desk);
   evas_object_event_callback_add(desk->layout, EVAS_CALLBACK_MOUSE_UP, _icon_mouse_up_cb, desk);
}

static void*
_app_exec_cb(void *data __UNUSED__, Efreet_Desktop *desktop __UNUSED__, char *command, int remaining __UNUSED__)
{
    ecore_exe_run(command, NULL);
    return NULL;
}

static void
_allapps_item_selected_cb(void *data , Evas_Object *obj __UNUSED__, void *event_info)
{
    Efreet_Menu *menu = event_info;
    Evas_Object *o_edje;
    Elfe_Desktop *desk = data;

    o_edje = elm_layout_edje_get(desk->layout);
    edje_object_signal_emit(o_edje, "allapps,toggle", "elfe");

    efreet_desktop_command_get(menu->desktop, NULL,
                               _app_exec_cb, NULL);
}

static void
_dock_allapps_clicked_cb(void *data , Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
   Elfe_Desktop *desk = data;
   Evas_Object *o_edje;

   if (!desk->edit_mode)
     {

	if (!desk->allapps)
	  {
	     desk->allapps = elfe_allapps_add(desk->layout);
	     evas_object_smart_callback_add(desk->allapps, "entry,longpressed", _app_longpressed_cb, desk);
	     evas_object_smart_callback_add(desk->allapps, "gadget,longpressed", _gadget_longpressed_cb, desk);
	     evas_object_smart_callback_add(desk->allapps, "item,selected", _allapps_item_selected_cb, desk);
	     evas_object_show(desk->allapps);
	     elm_layout_content_set(desk->layout, "elfe.swallow.allapps", desk->allapps);
	  }

	o_edje = elm_layout_edje_get(desk->layout);
	edje_object_signal_emit(o_edje, "allapps,toggle", "elfe");
     }
   else
     {
	elfe_desktop_edit_mode_set(desk->layout, EINA_FALSE);
        elfe_dock_place_mode_set(desk->dock, EINA_FALSE);
     }
}

void
elfe_desktop_app_add(Evas_Object *obj, Efreet_Menu *menu, Evas_Coord x, Evas_Coord y)
{
    Elfe_Desktop *desk = evas_object_data_get(obj, "elfe_desktop");
    Evas_Object *gad;
    Evas_Coord ox, oy, ow, oh;

    gad = eina_list_nth(desk->gadgets, desk->current_desktop);

    evas_object_geometry_get(desk->dock, &ox, &oy, &ow, &oh);
    if (ELM_RECTS_INTERSECT(ox, oy, ow, oh, x, y, 1, 1))
      elfe_dock_item_app_add(desk->dock, menu, x, y);
    else
      elfe_desktop_page_item_app_add(gad, menu, x, y);
}

void
elfe_desktop_gadget_add(Evas_Object *obj, const char *name, Evas_Coord x, Evas_Coord y)
{
    Elfe_Desktop *desk = evas_object_data_get(obj, "elfe_desktop");
    Evas_Object *gad;
    gad = eina_list_nth(desk->gadgets, desk->current_desktop);
    elfe_desktop_page_item_gadget_add(gad, name, x, y);
}


Evas_Object *
elfe_desktop_add(Evas_Object *parent, E_Zone *zone)
{
   Evas_Object *tb, *pad, *bx;

   int i;
   const char *desktop_name;
   Evas_Object *gad;
   char buf[4096];

   snprintf(buf, sizeof(buf), "%s/default.edj",
            elfe_home_cfg->mod_dir);

   desk = calloc(1, sizeof(Elfe_Desktop));

   desk->layout = elm_layout_add(parent);
   elm_layout_file_set(desk->layout, buf, "elfe/desktop/layout");

   desk->sc = elm_scroller_add(parent);
   elm_scroller_bounce_set(desk->sc, EINA_TRUE, EINA_FALSE);
   elm_scroller_policy_set(desk->sc, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
   elm_scroller_page_relative_set(desk->sc, 1.0, 1.0);

   bx = elm_box_add(desk->sc);
   evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, EVAS_HINT_FILL);
   elm_box_homogenous_set(bx, EINA_TRUE);
   elm_box_horizontal_set(bx, EINA_TRUE);

   desk->selector = elm_box_add(desk->layout);
   elm_box_homogenous_set(desk->selector, EINA_TRUE);
   elm_box_horizontal_set(desk->selector, EINA_TRUE);
   evas_object_show(desk->selector);

   for (i = 0; i < ELFE_DESKTOP_NUM; i++)
     {
	Eina_Array *pad_arr = eina_array_new(4);;
	Evas_Object *sel;

	tb = elm_table_add(bx);
	evas_object_size_hint_weight_set(tb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
	evas_object_size_hint_align_set(tb, EVAS_HINT_FILL, EVAS_HINT_FILL);
        evas_object_show(tb);

	pad = evas_object_rectangle_add(evas_object_evas_get(bx));
	evas_object_size_hint_min_set(pad, 460, ELFE_DESKTOP_PADDING_H);
	elm_table_pack(tb, pad, 1, 0, 1, 1);
	//evas_object_show(pad);
	eina_array_push(pad_arr, pad);


	pad = evas_object_rectangle_add(evas_object_evas_get(bx));
	evas_object_size_hint_min_set(pad, 460, ELFE_DESKTOP_PADDING_H);
	elm_table_pack(tb, pad, 1, 2, 1, 1);
	//evas_object_show(pad);
	eina_array_push(pad_arr, pad);

	pad = evas_object_rectangle_add(evas_object_evas_get(bx));
	evas_object_size_hint_min_set(pad, ELFE_DESKTOP_PADDING_W, 480);
	elm_table_pack(tb, pad, 0, 1, 1, 1);
	//evas_object_show(pad);
	eina_array_push(pad_arr, pad);

	pad = evas_object_rectangle_add(evas_object_evas_get(bx));
	evas_object_size_hint_min_set(pad, ELFE_DESKTOP_PADDING_W, 480);
	elm_table_pack(tb, pad, 2, 1, 1, 1);
	//evas_object_show(pad);
	eina_array_push(pad_arr, pad);

	desk->pads = eina_list_append(desk->pads, pad_arr);

	evas_object_show(tb);

        elm_box_pack_end(bx, tb);

        desktop_name = eina_stringshare_printf("Elfe Desktop %d", i);
        gad = elfe_desktop_page_add(tb, zone, i, desktop_name);
        evas_object_show(gad);

        desk->gadgets = eina_list_append(desk->gadgets, gad);
        eina_stringshare_del(desktop_name);
        elm_table_pack(tb, gad, 1, 1, 1, 1);

	sel = elm_layout_add(bx);
	elm_layout_file_set(sel, buf, "elfe/desktop/selector");
	evas_object_show(sel);
        evas_object_size_hint_min_set(sel, 32, 32);
	if (!i)
	  {
	     Evas_Object *o_edje;
	     o_edje = elm_layout_edje_get(sel);
	     edje_object_signal_emit(o_edje, "select", "elfe");
	  }
	elm_box_pack_end(desk->selector, sel);

	desk->selector_objs = eina_list_append(desk->selector_objs, sel);


     }

   elm_scroller_content_set(desk->sc, bx);
   evas_object_show(bx);

   desk->dock = elfe_dock_add(bx);
   evas_object_smart_callback_add(desk->dock, "allapps,clicked", _dock_allapps_clicked_cb, desk);
   /* evas_object_size_hint_min_set(desk->dock, 0, 80); */
   /* evas_object_size_hint_max_set(desk->dock, 9999, 80); */

   evas_object_smart_callback_add(desk->sc, "scroll,anim,stop", _scroller_scroll_anim_stop_cb, desk);

   evas_object_event_callback_add(desk->sc, EVAS_CALLBACK_MOUSE_MOVE, _scroller_mouse_move_cb, desk);
   evas_object_event_callback_add(desk->sc, EVAS_CALLBACK_MOUSE_UP, _scroller_mouse_up_cb, desk);
   evas_object_event_callback_add(desk->sc, EVAS_CALLBACK_MOUSE_DOWN, _scroller_mouse_down_cb, desk);


   evas_object_event_callback_add(desk->sc, EVAS_CALLBACK_RESIZE,
				  _cb_object_resize, desk);

   evas_object_data_set(desk->layout, "elfe_desktop", desk);

   elm_layout_content_set(desk->layout, "elfe.swallow.content", desk->sc);
   elm_layout_content_set(desk->layout, "elfe.swallow.selector", desk->selector);
   elm_layout_content_set(desk->layout, "elfe.swallow.dock", desk->dock);

   return desk->layout;

}

void
elfe_desktop_edit_mode_set(Evas_Object *obj, Eina_Bool mode)
{
   Elfe_Desktop *desk = evas_object_data_get(obj, "elfe_desktop");
   Evas_Object *gad;
   int m,n;
   int i,j;
   Eina_List *l;
   char buf[PATH_MAX];
   Evas_Object *over;

   if (desk->edit_mode == mode)
       return;

   desk->edit_mode = mode;

   snprintf(buf, sizeof(buf), "%s/default.edj",
            elfe_home_cfg->mod_dir);

   if (mode)
     {
	Evas_Coord x, y, w, h;
	gad = eina_list_nth(desk->gadgets, desk->current_desktop);
	evas_object_geometry_get(gad, &x, &y, &w, &h);

	m = w / elfe_home_cfg->cols;
	n = h / elfe_home_cfg->rows;

	for (i = 0; i < elfe_home_cfg->cols; i++)
	  for (j = 0; j < elfe_home_cfg->rows; j++)
	    {
	       Evas_Object *o_edje;

	       over = elm_layout_add(desk->sc);
	       elm_layout_file_set(over, buf, "elfe/gadget/places/over");
	       o_edje = elm_layout_edje_get(over);
               if (elfe_desktop_page_pos_is_free(gad, j, i))
                   edje_object_signal_emit(o_edje, "place,busy", "elfe");
               else
                   edje_object_signal_emit(o_edje, "place,free", "elfe");

	       evas_object_resize(o_edje, m, n);
	       evas_object_move(o_edje, x + i*m,  y + j*n);
	       evas_object_show(over);
               desk->overs = eina_list_append(desk->overs, over);
	    }
     }
   else
     {
	EINA_LIST_FREE(desk->overs, over)
	  {
	     evas_object_del(over);
	  }

	desk->overs = NULL;

	EINA_LIST_FOREACH(desk->gadgets, l, gad)
	  {
	     elfe_desktop_page_edit_mode_set(gad, EINA_FALSE);
	     evas_object_smart_callback_call(desk->layout, "editmode,off", desk);
	  }
	elfe_dock_edit_mode_set(desk->dock, EINA_FALSE);
     }
}
