#include "private.h"
#include "elm_widget.h"
#include <sys/types.h>
#include <regex.h>
#include "elm_entry_eo.h"

#define BLINK_INTERVAL 0.1

static Elm_Prefs_Item_Type supported_types[] =
{
   ELM_PREFS_TYPE_TEXT,
   ELM_PREFS_TYPE_TEXTAREA,
   ELM_PREFS_TYPE_UNKNOWN
};

static Eina_Bool
_color_change_do(void *data)
{
   Evas_Object *obj = data;
   int color;

   color = (int)(uintptr_t)evas_object_data_get(obj, "current_color");
   switch (color)
     {
      case 0:
        evas_object_data_set(obj, "current_color", (void *)1);
        evas_object_color_set(obj, 255, 0, 0, 255);    /* 1st red */
        goto renew;

      case 1:
        evas_object_data_set(obj, "current_color", (void *)2);
        evas_object_color_set(obj, 255, 255, 255, 255);    /* 2nd white */
        goto renew;

      case 2:
        evas_object_data_set(obj, "current_color", (void *)3);
        evas_object_color_set(obj, 255, 0, 0, 255);    /* 2nd red */
        goto renew;

      case 3:
      default:
        evas_object_data_set(obj, "current_color", (void *)0);
        evas_object_color_set(obj, 255, 255, 255, 255);    /* back to white */
        goto end;
     }

renew:
   return ECORE_CALLBACK_RENEW;

end:
   evas_object_data_del(obj, "timer");
   return ECORE_CALLBACK_CANCEL;
}

static Eina_Bool
elm_prefs_entry_value_validate(Evas_Object *obj)
{
   Ecore_Timer *timer;
   const char *val;
   regex_t *regex;
   size_t min;

   val = elm_entry_entry_get(obj);
   if (!val) return EINA_FALSE;

   regex = evas_object_data_get(obj, "accept_regex");
   if (regex)
     {
        if (regexec(regex, val, 0, NULL, 0)) goto mismatch;
     }

   regex = evas_object_data_get(obj, "deny_regex");
   if (regex)
     {
        /* we want tokens *out* of the deny language */
        if (!regexec(regex, val, 0, NULL, 0)) goto mismatch;
     }

   min = (size_t) evas_object_data_get(obj, "min_size");
   if (min)
     {
        if (strlen(val) < min) goto mismatch;
     }

   return EINA_TRUE;

mismatch:
   evas_object_color_set(obj, 255, 0, 0, 255);

   timer = evas_object_data_get(obj, "timer");
   if (timer) ecore_timer_del(timer);

   evas_object_data_set
     (obj, "timer", ecore_timer_add(BLINK_INTERVAL, _color_change_do, obj));

   return EINA_FALSE;
}

static void
_item_changed_cb(void *data, const Efl_Event *event)
{
   Elm_Prefs_Item_Changed_Cb prefs_it_changed_cb = data;

   if (efl_ui_focus_object_focus_get(event->object))
     prefs_it_changed_cb(event->object);
}

static void
_entry_del_cb(void *data EINA_UNUSED,
              Evas *evas EINA_UNUSED,
              Evas_Object *obj,
              void *event_info EINA_UNUSED)
{
   regex_t *regex;
   Ecore_Timer *timer;

   regex = evas_object_data_del(obj, "accept_regex");
   if (regex)
     {
        regfree(regex);
        free(regex);
     }

   regex = evas_object_data_del(obj, "deny_regex");
   if (regex)
     {
        regfree(regex);
        free(regex);
     }

   timer = evas_object_data_del(obj, "timer");
   if (timer) ecore_timer_del(timer);

   evas_object_data_del(obj, "min_size");
}

static Evas_Object *
elm_prefs_entry_add(const Elm_Prefs_Item_Iface *iface EINA_UNUSED,
                    Evas_Object *prefs,
                    const Elm_Prefs_Item_Type type,
                    const Elm_Prefs_Item_Spec spec,
                    Elm_Prefs_Item_Changed_Cb cb)
{
   Evas_Object *obj = elm_entry_add(prefs);
   regex_t *regex = NULL;
   char buf[256];

   Elm_Entry_Filter_Limit_Size limit = {
        .max_char_count = spec.s.length.max
   };

   evas_object_data_set(obj, "prefs_type", (void *)type);

   /* FIXME: have this warning animation on the theme, later */

   /* 0: orig. white; 1: 1st red; 2: 2nd white; 3: 2o red */
   evas_object_data_set(obj, "current_color", 0);

   /* FIXME: is it worth to ERR with the item's name, too, here? */

   efl_event_callback_add
     (obj, ELM_ENTRY_EVENT_ACTIVATED, _item_changed_cb, cb);
   efl_event_callback_add
     (obj, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_CHANGED, _item_changed_cb, cb);
   if (spec.s.accept)
     {
        int ret;

        regex = calloc(1, sizeof(regex_t));
        ret = regcomp(regex, spec.s.accept, REG_EXTENDED | REG_NOSUB);

        if (ret)
          {
             regerror(ret, regex, buf, sizeof(buf));
             regfree(regex);
             free(regex);
             ERR("bad regular expression (%s) on item's 'accept' tag (%s)."
                 " Because of that, the 'accept' tag will be dropped for the "
                 "item.", spec.s.accept, buf);
          }
        else
          evas_object_data_set(obj, "accept_regex", regex);
     }

   if (spec.s.deny)
     {
        int ret;

        regex = calloc(1, sizeof(regex_t));
        ret = regcomp(regex, spec.s.deny, REG_EXTENDED | REG_NOSUB);

        if (ret)
          {
             regerror(ret, regex, buf, sizeof(buf));
             regfree(regex);
             free(regex);
             ERR("bad regular expression (%s) on item's 'deny' tag (%s)."
                 " Because of that, the 'deny' tag will be dropped for the "
                 "item.", spec.s.deny, buf);
          }
        else
          evas_object_data_set(obj, "deny_regex", regex);
     }

   if (spec.s.length.min) /* zero makes no sense */
     {
        size_t min = (size_t) spec.s.length.min;
        evas_object_data_set(obj, "min_size", (void *) min);
     }

   evas_object_event_callback_add
     (obj, EVAS_CALLBACK_DEL, _entry_del_cb, NULL);

   elm_entry_scrollable_set(obj, EINA_TRUE);

   if (type == ELM_PREFS_TYPE_TEXT)
     elm_entry_single_line_set(obj, EINA_TRUE);

   elm_entry_markup_filter_append(obj, elm_entry_filter_limit_size, &limit);
   elm_layout_text_set(obj, NULL, spec.s.placeholder);

   return obj;
}

/* already expects an EINA_VALUE_TYPE_STRINGSHARE one */
static Eina_Bool
elm_prefs_entry_value_set(Evas_Object *obj,
                          Eina_Value *value)
{
   const char *val;

   eina_value_get(value, &val);

   return elm_layout_text_set(obj, NULL, val);
}

static Eina_Bool
elm_prefs_entry_value_get(Evas_Object *obj,
                          Eina_Value *value)
{
   const char *val;

   val = elm_layout_text_get(obj, NULL);

   if (!eina_value_setup(value, EINA_VALUE_TYPE_STRINGSHARE))
     return EINA_FALSE;
   if (!eina_value_set(value, val)) return EINA_FALSE;

   return EINA_TRUE;
}

static Eina_Bool
elm_prefs_entry_icon_set(Evas_Object *obj,
                         const char *icon)
{
   Evas_Object *ic = elm_icon_add(obj);

   elm_icon_standard_set(ic, icon);

   return elm_layout_content_set(obj, "icon", ic);
}

static Eina_Bool
elm_prefs_entry_editable_set(Evas_Object *obj,
                             Eina_Bool editable)
{
   elm_entry_editable_set(obj, editable);

   return EINA_TRUE;
}

static Eina_Bool
elm_prefs_entry_editable_get(Evas_Object *obj)
{
   return elm_entry_editable_get(obj);
}

static Eina_Bool
elm_prefs_entry_expand_want(Evas_Object *obj EINA_UNUSED)
{
   return EINA_TRUE;
}

PREFS_ITEM_WIDGET_ADD(entry,
                      supported_types,
                      elm_prefs_entry_value_set,
                      elm_prefs_entry_value_get,
                      elm_prefs_entry_value_validate,
                      NULL,
                      elm_prefs_entry_icon_set,
                      elm_prefs_entry_editable_set,
                      elm_prefs_entry_editable_get,
                      elm_prefs_entry_expand_want);
