#include "xkbswitch.h"

typedef struct _Instance
{
   Evas_Object         *o_main;
   Evas_Object         *o_xkbswitch;
   Evas_Object         *o_xkbflag;
   Evas_Object         *menu;
   Evas_Object         *popup;
   E_Gadget_Site_Orient orient;
   E_Config_XKB_Layout *layout;
   Ecore_Timer         *menu_timer;
   unsigned int         menu_timestamp;
} Instance;

static Eina_List *ginstances = NULL;
static Ecore_Event_Handler *xkbg_change_handle = NULL;

void
_xkbg_update_icon(int cur_group)
{
   Instance *inst;
   Eina_List *l;
   E_Config_XKB_Layout *cl;

   EINA_SAFETY_ON_NULL_RETURN(e_config->xkb.used_layouts);
   //INF("ui: %d", cur_group);
   cl = eina_list_nth(e_config->xkb.used_layouts, cur_group);
   EINA_SAFETY_ON_NULL_RETURN(cl);
   if (!e_config_xkb_layout_eq(cl, e_config->xkb.current_layout))
     {
        e_config_xkb_layout_free(e_config->xkb.current_layout);
        e_config->xkb.current_layout = e_config_xkb_layout_dup(cl);
     }

   if (e_config->xkb.only_label)
     {
        EINA_LIST_FOREACH(ginstances, l, inst)
          {
             if (!e_config_xkb_layout_eq(e_config->xkb.current_layout, inst->layout))
               inst->layout = e_config->xkb.current_layout;
             E_FREE_FUNC(inst->o_xkbflag, evas_object_del);
             e_theme_edje_object_set(inst->o_xkbswitch,
                                     "base/theme/gadget/xkbswitch",
                                     "e/gadget/xkbswitch/noflag");
             elm_layout_text_set(inst->o_xkbswitch,
                                 "e.text.label", cl->name);
          }
     }
   else
     {
        EINA_LIST_FOREACH(ginstances, l, inst)
          {
             if (!e_config_xkb_layout_eq(e_config->xkb.current_layout, inst->layout))
               inst->layout = e_config->xkb.current_layout;
             if (!inst->o_xkbflag)
               inst->o_xkbflag = e_icon_add(evas_object_evas_get(inst->o_xkbswitch));
             e_theme_edje_object_set(inst->o_xkbswitch,
                                     "base/theme/gadget/xkbswitch",
                                     "e/gadget/xkbswitch/main");
             e_xkb_e_icon_flag_setup(inst->o_xkbflag, cl->name);
             elm_layout_content_set(inst->o_xkbswitch, "e.swallow.flag",
                                    inst->o_xkbflag);
             elm_layout_text_set(inst->o_xkbswitch, "e.text.label",
                                 e_xkb_layout_name_reduce(cl->name));
          }
     }
}

static Eina_Bool
_xkbg_changed_state(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
{
   _xkbg_update_icon(e_config->xkb.cur_group);
   return ECORE_CALLBACK_PASS_ON;
}

static Evas_Object *
_xkbg_gadget_configure(Evas_Object *g EINA_UNUSED)
{
   if (e_configure_registry_exists("keyboard_and_mouse/xkbswitch"))
     {
        e_configure_registry_call("keyboard_and_mouse/xkbswitch", NULL, NULL);
     }
   return NULL;
}

static void
_xkbg_cb_menu_set(void *data, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   Instance *inst = evas_object_data_get(obj, "inst");
   Eina_List *l;
   int cur_group = -1, grp = -1;
   E_Config_XKB_Layout *cl2, *cl = data;

   if (inst->popup)
     elm_ctxpopup_dismiss(inst->popup);

   EINA_LIST_FOREACH(e_config->xkb.used_layouts, l, cl2)
     {
        grp++;
        if (cl2 == cl) cur_group = grp;
     }
   if (cur_group == -1) return;
   e_xkb_layout_set(cl);
   e_config_xkb_layout_free(e_config->xkb.sel_layout);
   e_config->xkb.sel_layout = e_config_xkb_layout_dup(cl);
   if (e_comp->comp_type == E_PIXMAP_TYPE_WL)
#ifdef HAVE_WAYLAND
     e_comp_wl_input_keymap_index_set(cur_group);
#else
     (void)cur_group;
#endif
}

static void
_xkbg_popup_dismissed(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   Instance *inst = data;
   E_FREE_FUNC(obj, evas_object_del);
   inst->popup = NULL;
}

static void
_xkbg_popup_deleted(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Instance *inst = data;
   inst->popup = NULL;
}

static Eina_Bool
_xkbg_popup_cb(void *data)
{
   Instance *inst;
   E_Config_XKB_Layout *cl, *cur;
   Eina_List *l;
   Elm_Object_Item *mi;
   char buf[4096], buf2[4096];

   inst = data;
   inst->menu_timer = NULL;
   cur = e_xkb_layout_get();

   inst->popup = elm_ctxpopup_add(e_comp->elm);
   elm_object_style_set(inst->popup, "noblock");
   evas_object_smart_callback_add(inst->popup, "dismissed", _xkbg_popup_dismissed, inst);
   evas_object_event_callback_add(inst->popup, EVAS_CALLBACK_DEL, _xkbg_popup_deleted, inst);

   inst->menu = elm_list_add(inst->popup);
   elm_list_select_mode_set(inst->menu, ELM_OBJECT_SELECT_MODE_ALWAYS);
   evas_object_data_set(inst->menu, "inst", inst);
   elm_object_content_set(inst->popup, inst->menu);
   E_EXPAND(inst->menu);
   E_FILL(inst->menu);

   /* Append all the layouts */
   EINA_LIST_FOREACH(e_config->xkb.used_layouts, l, cl)
     {
        const char *name = cl->name;
        Evas_Object *ic;

        e_xkb_flag_file_get(buf, sizeof(buf), name);
        if (cl->variant)
          snprintf(buf2, sizeof(buf2), "%s (%s, %s)", cl->name, cl->model, cl->variant);
        else
          snprintf(buf2, sizeof(buf2), "%s (%s)", cl->name, cl->model);

        ic = elm_icon_add(inst->menu);
        E_EXPAND(ic);
        E_FILL(ic);
        elm_image_file_set(ic, buf, NULL);
        evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
        evas_object_show(ic);

        mi = elm_list_item_append(inst->menu, buf2, ic, NULL, _xkbg_cb_menu_set, cl);

        if (e_config_xkb_layout_eq(cur, cl))
          elm_list_item_selected_set(mi, EINA_TRUE);
     }

   evas_object_show(inst->menu);
   evas_object_size_hint_min_set(inst->popup, 200 * e_scale, 100 * e_scale);
   e_gadget_util_ctxpopup_place(inst->o_main, inst->popup, inst->o_xkbswitch);
   evas_object_show(inst->popup);
   return ECORE_CALLBACK_CANCEL;
}

static void
_xkbg_cb_mouse_up(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   Evas_Event_Mouse_Up *ev = event;
   Instance *inst = data;

   if (!inst) return;
   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if ((ev->button == 1) && (inst->popup))
     {
        elm_ctxpopup_dismiss(inst->popup);
        return;
     }
   else if ((ev->button == 2) /* Middle click */
            ||
            ((ev->button == 1) && (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)) /* double Left-click */
           )
     {
        if (inst->menu_timer)
          {
             ecore_timer_del(inst->menu_timer);
             inst->menu_timer = NULL;
          }
        e_xkb_layout_next();
     }

   else if ((ev->button == 1) && (!inst->popup)) /* Left-click layout menu */
     {
        if (!e_config->xkb.dont_touch_my_damn_keyboard)
          {
             if (!inst->menu_timer)
               {
                  inst->menu_timestamp = ev->timestamp;
#ifdef HAVE_WAYLAND_ONLY
                  inst->menu_timer = ecore_timer_add(0.25,
                                                      _xkbg_popup_cb, inst);
#else
                  inst->menu_timer = ecore_timer_add(ecore_x_double_click_time_get(),
                                                      _xkbg_popup_cb, inst);
#endif
               }
          }
     }
}

static void
_xkbg_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data EINA_UNUSED)
{
   //Evas_Coord w, h;
   Instance *inst = data;

   /* This does not work properly yet
      edje_object_parts_extends_calc(elm_layout_edje_get(inst->o_xkbswitch), 0, 0, &w, &h);
      evas_object_size_hint_aspect_set(inst->o_xkbswitch, EVAS_ASPECT_CONTROL_BOTH, w, h);*/
   evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
}

static void
_xkbg_gadget_created_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   Instance *inst = data;

   if (inst->o_main)
     {
        e_gadget_configure_cb_set(inst->o_main, _xkbg_gadget_configure);

        inst->o_xkbswitch = elm_layout_add(inst->o_main);
        E_EXPAND(inst->o_xkbswitch);
        E_FILL(inst->o_xkbswitch);

        inst->layout = e_xkb_layout_get();
        if (e_config->xkb.only_label || (!inst->layout))
          {
             if (inst->orient == E_GADGET_SITE_ORIENT_VERTICAL)
               e_theme_edje_object_set(inst->o_xkbswitch,
                                       "base/theme/gadget/xkbswitch",
                                       "e/gadget/xkbswitch/noflag_vert");
             else
               e_theme_edje_object_set(inst->o_xkbswitch,
                                       "base/theme/gadget/xkbswitch",
                                       "e/gadget/xkbswitch/noflag");
          }
        else
          {
             if (inst->orient == E_GADGET_SITE_ORIENT_VERTICAL)
               e_theme_edje_object_set(inst->o_xkbswitch,
                                       "base/theme/gadget/xkbswitch",
                                       "e/gadget/xkbswitch/main_vert");
             else
               e_theme_edje_object_set(inst->o_xkbswitch,
                                       "base/theme/gadget/xkbswitch",
                                       "e/gadget/xkbswitch/main");
          }
        elm_layout_text_set(inst->o_xkbswitch, "e.text.label",
                            inst->layout ? e_xkb_layout_name_reduce(inst->layout->name) : _("NONE"));
        if (inst->layout && (!e_config->xkb.only_label))
          {
             inst->o_xkbflag = e_icon_add(evas_object_evas_get(inst->o_xkbswitch));
             e_xkb_e_icon_flag_setup(inst->o_xkbflag, inst->layout->name);
             elm_layout_content_set(inst->o_xkbswitch, "e.swallow.flag",
                                    inst->o_xkbflag);
          }
        else inst->o_xkbflag = NULL;
        evas_object_event_callback_add(inst->o_xkbswitch, EVAS_CALLBACK_MOUSE_UP,
                                       _xkbg_cb_mouse_up, inst);
        evas_object_event_callback_add(inst->o_xkbswitch, EVAS_CALLBACK_RESIZE,
                                       _xkbg_resize_cb, inst);
        elm_box_pack_end(inst->o_main, inst->o_xkbswitch);
        evas_object_show(inst->o_xkbswitch);
     }
   evas_object_smart_callback_del_full(obj, "gadget_created", _xkbg_gadget_created_cb, data);
}

static void
xkbg_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Instance *inst = data;

   if (inst->popup)
     elm_ctxpopup_dismiss(inst->popup);
   ginstances = eina_list_remove(ginstances, inst);
   free(inst);
}

EINTERN Evas_Object *
xkbg_gadget_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient)
{
   Instance *inst;

   if (*id == 0) *id = 1;
   inst = E_NEW(Instance, 1);
   inst->o_main = elm_box_add(parent);
   inst->orient = orient;
   evas_object_smart_callback_add(parent, "gadget_created", _xkbg_gadget_created_cb, inst);
   evas_object_event_callback_add(inst->o_main, EVAS_CALLBACK_DEL, xkbg_del, inst);

   ginstances = eina_list_append(ginstances, inst);
   return inst->o_main;
}

EINTERN void
xkbg_init(void)
{
   xkbg_change_handle = ecore_event_handler_add(E_EVENT_XKB_CHANGED, _xkbg_changed_state, NULL);
}

EINTERN void
xkbg_shutdown(void)
{
   if (_xkbg.evh) ecore_event_handler_del(_xkbg.evh);
   if (_xkbg.cfd) e_object_del(E_OBJECT(_xkbg.cfd));
   _xkbg.cfd = NULL;
   ecore_event_handler_del(xkbg_change_handle);
}

