#include "e.h"
#include "e_mod_main.h"
#include "itask_border_menu.h"
#include "itask_item.h"
#include "itask_menu.h"


static void _itask_item_cb_mouse_in(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _itask_item_cb_mouse_out(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _itask_item_cb_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _itask_item_cb_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _itask_item_cb_mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _itask_item_cb_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _itask_item_cb_resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _itask_item_cb_menu_post(void *data, E_Menu *m);
static void _itask_item_cb_drag_finished(E_Drag *drag, int dropped);
static void _itask_item_cb_menu_post(void *data, E_Menu *m);

static Itask_Item *_itask_item_find(Itask *it, E_Border *bd);

static Itask_Item *_itask_item_get_youngest(Eina_List *list);
static Itask_Item *_itask_item_get_oldest(Eina_List *list);

EAPI void
itask_item_add_to_bar(Itask_Item *ic)
{
   Itask *it = ic->itask;
   Eina_List *l;
   ic->in_bar = 1;
   Itask_Item *l_ic = NULL;
   if(it->num_items == 0 && it->hide_menu_button){
     itask_menu_button(it);
   }
   it->num_items++;
       
   ic->o_holder = edje_object_add(evas_object_evas_get(it->o_box));
 
   if(it->option_ibox_style)
   {
   	  if (!e_theme_edje_object_set(ic->o_holder, "base/theme/modules/itask", "modules/itask/icon"))
        edje_object_file_set(ic->o_holder, itask_theme_path, "modules/itask/icon"); 
   }  
   else
   {
      if (!e_theme_edje_object_set(ic->o_holder, "base/theme/modules/itask", "modules/itask/item"))
        edje_object_file_set(ic->o_holder, itask_theme_path, "modules/itask/item");
   }
   evas_object_event_callback_add(ic->o_holder, EVAS_CALLBACK_MOUSE_IN,  _itask_item_cb_mouse_in,  ic);
   evas_object_event_callback_add(ic->o_holder, EVAS_CALLBACK_MOUSE_OUT, _itask_item_cb_mouse_out, ic);
   evas_object_event_callback_add(ic->o_holder, EVAS_CALLBACK_MOUSE_DOWN, _itask_item_cb_mouse_down, ic);
   evas_object_event_callback_add(ic->o_holder, EVAS_CALLBACK_MOUSE_UP, _itask_item_cb_mouse_up, ic);
   evas_object_event_callback_add(ic->o_holder, EVAS_CALLBACK_MOUSE_MOVE, _itask_item_cb_mouse_move, ic);
   evas_object_event_callback_add(ic->o_holder, EVAS_CALLBACK_MOVE, _itask_item_cb_move, ic);
   evas_object_event_callback_add(ic->o_holder, EVAS_CALLBACK_RESIZE, _itask_item_cb_resize, ic);
   evas_object_show(ic->o_holder);
   
   if(0) // TODO use this for 'plain' mode or effects
   {
   ic->o_holder2 = edje_object_add(evas_object_evas_get(it->o_box));
   if (!e_theme_edje_object_set(ic->o_holder2, "base/theme/modules/itask", "modules/itask/icon_overlay"))
      edje_object_file_set(ic->o_holder2, itask_theme_path, "modules/itask/icon_overlay");
   evas_object_layer_set(ic->o_holder2, 9999);
   evas_object_pass_events_set(ic->o_holder2, 1);
   evas_object_show(ic->o_holder2);
   }
   
   if(!ic->o_icon)
      itask_item_set_icon(ic);
   else
   {
      edje_object_part_swallow(ic->o_holder, "icon", ic->o_icon);
      evas_object_pass_events_set(ic->o_icon, 1);
      evas_object_show(ic->o_icon);
      if(0)
      {
      edje_object_part_swallow(ic->o_holder2, "icon", ic->o_icon2);
      evas_object_pass_events_set(ic->o_icon2, 1);
      evas_object_show(ic->o_icon2);
      }
      
      if (ic->border->iconic)
         itask_icon_signal_emit(ic, "iconify", "");
      if (ic->border->focused)
         itask_icon_signal_emit(ic, "focused", "");
   }
   itask_item_set_label(ic);

   if(ic->border->client.icccm.class)
      for(l = it->items_bar; l; l = l->next) 
      {
         l_ic = l->data;
         if(l_ic->border->client.icccm.class 
         && !strcmp(l_ic->border->client.icccm.class,ic->border->client.icccm.class))
            break;
         l_ic = NULL;
      }
   if(l_ic)
   {
      e_box_pack_before(it->o_box, ic->o_holder, l_ic->o_holder);
   }
   else
   {
      e_box_pack_after(it->o_box, ic->o_holder, it->o_button);
   }   
   
   e_box_freeze(it->o_box); 
   e_box_pack_options_set(ic->o_holder,
                             1, 1, /* fill */
                             1, 1, /* expand */
                             0.5, 0.5, /* align */
                             0, 0, /* min */
                             -1, -1 /* max */
                            );
   e_box_thaw(it->o_box);

   it->items_bar = eina_list_append(it->items_bar, ic);
}



EAPI void
itask_item_remove_from_bar(Itask_Item *ic)
{
   Itask *it = ic->itask;
   it->num_items--;
   if(it->num_items == 0 && it->hide_menu_button){
     itask_menu_remove(it);
   }   
   ic->in_bar = 0;
   itask_item_del_icon(ic);

   edje_object_part_unswallow(ic->o_holder, ic->o_icon);
   evas_object_del(ic->o_holder);
   if(0)
   {
   edje_object_part_unswallow(ic->o_holder2, ic->o_icon2);
   evas_object_del(ic->o_holder2);
   }
   it->items_bar = eina_list_remove(it->items_bar, ic);
}




EAPI Itask_Item *
itask_item_new(Itask *it, E_Border *bd)
{
   Itask_Item *ic, *l_ic;
   Eina_List *l;
   Eina_List *group = NULL;
   if(!itask_item_add_check(it, bd) || _itask_item_find(it, bd))
      return NULL;

   ic = E_NEW(Itask_Item, 1);
   e_object_ref(E_OBJECT(bd));

   ic->itask = it;
   ic->border = bd;
   ic->last_time = ecore_time_get();
   ic->items = NULL;

   it->items = eina_list_append(it->items, ic);
   
   /* group items */  //TODO rename ic->items to ic->group
   if(ic->border->client.icccm.class)
   {
      if(group = eina_hash_find(it->item_groups, ic->border->client.icccm.class))
      {
      	 group = eina_list_append(group, ic);
      }
      else
      {
      	 group = eina_list_append(group, ic);
         eina_hash_add(it->item_groups, ic->border->client.icccm.class, group);
      }
   }   
   return ic;
}


EAPI int
itask_item_realize(Itask_Item *ic)
{
  Itask *it;
  Eina_List *l;
  Itask_Item *l_ic;
  it = ic->itask;
  int resize = 1; 
  
  /* hack start */
  if(eina_list_data_find(ic->itask->items_bar, ic))
    return 0;
  /* hack end */
  
  //if(it->option_always_group)
  if(0){
  	  if(ic->border->client.icccm.class)
      for(l = it->items_bar; l; l = l->next) 
      {
         l_ic = l->data;
         if(l_ic->border->client.icccm.class 
         && !strcmp(l_ic->border->client.icccm.class,ic->border->client.icccm.class))
         {   
            itask_item_remove_from_bar(l_ic);
            break;
         }
      }
   }
  
   if(it->num_items >= it->max_items)
   //if(!itask_item_space_left(it, 1))
   {
   	  int resize = 0;
      itask_item_swap_oldest_from_bar(it);
      edje_object_signal_emit(it->o_button, "focused", "");
   } 
  itask_item_add_to_bar(ic);
  
  if(resize)
     itask_update_gc_orient(it);
  
  return 1;
}


EAPI void
itask_item_remove(Itask_Item *ic)
{
   Itask_Item *ic_from_menu, *ic2;
   Itask *it;
   Eina_List *group;
    
   it = ic->itask;

   it->items = eina_list_remove(it->items, ic);

   if(ic->in_bar)
      itask_item_remove_from_bar(ic);
   else
      it->items_menu = eina_list_remove(it->items_menu, ic);
   
   if(ic->border->client.icccm.class)
   {
      if(group = eina_hash_find(it->item_groups, ic->border->client.icccm.class))
      {
      	 group = eina_list_remove(group, ic);
      	 
      	 
      	 if(eina_list_count(group) == 0)
      	   eina_hash_del(it->item_groups, ic->border->client.icccm.class, group);
      	 else
      	 {
      	   eina_hash_modify(it->item_groups, ic->border->client.icccm.class, group);
      	 }
      }
   }  
   e_object_unref(E_OBJECT(ic->border));
   free(ic);
}



EAPI void
itask_item_swap_oldest_from_bar(Itask *it)
{
   Itask_Item *ic, *l_ic;
   Eina_List *l;
   
   ic = _itask_item_get_oldest(it->items_bar);
   if(ic)
   {
      itask_item_remove_from_bar(ic);
      
      it->items_menu = eina_list_append(it->items_menu, ic);
      edje_object_signal_emit(it->o_button, "focused", "");
   }
}




EAPI void
itask_item_swap_youngest_from_menu(Itask *it)
{
	Itask_Item *ic;
    ic = _itask_item_get_youngest(it->items_menu);
    if(ic)
    {
       itask_item_swap_to_bar(ic); 
    }
}



EAPI void
itask_item_swap_to_bar(Itask_Item *ic)
{
   Itask *it = ic->itask;
   	
   ic->last_time = ecore_time_get();
   itask_item_add_to_bar(ic);
   it->items_menu = eina_list_remove(it->items_menu, ic);
   
   if(!it->items_menu)
   {
     edje_object_signal_emit(it->o_button, "unfocused", "");
   //edje_object_part_text_set(it->o_button, "label", " ");
   }
}




EAPI void
itask_item_set_icon(Itask_Item *ic)
{
   ic->o_icon = e_border_icon_add(ic->border, evas_object_evas_get(ic->itask->o_box));
   edje_object_part_swallow(ic->o_holder, "icon", ic->o_icon);
   evas_object_pass_events_set(ic->o_icon, 1);
   evas_object_show(ic->o_icon);
   if(0)
   {
   ic->o_icon2 = e_border_icon_add(ic->border, evas_object_evas_get(ic->itask->o_box));
   edje_object_part_swallow(ic->o_holder2, "icon", ic->o_icon2);
   evas_object_pass_events_set(ic->o_icon2, 1);
   evas_object_show(ic->o_icon2);
   }
   
   if (ic->border->iconic)
      itask_icon_signal_emit(ic, "iconify", "");
   if (ic->border->focused)
      itask_icon_signal_emit(ic, "focused", "");

}



EAPI void
itask_item_set_label(Itask_Item *ic)
{
   const char *title;

   title = e_border_name_get(ic->border);

   if ((!title) || (!title[0]))
      title  = N_("No name!!");

   edje_object_part_text_set(ic->o_holder, "label", title);
   
   if(0) edje_object_part_text_set(ic->o_holder2, "label", title);
}



EAPI void
itask_item_del_icon(Itask_Item *ic)
{
   if (ic->o_icon)
      evas_object_del(ic->o_icon);
   if (ic->o_icon2)
      evas_object_del(ic->o_icon2);
   ic->o_icon = NULL;
   ic->o_icon2 = NULL;
}



EAPI void
itask_icon_signal_emit(Itask_Item *ic, char *sig, char *src)
{
   if (ic->o_holder)
      edje_object_signal_emit(ic->o_holder, sig, src);
   if (ic->o_icon)
      edje_object_signal_emit(ic->o_icon, sig, src);
   if(0)
   {
   if (ic->o_holder2)
      edje_object_signal_emit(ic->o_holder2, sig, src);
   if (ic->o_icon2)
      edje_object_signal_emit(ic->o_icon2, sig, src);
   }
}



static void
_itask_item_cb_menu_post(void *data, E_Menu *m)
{
   if (!m)
      return;
   e_object_del(E_OBJECT(m));
}



static void
_itask_item_cb_mouse_in(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Evas_Event_Mouse_In *ev;
   Itask_Item *ic;
   Itask *it;
   ev = event_info;
   ic = data;
   
   /* TODO a popup that shows the label*/
  /* it = ic->itask;
   E_Container *con;
   if (!(con = e_container_current_get(e_manager_current_get()))) return;
   
   if(it->item_label_popup)
   {
   	    e_popup_hide(it->item_label_popup);
   		e_object_del(E_OBJECT(it->item_label_popup));
   		it->item_label_popup = NULL;
   }
   
   it->item_label_popup = e_popup_new(e_zone_current_get(con), 0, 0, 100, 100);
   
   e_popup_show(it->item_label_popup);
   */
   itask_icon_signal_emit(ic, "active", "");
   if (ic->itask->show_label)
      itask_icon_signal_emit(ic, "label_active", "");
}



static void
_itask_item_cb_mouse_out(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Evas_Event_Mouse_Out *ev;
   Itask_Item *ic;
   Itask *it;
   
   ev = event_info;
   ic = data;
   /*
   it = ic->itask;
    if(it->item_label_popup)
   {
   	    e_popup_hide(it->item_label_popup);
   		e_object_del(E_OBJECT(it->item_label_popup));
   		it->item_label_popup = NULL;
   }*/
   itask_icon_signal_emit(ic, "passive", "");
   if (ic->itask->show_label)
      itask_icon_signal_emit(ic, "label_passive", "");
}

static void
_e_int_menus_apps_drag(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Border *bd;
   bd = data;
   
   /* start drag! */
   if (mi->icon_object)
      {
	 E_Drag *drag;
	 Evas_Object *o = NULL;
	 Evas_Coord x, y, w, h;
	 const char *drag_types[] = { "enlightenment/border" };

	 evas_object_geometry_get(mi->icon_object,
				     &x, &y, &w, &h);
	 drag = e_drag_new(m->zone->container, x, y,
			      drag_types, 1, bd, -1, NULL, _itask_item_cb_drag_finished);

     o = e_border_icon_add(bd, e_drag_evas_get(drag));
  
	 e_drag_object_set(drag, o);
         e_drag_resize(drag, w, h);
         e_object_ref(E_OBJECT(bd));
	 e_drag_start(drag, mi->drag.x + w, mi->drag.y + h);
      }
}

static void
_itask_item_cb_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Evas_Event_Mouse_Down *ev;
   Itask_Item *ic, *ic2;
   Instance *inst;
   Evas_Coord x, y, w, h;
   E_Menu *mn = NULL;
   E_Menu_Item *mi;
   int cx, cy, cw, ch, dir;
   const char *title;
   Eina_List *group, *l;
   
   ev = event_info;
   ic = data;
   inst = ic->itask->inst;
   
   if (ev->button == 1)
   {
      ic->drag.x = ev->output.x;
      ic->drag.y = ev->output.y;
      ic->drag.start = 1;
      ic->drag.dnd = 0;
   }
   else if (ev->button == 3)
   {
     // E_Border *bd = ic->border;
      if(ic->border)
      {
       // TODO:
       // Disable own menu for now, no time atm to get into why 
       // itasks menu doesnt work anymore
       //  mn = e_menu_new();
       //  mn = itask_border_menu_get(ic->border,mn);
       evas_object_geometry_get(ic->o_holder, &x, &y, &w, &h);
      e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon,
                                        &cx, &cy, &cw, &ch);
      x += cx;
      y += cy;
       
       e_int_border_menu_show(ic->border, x, y, 0, ev->timestamp);
       //       e_util_evas_fake_mouse_up_later(inst->gcc->gadcon->evas,
       //                              ev->button);
       //EAPI void e_int_border_menu_del(E_Border *bd);
         
      }
   }
   else if (ev->button == 2) // && itask->option_group_items
   {  
      group = eina_hash_find(ic->itask->item_groups, ic->border->client.icccm.class);
      if(group)
        mn = itask_menu_items_menu(group);
   }
   if(mn)
   {
      e_menu_post_deactivate_callback_set(mn, _itask_item_cb_menu_post, NULL);

      evas_object_geometry_get(ic->o_holder, &x, &y, &w, &h);
      e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon,
                                        &cx, &cy, &cw, &ch);
      x += cx;
      y += cy;

      dir = E_MENU_POP_DIRECTION_AUTO;
      switch (inst->gcc->gadcon->orient)
      {
      case E_GADCON_ORIENT_TOP:
      case E_GADCON_ORIENT_CORNER_TL:
      case E_GADCON_ORIENT_CORNER_TR:
         dir = E_MENU_POP_DIRECTION_DOWN;
         break;
      case E_GADCON_ORIENT_BOTTOM:
      case E_GADCON_ORIENT_CORNER_BL:
      case E_GADCON_ORIENT_CORNER_BR:
         dir = E_MENU_POP_DIRECTION_UP;
         break;
      case E_GADCON_ORIENT_LEFT:
      case E_GADCON_ORIENT_CORNER_LT:
      case E_GADCON_ORIENT_CORNER_LB:
         dir = E_MENU_POP_DIRECTION_RIGHT;
         break;
      case E_GADCON_ORIENT_RIGHT:
      case E_GADCON_ORIENT_CORNER_RT:
      case E_GADCON_ORIENT_CORNER_RB:
         dir = E_MENU_POP_DIRECTION_LEFT;
         break;
      case E_GADCON_ORIENT_FLOAT:
      case E_GADCON_ORIENT_HORIZ:
      case E_GADCON_ORIENT_VERT:
      default:
         dir = E_MENU_POP_DIRECTION_AUTO;
         break;
      }
      e_menu_activate_mouse(mn,
                            e_util_zone_current_get(e_manager_current_get()),
                            x, y, w, h,
                            dir, ev->timestamp);
      // edje_object_signal_emit(inst->o_button, "e,state,focused", "e");
      //e_util_evas_fake_mouse_up_later(inst->gcc->gadcon->evas,
      //                                ev->button);
   }
}



static void
_itask_item_cb_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Evas_Event_Mouse_Up *ev;
   Itask_Item *ic;
   E_Border *bd;
   E_Border_List *bl;
   E_Desk *current_desk;
   int found_border_above = 1;
   
   ev = event_info;
   ic = data;
   if ((ev->button == 1) && (!ic->drag.dnd))
   {
   	  current_desk = e_desk_current_get(ic->border->zone);
   	  
   	  if((ic->itask->option_iconify_focused) 
   	  && (!ic->border->iconic)
   	  && (!ic->border->shaded)
   	  && (ic->border->desk == current_desk))
      {
      	found_border_above = 0;
        bl = e_container_border_list_last(ic->border->zone->container);
        while ((bd = e_container_border_list_next(bl)))
        {
	      if (bd->new_client 
	      || ((bd->desk != current_desk) && (!bd->sticky))
	      || (bd->iconic)	      
	      || (bd->layer == 150)) continue; // TODO there is probably more stuff to filter out
          
          if(ic->border != bd)
          {
             found_border_above = 1;
             break;
          }	
        }
        e_container_border_list_free(bl);
      }
      
      if(!found_border_above)
         e_border_iconify(ic->border);
   	  else 
   	  {
         if(e_desk_current_get(ic->border->zone) != ic->border->desk && !ic->border->sticky)
            e_desk_show(ic->border->desk);
         
         if(ic->border->shaded)
            e_border_unshade(ic->border, E_DIRECTION_UP);
         
         if(ic->border->iconic)
            e_border_uniconify(ic->border);
         else
            e_border_raise(ic->border);

         e_border_focus_set(ic->border, 1, 1);
   	  }
   }
   ic->drag.start = 0;
}



static void
_itask_item_cb_mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Evas_Event_Mouse_Move *ev;
   Itask_Item *ic;

   ev = event_info;
   ic = data;
   if (ic->drag.start)
   {
      int dx, dy;

      dx = ev->cur.output.x - ic->drag.x;
      dy = ev->cur.output.y - ic->drag.y;
      if (((dx * dx) + (dy * dy)) >
            (e_config->drag_resist * e_config->drag_resist))
      {
         E_Drag *d;
         Evas_Object *o;
         Evas_Coord x, y, w, h;
         const char *drag_types[] =
            { "enlightenment/border" };

         ic->drag.dnd = 1;
         ic->drag.start = 0;

         evas_object_geometry_get(ic->o_icon, &x, &y, &w, &h);
         d = e_drag_new(ic->itask->inst->gcc->gadcon->zone->container,
                        x, y, drag_types, 1,
                        ic->border, -1, NULL, _itask_item_cb_drag_finished);
         o = e_border_icon_add(ic->border, e_drag_evas_get(d));
         e_drag_object_set(d, o);

         e_drag_resize(d, w, h);
         e_drag_start(d, ic->drag.x, ic->drag.y);
         evas_event_feed_mouse_up(ic->itask->inst->gcc->gadcon->evas,
                                  1, EVAS_BUTTON_NONE,
                                  ecore_x_current_time_get(), NULL);
         e_object_ref(E_OBJECT(ic->border));
      }
   }
}



static void
_itask_item_cb_move(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Itask_Item *ic;
   Evas_Coord x, y;
   if(0)
   {
   ic = data;
   evas_object_geometry_get(ic->o_holder, &x, &y, NULL, NULL);
   evas_object_move(ic->o_holder2, x, y);
   evas_object_raise(ic->o_holder2);
   }
}



static void
_itask_item_cb_resize(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Itask_Item *ic;
   Evas_Coord w, h;
   if(0)
   {
   ic = data;
   evas_object_geometry_get(ic->o_holder, NULL, NULL, &w, &h);
   evas_object_resize(ic->o_holder2, w, h);
   evas_object_raise(ic->o_holder2);
   }
}



static void
_itask_item_cb_drag_finished(E_Drag *drag, int dropped)
{
   E_Border *bd;

   bd = drag->data;
   if (!dropped)
   {
      e_border_desk_set(bd, e_desk_current_get(bd->zone));
      if(bd->iconic)
         e_border_uniconify(bd);
      else
         e_border_raise(bd);
   }
   e_object_unref(E_OBJECT(bd));
}

static Itask_Item *
_itask_item_find(Itask *it, E_Border *bd)
{
   Eina_List *l;
   Itask_Item *ic;

   for (l = it->items; l; l = l->next)
   {
      ic = l->data;
      if (ic->border == bd)
         return ic;
   }
   return NULL;
}



EAPI int
itask_item_add_check(Itask *it, E_Border *bd)
{
   if((it->skip_dialogs && bd->client.netwm.type == ECORE_X_WINDOW_TYPE_DIALOG)
         || (it->skip_always_below_windows && bd->layer == 50)
         || (bd->user_skip_winlist)
         || (bd->client.netwm.state.skip_taskbar)
         || ((it->show_zone) && (bd->zone != it->zone))
         || ((it->show_zone == 2) && (!bd->desk->visible)))
      return 0;
      
   return 1;
}

static Itask_Item *
_itask_item_get_oldest(Eina_List *list)
{ 
   Eina_List *l;
   Itask_Item *ic = NULL, *ic2;
   
   
   if(!list)
      return NULL;
     
   if(list->data)
      ic = list->data;

   for(l = list->next; l ; l = l->next)
   {
      ic2 = l->data;
      if(ic2 && ic2->last_time < ic->last_time)
         ic = ic2;
   }
   return ic;
}

static Itask_Item *
_itask_item_get_youngest(Eina_List *list)
{
   Eina_List *l;
   Itask_Item *ic = NULL, *ic2;

   if(!list)
      return NULL;

   if(list->data)
      ic = list->data;

   for(l = list->next; l ; l = l->next)
   {
      ic2 = l->data;
      if(ic2 && ic2->last_time > ic->last_time)
         ic = ic2;
   }
   return ic;
}
