/**
 * @addtogroup Optional_Look
 * @{
 *
 * @defgroup Module_Shot Screenshot
 *
 * Takes screen shots and can submit them to http://enlightenment.org
 *
 * @}
 */
#include "e.h"
#include <time.h>
#include <sys/mman.h>

#if defined(__FreeBSD__) || defined(__DragonFly__)
#include <sys/types.h>
#include <sys/sysctl.h>
#endif

static E_Module *shot_module = NULL;

static E_Action *border_act = NULL, *act = NULL;
static E_Int_Menu_Augmentation *maug = NULL;
static Ecore_Timer *timer, *border_timer = NULL;
static Evas_Object *win = NULL;
static Evas_Object *snap = NULL;
E_Confirm_Dialog *cd = NULL;
static Evas_Object *o_bg = NULL, *o_box = NULL, *o_content = NULL;
static Evas_Object *o_event = NULL, *o_img = NULL, *o_hlist = NULL;
static int quality = 90;
static int screen = -1;
#define MAXZONES 64
static Evas_Object *o_rectdim[MAXZONES] = { NULL };
static Evas_Object *o_radio_all = NULL;
static Evas_Object *o_radio[MAXZONES] = { NULL };
static Evas_Object *o_fsel = NULL;
static Evas_Object *o_label = NULL;
static Evas_Object *o_entry = NULL;
static unsigned char *fdata = NULL;
static int fsize = 0;
static Ecore_Con_Url *url_up = NULL;
static Eina_List *handlers = NULL;
static char *url_ret = NULL;
static E_Dialog *fsel_dia = NULL;
static E_Client_Menu_Hook *border_hook = NULL;

static E_Client *shot_ec = NULL;
static E_Zone *shot_zone = NULL;
static char *shot_params;

static void _file_select_ok_cb(void *data EINA_UNUSED, E_Dialog *dia);
static void _file_select_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia);

static void
_win_cancel_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED)
{
   E_FREE_FUNC(win, evas_object_del);
}

static void
_win_delete_cb()
{
   win = NULL;
}

static void
_on_focus_cb(void *data EINA_UNUSED, Evas_Object *obj)
{
   if (obj == o_content) e_widget_focused_object_clear(o_box);
   else if (o_content) e_widget_focused_object_clear(o_content);
}

static void
_key_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   Evas_Event_Key_Down *ev = event;

   if (!strcmp(ev->key, "Tab"))
     {
        if (evas_key_modifier_is_set(evas_key_modifier_get(evas_object_evas_get(win)), "Shift"))
          {
             if (e_widget_focus_get(o_box))
               {
                  if (!e_widget_focus_jump(o_box, 0))
                    {
                       e_widget_focus_set(o_content, 0);
                       if (!e_widget_focus_get(o_content))
                          e_widget_focus_set(o_box, 0);
                    }
               }
             else
               {
                  if (!e_widget_focus_jump(o_content, 0))
                     e_widget_focus_set(o_box, 0);
               }
          }
        else
          {
             if (e_widget_focus_get(o_box))
               {
                  if (!e_widget_focus_jump(o_box, 1))
                    {
                       e_widget_focus_set(o_content, 1);
                       if (!e_widget_focus_get(o_content))
                          e_widget_focus_set(o_box, 1);
                    }
               }
             else
               {
                  if (!e_widget_focus_jump(o_content, 1))
                     e_widget_focus_set(o_box, 1);
               }
          }
     }
   else if (((!strcmp(ev->key, "Return")) ||
             (!strcmp(ev->key, "KP_Enter")) ||
             (!strcmp(ev->key, "space"))))
     {
        Evas_Object *o = NULL;

        if ((o_content) && (e_widget_focus_get(o_content)))
          o = e_widget_focused_object_get(o_content);
        else
          o = e_widget_focused_object_get(o_box);
        if (o) e_widget_activate(o);
     }
   else if (!strcmp(ev->key, "Escape"))
     _win_cancel_cb(NULL, NULL);
}

static void
_save_key_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   Evas_Event_Key_Down *ev = event;

   if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
     _file_select_ok_cb(NULL, fsel_dia);
   else if (!strcmp(ev->key, "Escape"))
     _file_select_cancel_cb(NULL, fsel_dia);
}

static void
_screen_change_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Eina_List *l;
   E_Zone *z;

   EINA_LIST_FOREACH(e_comp->zones, l, z)
     {
        if (screen == -1)
          evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0);
        else if (screen == (int)z->num)
          evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0);
        else
          evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 200);
     }
}

static void
_save_to(const char *file)
{
   char opts[256];

   if (eina_str_has_extension(file, ".png"))
     snprintf(opts, sizeof(opts), "compress=%i", 9);
   else
     snprintf(opts, sizeof(opts), "quality=%i", quality);
   if (screen == -1)
     {
        if (o_img)
          {
             if (!evas_object_image_save(o_img, file, NULL, opts))
               e_util_dialog_show(_("Error saving screenshot file"),
                                  _("Path: %s"), file);
          }
     }
   else
     {
        Evas_Object *o;
        Eina_List *l;
        E_Zone *z = NULL;

        EINA_LIST_FOREACH(e_comp->zones, l, z)
          {
             if (screen == (int)z->num) break;
             z = NULL;
          }
        if (z)
          {
             unsigned char *src, *dst, *s, *d;
             int sstd, dstd, y;

             o = evas_object_image_add(evas_object_evas_get(o_img));
             evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888);
             evas_object_image_alpha_set(o, EINA_FALSE);
             evas_object_image_size_set(o, z->w, z->h);
             dstd = evas_object_image_stride_get(o);
             src = evas_object_image_data_get(o_img, EINA_FALSE);
             sstd = evas_object_image_stride_get(o_img);
             dst = evas_object_image_data_get(o, EINA_TRUE);
             if ((dstd > 0) && (sstd > 0) && (src) && (dst))
               {
                  d = dst;
                  for (y = z->y; y < z->y + z->h; y++)
                    {
                       s = src + (sstd * y) + (z->x * 4);
                       memcpy(d, s, z->w * 4);
                       d += dstd;
                    }
                  if (!evas_object_image_save(o, file, NULL, opts))
                    e_util_dialog_show(_("Error saving screenshot file"),
                                       _("Path: %s"), file);
               }

             evas_object_del(o);
          }
     }
}

static void
_file_select_ok_cb(void *data EINA_UNUSED, E_Dialog *dia)
{
   const char *file;

   dia = fsel_dia;
   file = e_widget_fsel_selection_path_get(o_fsel);
   if ((!file) || (!file[0]) ||
       ((!eina_str_has_extension(file, ".jpg")) &&
           (!eina_str_has_extension(file, ".png"))))
     {
        e_util_dialog_show
        (_("Error - Unknown format"),
            _("File has an unspecified extension.<ps/>"
              "Please use '.jpg' or '.png' extensions<ps/>"
              "only as other formats are not<ps/>"
              "supported currently."));
        return;
     }
   _save_to(file);
   if (dia) e_util_defer_object_del(E_OBJECT(dia));
   E_FREE_FUNC(win, evas_object_del);
   fsel_dia = NULL;
}

static void
_file_select_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia)
{
   if (dia) e_util_defer_object_del(E_OBJECT(dia));
   fsel_dia = NULL;
}

static void
_file_select_del_cb(void *d EINA_UNUSED)
{
   fsel_dia = NULL;
}

static void
_win_save_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED)
{ 
   E_Dialog *dia;
   Evas_Object *o;
   Evas_Coord mw, mh;
   int mask = 0;
   time_t tt;
   struct tm *tm;
   char buf[PATH_MAX];

   time(&tt);
   tm = localtime(&tt);
   if (quality == 100)
     strftime(buf, sizeof(buf), "shot-%Y-%m-%d_%H-%M-%S.png", tm);
   else
     strftime(buf, sizeof(buf), "shot-%Y-%m-%d_%H-%M-%S.jpg", tm);
   fsel_dia = dia = e_dialog_new(NULL, "E", "_e_shot_fsel");
   e_dialog_resizable_set(dia, EINA_TRUE);
   e_dialog_title_set(dia, _("Select screenshot save location"));
   o = e_widget_fsel_add(evas_object_evas_get(dia->win), "desktop", "/", 
                         buf, NULL, NULL, NULL, NULL, NULL, 1);
   e_object_del_attach_func_set(E_OBJECT(dia), _file_select_del_cb);
   e_widget_fsel_window_set(o, dia->win);
   o_fsel = o;
   evas_object_show(o);
   e_widget_size_min_get(o, &mw, &mh);
   e_dialog_content_set(dia, o, mw, mh);
   e_dialog_button_add(dia, _("Save"), NULL,
                       _file_select_ok_cb, NULL);
   e_dialog_button_add(dia, _("Cancel"), NULL,
                       _file_select_cancel_cb, NULL);
   elm_win_center(dia->win, 1, 1);
   o = evas_object_rectangle_add(evas_object_evas_get(dia->win));
   if (!evas_object_key_grab(o, "Return", mask, ~mask, 0))
     printf("grab err\n");
   mask = 0;
   if (!evas_object_key_grab(o, "KP_Enter", mask, ~mask, 0))
     printf("grab err\n");
   mask = 0;
   if (!evas_object_key_grab(o, "Escape", mask, ~mask, 0))
     printf("grab err\n");
   evas_object_event_callback_add(o, EVAS_CALLBACK_KEY_DOWN,
                                  _save_key_down_cb, NULL);
   e_dialog_show(dia);
}

static void
_share_done(void)
{
   E_FREE_LIST(handlers, ecore_event_handler_del);
   o_label = NULL;
   E_FREE(url_ret);
   E_FREE_FUNC(url_up, ecore_con_url_free);
   url_up = NULL;
}

static void
_upload_ok_cb(void *data EINA_UNUSED, E_Dialog *dia)
{
   // ok just hides dialog and does background upload
   o_label = NULL;
   if (dia) e_util_defer_object_del(E_OBJECT(dia));
   if (!win) return;
   E_FREE_FUNC(win, evas_object_del);
}

static void
_upload_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia)
{
   o_label = NULL;
   if (dia) e_util_defer_object_del(E_OBJECT(dia));
   E_FREE_FUNC(win, evas_object_del);
   _share_done();
}

static Eina_Bool
_upload_data_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event)
{
   Ecore_Con_Event_Url_Data *ev = event;

   if (ev->url_con != url_up) return EINA_TRUE;
   if ((o_label) && (ev->size < 1024))
     {
        char *txt = alloca(ev->size + 1);

        memcpy(txt, ev->data, ev->size);
        txt[ev->size] = 0;
/*
        printf("GOT %i bytes: '%s'\n", ev->size, txt);
        int i;
        for (i = 0; i < ev->size; i++) printf("%02x.", ev->data[i]);
        printf("\n");
 */
        if (!url_ret) url_ret = strdup(txt);
        else
          {
             char *n;

             n = malloc(strlen(url_ret) + ev->size + 1);
             if (n)
               {
                  strcpy(n, url_ret);
                  free(url_ret);
                  strcat(n, txt);
                  url_ret = n;
               }
          }
     }
   return EINA_FALSE;
}

static Eina_Bool
_upload_progress_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event)
{
   size_t total, current;
   Ecore_Con_Event_Url_Progress *ev = event;

   if (ev->url_con != url_up) return ECORE_CALLBACK_RENEW;
   total = ev->up.total;
   current = ev->up.now;
   if (o_label)
     {
        char buf[1024];
        char *buf_now, *buf_total;

        buf_now = e_util_size_string_get(current);
        buf_total = e_util_size_string_get(total);
        snprintf(buf, sizeof(buf), _("Uploaded %s / %s"), buf_now, buf_total);
        E_FREE(buf_now);
        E_FREE(buf_total);
        e_widget_label_text_set(o_label, buf);
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_upload_complete_cb(void *data, int ev_type EINA_UNUSED, void *event)
{
   int status;
   Ecore_Con_Event_Url_Complete *ev = event;

   if (ev->url_con != url_up) return ECORE_CALLBACK_RENEW;
   status = ev->status;

   if (data)
     e_widget_disabled_set(data, 1);
   if (status != 200)
     {
        e_util_dialog_show(_("Error - Upload Failed"),
                           _("Upload failed with status code:<ps/>"
                             "%i"), status);
        _share_done();
        return EINA_FALSE;
     }
   if ((o_entry) && (url_ret))
      e_widget_entry_text_set(o_entry, url_ret);
   _share_done();
   return ECORE_CALLBACK_RENEW;
}

static void
_win_share_del(void *data EINA_UNUSED)
{
   if (handlers)
     ecore_event_handler_data_set(eina_list_last_data_get(handlers), NULL);
   _upload_cancel_cb(NULL, NULL);
   if (cd) e_object_del(E_OBJECT(cd));
}

static void
_win_share_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED)
{
   E_Dialog *dia;
   Evas_Object *o, *ol;
   Evas_Coord mw, mh;
   char buf[PATH_MAX];
   FILE *f;
   int i, fd = -1;

   srand(time(NULL));
   for (i = 0; i < 10240; i++)
     {
        int v = rand();

        if (quality == 100)
          snprintf(buf, sizeof(buf), "/tmp/e-shot-%x.png", v);
        else
          snprintf(buf, sizeof(buf), "/tmp/e-shot-%x.jpg", v);
        fd = open(buf, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
        if (fd >= 0) break;
     }
   if (fd < 0)
     {
        e_util_dialog_show(_("Error - Can't create file"),
                           _("Cannot create temporary file '%s': %s"),
                           buf, strerror(errno));
        E_FREE_FUNC(win, evas_object_del);
        return;
     }
   _save_to(buf);
   E_FREE_FUNC(win, evas_object_del);
   f = fdopen(fd, "rb");
   if (!f)
     {
        e_util_dialog_show(_("Error - Can't open file"),
                           _("Cannot open temporary file '%s': %s"),
                           buf, strerror(errno));
        return;
     }
   fseek(f, 0, SEEK_END);
   fsize = ftell(f);
   if (fsize < 1)
     {
        e_util_dialog_show(_("Error - Bad size"),
                           _("Cannot get size of file '%s'"),
                           buf);
        fclose(f);
        return;
     }
   rewind(f);
   free(fdata);
   fdata = malloc(fsize);
   if (!fdata)
     {
        e_util_dialog_show(_("Error - Can't allocate memory"),
                           _("Cannot allocate memory for picture: %s"),
                           strerror(errno));
        fclose(f);
        return;
     }
   if (fread(fdata, fsize, 1, f) != 1)
     {
        e_util_dialog_show(_("Error - Can't read picture"),
                           _("Cannot read picture"));
        E_FREE(fdata);
        fclose(f);
        return;
     }
   fclose(f);
   ecore_file_unlink(buf);

   _share_done();

   E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_URL_DATA,
                         _upload_data_cb, NULL);
   E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_URL_PROGRESS,
                         _upload_progress_cb, NULL);

   url_up = ecore_con_url_new("https://www.enlightenment.org/shot.php");
   // why use http 1.1? proxies like squid don't handle 1.1 posts with expect
   // like curl uses by default, so go to 1.0 and this all works dandily
   // out of the box
   ecore_con_url_http_version_set(url_up, ECORE_CON_URL_HTTP_VERSION_1_0);
   ecore_con_url_post(url_up, fdata, fsize, "application/x-e-shot");
   dia = e_dialog_new(NULL, "E", "_e_shot_share");
   e_dialog_resizable_set(dia, EINA_TRUE);
   e_dialog_title_set(dia, _("Uploading screenshot"));

   o = e_widget_list_add(evas_object_evas_get(dia->win), 0, 0);
   ol = o;

   o = e_widget_label_add(evas_object_evas_get(dia->win), _("Uploading ..."));
   o_label = o;
   e_widget_list_object_append(ol, o, 0, 0, 0.5);

   o = e_widget_label_add(evas_object_evas_get(dia->win), 
                          _("Screenshot is available at this location:"));
   e_widget_list_object_append(ol, o, 0, 0, 0.5);

   o = e_widget_entry_add(dia->win, NULL, NULL, NULL, NULL);
   o_entry = o;
   e_widget_list_object_append(ol, o, 1, 0, 0.5);

   e_widget_size_min_get(ol, &mw, &mh);
   e_dialog_content_set(dia, ol, mw, mh);
   e_dialog_button_add(dia, _("Hide"), NULL, _upload_ok_cb, NULL);
   e_dialog_button_add(dia, _("Cancel"), NULL, _upload_cancel_cb, NULL);
   e_object_del_attach_func_set(E_OBJECT(dia), _win_share_del);
   E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_URL_COMPLETE,
                         _upload_complete_cb,
                         eina_list_last_data_get(dia->buttons));
   elm_win_center(dia->win, 1, 1);
   e_dialog_show(dia);
}

static void
_win_share_confirm_del(void *d EINA_UNUSED)
{
   cd = NULL;
}

static void
_win_share_confirm_yes(void *d EINA_UNUSED)
{
   _win_share_cb(NULL, NULL);
}

static void
_win_share_confirm_cb(void *d EINA_UNUSED, void *d2 EINA_UNUSED)
{
   if (cd) return;
   cd = e_confirm_dialog_show(_("Confirm Share"), NULL,
                              _("This image will be uploaded<ps/>"
                                "to enlightenment.org. It will be publicly visible."),
                              _("Confirm"), _("Cancel"),
                              _win_share_confirm_yes, NULL,
                              NULL, NULL, _win_share_confirm_del, NULL);
}

static void
_rect_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Down *ev = event_info;
   Eina_List *l;
   E_Zone *z;

   if (ev->button != 1) return;

   e_widget_radio_toggle_set(o_radio_all, 0);
   EINA_LIST_FOREACH(e_comp->zones, l, z)
     {
        if (obj == o_rectdim[z->num])
          {
             screen = z->num;
             e_widget_radio_toggle_set(o_radio[z->num], 1);
          }
        else
           e_widget_radio_toggle_set(o_radio[z->num], 0);
     }

   EINA_LIST_FOREACH(e_comp->zones, l, z)
     {
        if (screen == -1)
           evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0);
        else if (screen == (int)z->num)
           evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0);
        else
           evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 200);
     }
}

static void
_save_dialog_show(E_Zone *zone, E_Client *ec, const char *params, void *dst, int sw, int sh)
{
   Evas *evas, *evas2;
   Evas_Object *o, *oa, *op, *ol;
   Evas_Modifier_Mask mask;
   E_Radio_Group *rg;
   int w, h;
   char smode[128], squal[128], sscreen[128];

   win = elm_win_add(NULL, NULL, ELM_WIN_BASIC);

   evas = evas_object_evas_get(win);
   elm_win_title_set(win, _("Where to put Screenshot..."));
   evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, _win_delete_cb, NULL);
   elm_win_center(win, 1, 1);
   ecore_evas_name_class_set(e_win_ee_get(win), "E", "_shot_dialog");

   o = elm_layout_add(e_win_evas_win_get(evas));
   elm_win_resize_object_add(win, o);
   o_bg = o;;
   e_theme_edje_object_set(o, "base/theme/dialog", "e/widgets/dialog/main");
   evas_object_show(o);

   o = e_widget_list_add(evas, 0, 0);
   o_content = o;
   elm_object_part_content_set(o_bg, "e.swallow.content", o);
   evas_object_show(o);

   w = sw / 4;
   if (w < 220) w = 220;
   h = (w * sh) / sw;

   o = e_widget_aspect_add(evas, w, h);
   oa = o;
   o = e_widget_preview_add(evas, w, h);
   op = o;

   evas2 = e_widget_preview_evas_get(op);

   o = evas_object_image_filled_add(evas2);
   o_img = o;
   evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888);
   evas_object_image_alpha_set(o, EINA_FALSE);
   evas_object_image_size_set(o, sw, sh);
   evas_object_image_data_copy_set(o, dst);

   evas_object_image_data_update_add(o, 0, 0, sw, sh);
   e_widget_preview_extern_object_set(op, o);
   evas_object_show(o);

   evas_object_show(op);
   evas_object_show(oa);

   e_widget_aspect_child_set(oa, op);
   e_widget_list_object_append(o_content, oa, 0, 0, 0.5);

   o = e_widget_list_add(evas, 1, 1);
   o_hlist = o;

   o = e_widget_framelist_add(evas, _("Quality"), 0);
   ol = o;

   rg = e_widget_radio_group_new(&quality);
   o = e_widget_radio_add(evas, _("Perfect"), 100, rg);
   e_widget_framelist_object_append(ol, o);
   o = e_widget_radio_add(evas, _("High"), 90, rg);
   e_widget_framelist_object_append(ol, o);
   o = e_widget_radio_add(evas, _("Medium"), 70, rg);
   e_widget_framelist_object_append(ol, o);
   o = e_widget_radio_add(evas, _("Low"), 50, rg);
   e_widget_framelist_object_append(ol, o);

   e_widget_list_object_append(o_hlist, ol, 1, 0, 0.5);

   if (zone)
     {
        screen = -1;
        if (eina_list_count(e_comp->zones) > 1)
          {
             Eina_List *l;
             E_Zone *z;
             int i;

             o = e_widget_framelist_add(evas, _("Screen"), 0);
             ol = o;

             rg = e_widget_radio_group_new(&screen);
             o = e_widget_radio_add(evas, _("All"), -1, rg);
             o_radio_all = o;
             evas_object_smart_callback_add(o, "changed",
                                            _screen_change_cb, NULL);
             e_widget_framelist_object_append(ol, o);
             i = 0;
             EINA_LIST_FOREACH(e_comp->zones, l, z)
               {
                  char buf[32];

                  if (z->num >= MAXZONES) continue;
                  snprintf(buf, sizeof(buf), "%i", z->num);
                  o = e_widget_radio_add(evas, buf, z->num, rg);
                  o_radio[z->num] = o;
                  evas_object_smart_callback_add(o, "changed",
                                                 _screen_change_cb, NULL);
                  e_widget_framelist_object_append(ol, o);

                  o = evas_object_rectangle_add(evas2);
                  evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,
                                                 _rect_down_cb, NULL);
                  o_rectdim[z->num] = o;
                  evas_object_color_set(o, 0, 0, 0, 0);
                  evas_object_show(o);
                  evas_object_geometry_get(o_img, NULL, NULL, &w, &h);
                  evas_object_move(o, (z->x * w) / sw, (z->y * h) / sh);
                  evas_object_resize(o, (z->w * w) / sw, (z->h * h) / sh);
                  i++;
               }

             e_widget_list_object_append(o_hlist, ol, 1, 0, 0.5);
          }

     }
   e_widget_list_object_append(o_content, o_hlist, 0, 0, 0.5);

   o = o_content;
   e_widget_size_min_get(o, &w, &h);
   evas_object_size_hint_min_set(o, w, h);
   elm_object_part_content_set(o_bg, "e.swallow.content", o);
   evas_object_show(o);

   ///////////////////////////////////////////////////////////////////////

   o = e_widget_list_add(evas, 1, 1);
   o_box = o;
   e_widget_on_focus_hook_set(o, _on_focus_cb, NULL);
   elm_object_part_content_set(o_bg, "e.swallow.buttons", o);

   o = e_widget_button_add(evas, _("Save"), NULL, _win_save_cb, win, NULL);
   e_widget_list_object_append(o_box, o, 1, 0, 0.5);
   o = e_widget_button_add(evas, _("Share"), NULL,
                           _win_share_confirm_cb, win, NULL);
   e_widget_list_object_append(o_box, o, 1, 0, 0.5);
   o = e_widget_button_add(evas, _("Cancel"), NULL, _win_cancel_cb, win, NULL);
   e_widget_list_object_append(o_box, o, 1, 0, 0.5);

   o = o_box;
   e_widget_size_min_get(o, &w, &h);
   evas_object_size_hint_min_set(o, w, h);
   elm_object_part_content_set(o_bg, "e.swallow.buttons", o);

   o = evas_object_rectangle_add(evas);
   o_event = o;
   mask = 0;
   if (!evas_object_key_grab(o, "Tab", mask, ~mask, 0)) printf("grab err\n");
   mask = evas_key_modifier_mask_get(evas, "Shift");
   if (!evas_object_key_grab(o, "Tab", mask, ~mask, 0)) printf("grab err\n");
   mask = 0;
   if (!evas_object_key_grab(o, "Return", mask, ~mask, 0)) printf("grab err\n");
   mask = 0;
   if (!evas_object_key_grab(o, "KP_Enter", mask, ~mask, 0)) printf("grab err\n");
   mask = 0;
   if (!evas_object_key_grab(o, "space", mask, ~mask, 0)) printf("grab err\n");
   mask = 0;
   if (!evas_object_key_grab(o, "Escape", mask, ~mask, 0)) printf("grab err\n");
   evas_object_event_callback_add(o, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, NULL);

   evas_object_size_hint_min_get(o_bg, &w, &h);
   evas_object_resize(o_bg, w, h);
   evas_object_resize(win, w, h);
   evas_object_size_hint_min_set(win, w, h);
   evas_object_size_hint_max_set(win, 99999, 99999);

   if ((params) &&
       (sscanf(params, "%100s %100s %100s", smode, squal, sscreen) == 3))
     {
        screen = -1;
        if ((zone) && (!strcmp(sscreen, "current"))) screen = zone->num;
        else if (!strcmp(sscreen, "all")) screen = -1;
        else screen = atoi(sscreen);

        quality = 90;
        if (!strcmp(squal, "perfect")) quality = 100;
        else if (!strcmp(squal, "high")) quality = 90;
        else if (!strcmp(squal, "medium")) quality = 70;
        else if (!strcmp(squal, "low")) quality = 50;
        else quality = atoi(squal);

        if (!strcmp(smode, "save")) _win_save_cb(NULL, NULL);
        else if (!strcmp(smode, "share"))  _win_share_cb(NULL, NULL);
     }
   else
     {
        evas_object_show(win);
        e_win_client_icon_set(win, "screenshot");

        if (!e_widget_focus_get(o_bg)) e_widget_focus_set(o_box, 1);
        if (ec)
          {
             E_Client *c = e_win_client_get(win);

             if (c) evas_object_layer_set(c->frame, ec->layer);
          }
     }
}

static void
_shot_post(void *buffer EINA_UNUSED, Evas *e EINA_UNUSED, void *event EINA_UNUSED)
{
   int w, h;
   evas_object_geometry_get(snap, NULL, NULL, &w, &h);
   evas_event_callback_del(e_comp->evas, EVAS_CALLBACK_RENDER_POST, _shot_post);
   _save_dialog_show(shot_zone, shot_ec, shot_params, (void*)evas_object_image_data_get(snap, 0), w, h);
   E_FREE_FUNC(snap, evas_object_del);
   shot_ec = NULL;
   shot_zone = NULL;
   E_FREE(shot_params);
}

static void
_shot_now(E_Zone *zone, E_Client *ec, const char *params)
{
   int x, y, w, h;
   if ((win) || (url_up) || snap) return;
   if ((!zone) && (!ec)) return;
   if (zone)
     {
        w = e_comp->w;
        h = e_comp->h;
        x = y = 0;
     }
   else
     {
        int pad = 0;

        if (params)
          {
             const char *p = strstr(params, "pad ");

             if (p)
               {
                  pad = atoi(p + 4);
                  if (pad < 0) pad = 0;
               }
          }
        x = ec->x - pad;
        y = ec->y - pad;
        w = ec->w + (pad * 2);
        h = ec->h + (pad * 2);
        x = E_CLAMP(x, 0, e_comp->w);
        y = E_CLAMP(y, 0, e_comp->h);
        w = E_CLAMP(w, 1, e_comp->w);
        h = E_CLAMP(h, 1, e_comp->h);
     }
   if (eina_streq(ecore_evas_engine_name_get(e_comp->ee), "buffer"))
     {
        _save_dialog_show(zone, ec, params, (void*)ecore_evas_buffer_pixels_get(e_comp->ee), w, h);
        return;
     }
   shot_ec = ec;
   shot_zone = zone;
   shot_params = eina_strdup(params);
   snap = evas_object_image_filled_add(e_comp->evas);
   evas_object_pass_events_set(snap, 1);
   evas_object_layer_set(snap, EVAS_LAYER_MAX);
   evas_object_image_snapshot_set(snap, 1);
   evas_object_geometry_set(snap, x, y, w, h);
   evas_object_show(snap);
   evas_object_image_data_update_add(snap, 0, 0, w, h);
   evas_object_image_pixels_dirty_set(snap, 1);
   evas_event_callback_add(e_comp->evas, EVAS_CALLBACK_RENDER_POST, _shot_post, snap);
   ecore_evas_manual_render(e_comp->ee);
}

static Eina_Bool
_shot_delay(void *data)
{
   timer = NULL;
   _shot_now(data, NULL, NULL);

   return EINA_FALSE;
}

static Eina_Bool
_shot_delay_border(void *data)
{
   border_timer = NULL;
   _shot_now(NULL, data, NULL);

   return EINA_FALSE;
}

static Eina_Bool
_shot_delay_border_padded(void *data)
{
   char buf[128];

   border_timer = NULL;
   snprintf(buf, sizeof(buf), "pad %i", (int)(64 * e_scale));
   _shot_now(NULL, data, buf);

   return EINA_FALSE;
}

static void
_shot_border(E_Client *ec)
{
   if (border_timer) ecore_timer_del(border_timer);
   border_timer = ecore_timer_loop_add(1.0, _shot_delay_border, ec);
}

static void
_shot_border_padded(E_Client *ec)
{
   if (border_timer) ecore_timer_del(border_timer);
   border_timer = ecore_timer_loop_add(1.0, _shot_delay_border_padded, ec);
}

static void
_shot(E_Zone *zone)
{
   if (timer) ecore_timer_del(timer);
   timer = ecore_timer_loop_add(1.0, _shot_delay, zone);
}

static void
_e_mod_menu_border_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   _shot_border(data);
}

static void
_e_mod_menu_border_padded_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   _shot_border_padded(data);
}

static void
_e_mod_menu_cb(void *data EINA_UNUSED, E_Menu *m, E_Menu_Item *mi EINA_UNUSED)
{
   if (m->zone) _shot(m->zone);
}

static void
_e_mod_action_border_cb(E_Object *obj EINA_UNUSED, const char *params)
{
   E_Client *ec;

   ec = e_client_focused_get();
   if (!ec) return;
   if (border_timer)
     {
        ecore_timer_del(border_timer);
        border_timer = NULL;
     }
   _shot_now(NULL, ec, params);
}

typedef struct
{
   E_Zone *zone;
   char *params;
} Delayed_Shot;

static void
_delayed_shot(void *data)
{
   Delayed_Shot *ds = data;

   _shot_now(ds->zone, NULL, ds->params);
   e_object_unref(E_OBJECT(ds->zone));
   free(ds->params);
   free(ds);
}

static void
_e_mod_action_cb(E_Object *obj, const char *params)
{
   E_Zone *zone = NULL;
   Delayed_Shot *ds;

   if (obj)
     {
        if (obj->type == E_COMP_TYPE)
          zone = e_zone_current_get();
        else if (obj->type == E_ZONE_TYPE)
          zone = ((void *)obj);
        else
          zone = e_zone_current_get();
     }
   if (!zone) zone = e_zone_current_get();
   if (!zone) return;
   E_FREE_FUNC(timer, ecore_timer_del);
   ds = E_NEW(Delayed_Shot, 1);
   e_object_ref(E_OBJECT(zone));
   ds->zone = zone;
   ds->params = params ? strdup(params) : NULL;
   /* forced main loop iteration in screenshots causes bugs if the action
    * executes immediately
    */
   ecore_job_add(_delayed_shot, ds);
}

static void
_bd_hook(void *d EINA_UNUSED, E_Client *ec)
{
   E_Menu_Item *mi;
   E_Menu *m;
   Eina_List *l;

   if (!ec->border_menu) return;
   if (ec->iconic || (ec->desk != e_desk_current_get(ec->zone))) return;
   m = ec->border_menu;

   /* position menu item just before first separator */
   EINA_LIST_FOREACH(m->items, l, mi)
     if (mi->separator) break;
   if ((!mi) || (!mi->separator)) return;
   l = eina_list_prev(l);
   mi = eina_list_data_get(l);
   if (!mi) return;

   mi = e_menu_item_new_relative(m, mi);
   e_menu_item_label_set(mi, _("Take Shot"));
   e_util_menu_item_theme_icon_set(mi, "screenshot");
   e_menu_item_callback_set(mi, _e_mod_menu_border_cb, ec);
   mi = e_menu_item_new_relative(m, mi);
   e_menu_item_label_set(mi, _("Take Padded Shot"));
   e_util_menu_item_theme_icon_set(mi, "screenshot");
   e_menu_item_callback_set(mi, _e_mod_menu_border_padded_cb, ec);
}

static void
_e_mod_menu_add(void *data EINA_UNUSED, E_Menu *m)
{
   E_Menu_Item *mi;

   mi = e_menu_item_new(m);
   e_menu_item_label_set(mi, _("Take Screenshot"));
   e_util_menu_item_theme_icon_set(mi, "screenshot");
   e_menu_item_callback_set(mi, _e_mod_menu_cb, NULL);
}

/* module setup */
E_API E_Module_Api e_modapi =
{
   E_MODULE_API_VERSION,
     "Shot"
};

E_API void *
e_modapi_init(E_Module *m)
{
   if (!ecore_con_url_init())
     {
        e_util_dialog_show(_("Shot Error"),
                           _("Cannot initialize network"));
        return NULL;
     }

   e_module_delayed_set(m, 1);

   shot_module = m;
   act = e_action_add("shot");
   if (act)
     {
        act->func.go = _e_mod_action_cb;
        e_action_predef_name_set(N_("Screen"), N_("Take Screenshot"),
                                 "shot", NULL,
                                 "syntax: [share|save [perfect|high|medium|low|QUALITY current|all|SCREEN-NUM]", 1);
     }
   border_act = e_action_add("border_shot");
   if (border_act)
     {
        border_act->func.go = _e_mod_action_border_cb;
        e_action_predef_name_set(N_("Window : Actions"), N_("Take Shot"),
                                 "border_shot", NULL,
                                 "syntax: [share|save perfect|high|medium|low|QUALITY all|current] [pad N]", 1);
     }
   maug = e_int_menus_menu_augmentation_add_sorted
     ("main/2",  _("Take Screenshot"), _e_mod_menu_add, NULL, NULL, NULL);
   border_hook = e_int_client_menu_hook_add(_bd_hook, NULL);

   return m;
}

E_API int
e_modapi_shutdown(E_Module *m EINA_UNUSED)
{
   _share_done();
   E_FREE_FUNC(win, evas_object_del);
   E_FREE_FUNC(cd, e_object_del);
   if (timer)
     {
        ecore_timer_del(timer);
        timer = NULL;
     }
   if (maug)
     {
        e_int_menus_menu_augmentation_del("main/2", maug);
        maug = NULL;
     }
   if (act)
     {
        e_action_predef_name_del("Screen", "Take Screenshot");
        e_action_del("shot");
        act = NULL;
     }

   shot_module = NULL;
   e_int_client_menu_hook_del(border_hook);
   ecore_con_url_shutdown();
   return 1;
}

E_API int
e_modapi_save(E_Module *m EINA_UNUSED)
{
   return 1;
}
