#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif

#include <Elementary.h>
#include "elm_priv.h"

#define ELM_PREFS_DATA_CHECK(prefs_data)                        \
  do                                                            \
    {                                                           \
       EINA_SAFETY_ON_NULL_RETURN(prefs_data);                  \
       if (!EINA_MAGIC_CHECK(prefs_data, ELM_PREFS_DATA_MAGIC)) \
         {                                                      \
            EINA_MAGIC_FAIL(prefs_data, ELM_PREFS_DATA_MAGIC);  \
            return;                                             \
         }                                                      \
       EINA_SAFETY_ON_TRUE_RETURN(prefs_data->refcount <= 0);   \
    }                                                           \
  while (0)

#define ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, retval)            \
  do                                                                      \
    {                                                                     \
       EINA_SAFETY_ON_NULL_RETURN_VAL(prefs_data, retval);                \
       if (!EINA_MAGIC_CHECK(prefs_data, ELM_PREFS_DATA_MAGIC))           \
         {                                                                \
            EINA_MAGIC_FAIL(prefs_data, ELM_PREFS_DATA_MAGIC);            \
            return retval;                                                \
         }                                                                \
       EINA_SAFETY_ON_TRUE_RETURN_VAL(prefs_data->refcount <= 0, retval); \
    }                                                                     \
  while (0)

/* EET-bound data */

static struct
{
   Elm_Prefs_Item_Type t;
   const char         *name;
} eet_mapping[] = {
   { ELM_PREFS_TYPE_BOOL, "boolean" },
   { ELM_PREFS_TYPE_INT, "integer" },
   { ELM_PREFS_TYPE_FLOAT, "float" },
   { ELM_PREFS_TYPE_TEXT, "text" },
   { ELM_PREFS_TYPE_TEXTAREA, "text" },
   { ELM_PREFS_TYPE_DATE, "date" },
   { ELM_PREFS_TYPE_PAGE, "page" },
   { ELM_PREFS_TYPE_UNKNOWN, NULL }
};

typedef struct _Eet_Boolean_Item
{
    Eina_Bool b;
} Eet_Boolean_Item;

typedef struct _Eet_Integer_Item
{
   int i;
} Eet_Integer_Item;

typedef struct _Eet_Float_Item
{
   float f;
} Eet_Float_Item;

typedef struct _Eet_String_Item
{
   const char *s;
} Eet_String_Item;

typedef struct _Eet_Date_Item
{
   unsigned int  d;
   unsigned int  m;
   unsigned int y;
} Eet_Date_Item;

typedef struct _Eet_Data_Item
{
   Elm_Prefs_Item_Type type;

   const char         *name;

   union
   {
      /* why structs here? ask to who designed eet unions */
      Eet_Boolean_Item b;
      Eet_Integer_Item i;
      Eet_Float_Item   f;
      Eet_String_Item  s;
      Eet_Date_Item    d;
   } value;
} Eet_Data_Item;

typedef struct _Eet_Data_Value
{
   unsigned int version;
   Eina_List   *values;
} Eet_Data_Value;

/* Run time data */
typedef struct _Elm_Prefs_Data_Item
{
   Elm_Prefs_Item_Type  type;
   Eina_Value           value;
} Elm_Prefs_Data_Item;

typedef struct _Elm_Prefs_Data_Event
{
   EINA_INLIST;

   Elm_Prefs_Data_Event_Type type;

   Elm_Prefs_Data_Event_Cb   cb;
   const void               *data;

   Eina_Bool                 deleted : 1;
} Elm_Prefs_Data_Event;

struct _Elm_Prefs_Data
{
   EINA_MAGIC;

   Eina_Hash    *keys;
   unsigned int  version;

   const char   *data_file;
   const char   *key;

   Eet_File_Mode mode;
   int           refcount;

   Ecore_Poller *saving_poller;

   int           walking;

   Eina_Inlist  *event_cbs;

   Eina_List    *deleted;

   Eina_Bool     autosave : 1;
   Eina_Bool     dirty : 1;
};

static int _elm_prefs_data_init_count = 0;
static Eet_Data_Descriptor *_bool_value_edd;
static Eet_Data_Descriptor *_int_value_edd;
static Eet_Data_Descriptor *_float_value_edd;
static Eet_Data_Descriptor *_str_value_edd;
static Eet_Data_Descriptor *_date_value_edd;

static Eet_Data_Descriptor *_values_edd;
static Eet_Data_Descriptor *_item_edd;
static Eet_Data_Descriptor *_item_unified_edd;

static const char *
_union_type_get(const void *data,
                Eina_Bool *unknow)
{
   const Elm_Prefs_Item_Type *t = data;
   int i;

   if (unknow)
     *unknow = EINA_FALSE;

   for (i = 0; eet_mapping[i].name != NULL; ++i)
     if (*t == eet_mapping[i].t)
       return eet_mapping[i].name;

   if (unknow)
     *unknow = EINA_TRUE;

   return NULL;
}

static Eina_Bool
_union_type_set(const char *type,
                void *data,
                Eina_Bool unknow)
{
   Elm_Prefs_Item_Type *t = data;
   int i;

   if (unknow)
     return EINA_FALSE;

   for (i = 0; eet_mapping[i].name != NULL; ++i)
     if (strcmp(eet_mapping[i].name, type) == 0)
       {
          *t = eet_mapping[i].t;
          return EINA_TRUE;
       }

   return EINA_FALSE;
}

static void
_data_keys_hash_free_cb(void *data)
{
   Eina_Hash *values = data;
   eina_hash_free(values);
}

static void
_data_values_hash_free_cb(void *data)
{
   Elm_Prefs_Data_Item *it = data;

   eina_value_flush(&(it->value));
   free(it);
}

static void
_eet_data_load(Elm_Prefs_Data *prefs_data,
               Eet_File *eet_file,
               const char *key)
{
   Eina_Hash      *map;
   Eet_Data_Item  *it;
   Eet_Data_Value *values = eet_data_read(eet_file, _values_edd, key);

   map = eina_hash_string_superfast_new(_data_values_hash_free_cb);
   eina_hash_set(prefs_data->keys, key, map);

   if (!values)
     {
        INF("problem while reading from file %s,"
            " starting with no data", prefs_data->data_file);
        return;
     }
   else
     prefs_data->version = values->version;

   EINA_LIST_FREE(values->values, it)
     {
        Elm_Prefs_Data_Item *item = malloc(sizeof(*item));
        if (!item) continue;

        Eina_Bool skip        = EINA_FALSE;
        Eina_Bool set_err     = EINA_FALSE;
        Eina_Bool setup_err   = EINA_FALSE;

        item->type = it->type;

        switch (it->type)
          {
           case ELM_PREFS_TYPE_BOOL:
             if (!eina_value_setup(&(item->value), EINA_VALUE_TYPE_UCHAR))
               setup_err = EINA_TRUE;
             else if (!eina_value_set(&(item->value), it->value.b.b))
               set_err = EINA_TRUE;
             break;

           case ELM_PREFS_TYPE_INT:
             if (!eina_value_setup(&(item->value), EINA_VALUE_TYPE_INT))
               setup_err = EINA_TRUE;
             else if (!eina_value_set(&(item->value), it->value.i.i))
               set_err = EINA_TRUE;
             break;

           case ELM_PREFS_TYPE_FLOAT:
             if (!eina_value_setup(&(item->value), EINA_VALUE_TYPE_FLOAT))
               setup_err = EINA_TRUE;
             else if (!eina_value_set(&(item->value), it->value.f.f))
               set_err = EINA_TRUE;
             break;

           case ELM_PREFS_TYPE_DATE:
           {
              struct timeval val;
              struct tm t;

              memset(&val, 0, sizeof val);
              memset(&t, 0, sizeof t);

              t.tm_year = it->value.d.y - 1900;
              t.tm_mon = it->value.d.m - 1;
              t.tm_mday = it->value.d.d;
              val.tv_sec = mktime(&t);

              if (!eina_value_setup(&(item->value), EINA_VALUE_TYPE_TIMEVAL))
                setup_err = EINA_TRUE;
              else if (!eina_value_set(&(item->value), val))
                set_err = EINA_TRUE;
           }
           break;

           case ELM_PREFS_TYPE_PAGE:
             _eet_data_load(prefs_data, eet_file, it->value.s.s);
             EINA_FALLTHROUGH;

           case ELM_PREFS_TYPE_TEXTAREA:
           case ELM_PREFS_TYPE_TEXT: /* using text type for all
                                      * text-like data */
             if (!eina_value_setup(&(item->value), EINA_VALUE_TYPE_STRINGSHARE))
               setup_err = EINA_TRUE;
             else if (!eina_value_set(&(item->value), it->value.s.s))
               set_err = EINA_TRUE;
             eina_stringshare_del(it->value.s.s);
             break;

           default:
             ERR("bad item (type = %d) fetched from data file %s, skipping it",
                 it->type, prefs_data->data_file);
             ELM_SAFE_FREE(item, free);
             skip = EINA_TRUE;
             break;
          }

        if (setup_err || set_err)
          {
             ERR("failed to set value for item %s, skipping it", it->name);
             skip = EINA_TRUE;

             if (set_err) eina_value_flush(&(item->value));
             ELM_SAFE_FREE(item, free);
          }

        if (!skip) eina_hash_set(map, it->name, item);

        eina_stringshare_del(it->name);
        free(it);
     }

   free(values);
}

EAPI Elm_Prefs_Data *
elm_prefs_data_new(const char *data_file,
                   const char *key,
                   Eet_File_Mode mode)
{
   Eet_File       *eet_file;
   Elm_Prefs_Data *prefs_data;

   EINA_SAFETY_ON_TRUE_RETURN_VAL(mode <= EET_FILE_MODE_INVALID, NULL);
   EINA_SAFETY_ON_TRUE_RETURN_VAL(mode > EET_FILE_MODE_READ_WRITE, NULL);

   prefs_data = calloc(1, sizeof(*prefs_data));

   EINA_MAGIC_SET(prefs_data, ELM_PREFS_DATA_MAGIC);

   prefs_data->mode = mode;

   prefs_data->refcount = 1;

   if (mode == EET_FILE_MODE_READ)
     prefs_data->autosave = EINA_FALSE;

   if (data_file) prefs_data->data_file = eina_stringshare_add(data_file);
   else
     prefs_data->data_file = eina_stringshare_printf
     ("%s/%s", efreet_config_home_get(), _elm_appname);

   prefs_data->key = eina_stringshare_add(key ? key : "main");

   prefs_data->keys = eina_hash_string_superfast_new(_data_keys_hash_free_cb);

   /* we can only start from scratch (ignore input) and (over)write on
    * save in this case, so skip input reading */
   if (mode == EET_FILE_MODE_WRITE)
     {
        INF("write-only data mode on %s starting prefs with no data",
            prefs_data->data_file);
        return prefs_data;
     }

   eet_file = eet_open(prefs_data->data_file, prefs_data->mode);
   if (!eet_file)
     {
        char bkp[PATH_MAX];

        snprintf(bkp, sizeof(bkp), "%s.bkp", prefs_data->data_file);

        WRN("failed to load from requested file, trying backup one: %s\n",
            bkp);

        eet_file = eet_open(bkp, EET_FILE_MODE_READ);
     }

   if (eet_file)
     {
        _eet_data_load(prefs_data, eet_file, prefs_data->key);
        eet_close(eet_file);
     }

   return prefs_data;
}

EAPI unsigned int
elm_prefs_data_version_get(const Elm_Prefs_Data *prefs_data)
{
   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, -1);

   return prefs_data->version;
}

static void
_eet_data_save(const Elm_Prefs_Data *prefs_data,
               Eet_File *eet_file,
               const char *key)
{
   Eet_Data_Value edv;

   Eet_Data_Item *it;
   Elm_Prefs_Data_Item *item;

   Eina_Hash *values;
   Eina_Iterator *itr;
   Eina_Hash_Tuple *tuple;

   /* now we have to translate our prefs_data->values hash of
    * Elm_Prefs_Data_Item values into an Eet_Data_Value node, so that
    * we can serialize the latter */

   edv.version = prefs_data->version;
   edv.values = NULL;

   values = eina_hash_find(prefs_data->keys, key);

   itr = eina_hash_iterator_tuple_new(values);
   EINA_ITERATOR_FOREACH(itr, tuple)
     {
        Eina_Bool err = EINA_FALSE;

        item = (Elm_Prefs_Data_Item*) tuple->data;

        it = malloc(sizeof(*it));
        it->name = tuple->key;
        it->type = item->type;

        if (it->type == ELM_PREFS_TYPE_PAGE)
          {
             const char *n;
             if (eina_value_get(&item->value, &n))
               _eet_data_save(prefs_data, eet_file, n);
             else
               err = EINA_TRUE;
          }

        const Eina_Value_Type *t = eina_value_type_get(&(item->value));

        if (t == EINA_VALUE_TYPE_UCHAR)
          {
             if (!eina_value_get(&(item->value), &(it->value.b.b)))
               err = EINA_TRUE;
          }
        else if (t == EINA_VALUE_TYPE_INT)
          {
             if (!eina_value_get(&(item->value), &(it->value.i.i)))
               err = EINA_TRUE;
          }
        else if (t == EINA_VALUE_TYPE_FLOAT)
          {
             if (!eina_value_get(&(item->value), &(it->value.f.f)))
               err = EINA_TRUE;
          }
        else if (t == EINA_VALUE_TYPE_STRINGSHARE)
          {
             if (!eina_value_get(&(item->value), &(it->value.s.s)))
               err = EINA_TRUE;
          }
        else if (t == EINA_VALUE_TYPE_TIMEVAL)
          {
             struct timeval val;
             struct tm *tm;

             if (eina_value_get(&(item->value), &val))
               {
                  time_t gmt = val.tv_sec;

                  tm = gmtime(&gmt);

                  it->value.d.y = tm->tm_year + 1900;
                  it->value.d.m = tm->tm_mon + 1;
                  it->value.d.d = tm->tm_mday;
               }
             else
               err = EINA_TRUE;
          }
        else
          {
             ERR("bad value found on elm prefs data, skipping it");
             free(it);
             continue;
          }

        if (err)
          {
             ERR("failed to get value from %s, skipping it", it->name);
             free(it);
             continue;
          }

        edv.values = eina_list_append(edv.values, it);
     }
   eina_iterator_free(itr);

   if (!(eet_data_write(eet_file, _values_edd, key, &edv,
                        EET_COMPRESSION_DEFAULT)))
     ERR("failed to write elm prefs data!");

   EINA_LIST_FREE(edv.values, it)
      free(it);
}

static void
_elm_prefs_data_save_do(const Elm_Prefs_Data *prefs_data,
                        const char *file,
                        const char *key)
{
   char bkp[PATH_MAX];
   Eet_File *eet_file;

   snprintf(bkp, sizeof(bkp), "%s.bkp", file);

   ecore_file_unlink(bkp);
   ecore_file_mv(file, bkp);
   eet_file = eet_open(file, EET_FILE_MODE_WRITE);
   if (eet_file)
     {
        _eet_data_save(prefs_data, eet_file, key);
        eet_close(eet_file);
     }
   else
     {
        ERR("failed to open elm prefs data file to write!");
        ecore_file_mv(bkp, file);
     }
}

static void
_event_cbs_clear(Elm_Prefs_Data *prefs_data)
{
   Elm_Prefs_Data_Event *evt;

   EINA_LIST_FREE(prefs_data->deleted, evt)
     {
        prefs_data->event_cbs =
          eina_inlist_remove(prefs_data->event_cbs, EINA_INLIST_GET(evt));
        free(evt);
     }
}

static void
_elm_prefs_data_event_callback_call(Elm_Prefs_Data *prefs_data,
                                    Elm_Prefs_Data_Event_Type type,
                                    void *event_info)
{
   Elm_Prefs_Data_Event *evt;

   prefs_data->walking++;
   EINA_INLIST_FOREACH(prefs_data->event_cbs, evt)
     {
        if ((evt->type == type) && (!evt->deleted))
          evt->cb((void *)evt->data, evt->type, prefs_data, event_info);
     }
   prefs_data->walking--;

   if (!prefs_data->walking)
     _event_cbs_clear(prefs_data);
}

static Eina_Bool
_elm_prefs_data_save(void *d)
{
   Elm_Prefs_Data *prefs_data = d;

   if (!prefs_data->dirty) goto end;

   if (prefs_data->saving_poller) /* only then we are auto-saving */
     _elm_prefs_data_event_callback_call
       (prefs_data, ELM_PREFS_DATA_EVENT_GROUP_AUTOSAVED,
       (char *)prefs_data->key);

   _elm_prefs_data_save_do(prefs_data, prefs_data->data_file, prefs_data->key);

   prefs_data->dirty = EINA_FALSE;

end:
   prefs_data->saving_poller = NULL;
   return ECORE_CALLBACK_CANCEL;
}

static void
_elm_prefs_data_mark_as_dirty(Elm_Prefs_Data *prefs_data)
{
   prefs_data->dirty = EINA_TRUE;

   if ((prefs_data->autosave) && (prefs_data->mode != EET_FILE_MODE_READ))
     {
        if (prefs_data->saving_poller) return;

        prefs_data->saving_poller = ecore_poller_add
            (ECORE_POLLER_CORE, 1, _elm_prefs_data_save, prefs_data);
     }
}

EAPI Eina_Bool
elm_prefs_data_version_set(Elm_Prefs_Data *prefs_data,
                           unsigned int version)
{
   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, EINA_FALSE);

   prefs_data->version = version;

   _elm_prefs_data_mark_as_dirty(prefs_data);

   return EINA_TRUE;
}

EAPI Elm_Prefs_Data *
elm_prefs_data_ref(Elm_Prefs_Data *prefs_data)
{
   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, NULL);

   prefs_data->refcount++;

   return prefs_data;
}

static void
_elm_prefs_data_del(Elm_Prefs_Data *prefs_data)
{
   if (prefs_data->saving_poller) ecore_poller_del(prefs_data->saving_poller);

   if (prefs_data->mode != EET_FILE_MODE_READ)
     _elm_prefs_data_save(prefs_data);

   while (prefs_data->event_cbs)
     {
        Elm_Prefs_Data_Event *ecb = EINA_INLIST_CONTAINER_GET
            (prefs_data->event_cbs, Elm_Prefs_Data_Event);

        prefs_data->event_cbs = eina_inlist_remove
            (prefs_data->event_cbs, prefs_data->event_cbs);
        free(ecb);
     }

   eina_hash_free(prefs_data->keys);

   eina_stringshare_del(prefs_data->data_file);
   eina_stringshare_del(prefs_data->key);

   free(prefs_data);
}

EAPI void
elm_prefs_data_unref(Elm_Prefs_Data *prefs_data)
{
   ELM_PREFS_DATA_CHECK(prefs_data);

   prefs_data->refcount--;

   if (prefs_data->refcount == 0)
     _elm_prefs_data_del(prefs_data);
}

EAPI Eina_Bool
elm_prefs_data_value_set(Elm_Prefs_Data *prefs_data,
                         const char *path,
                         const Elm_Prefs_Item_Type type,
                         const Eina_Value *value)
{
   char key[PATH_MAX];
   char *name;
   size_t n;
   Eina_Value tmp;
   Eina_Hash *values;
   Elm_Prefs_Data_Item *item;
   Elm_Prefs_Data_Event_Changed evt_info;

   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(path, EINA_FALSE);

   /* if there is a value, try to set it to a temporary value,
    * otherwise fail immediately */
   if (value)
     {
        if (!eina_value_copy(value, &tmp))
          {
             ERR("failed to set item '%s' to value %p", path, value);
             return EINA_FALSE;
          }
     }

   name = strrchr(path, ':');
   if (!name)
     {
        ERR("invalid item path: %s", path);
        return EINA_FALSE;
     }
   else
     {
        n = name - path;
        strncpy(key, path, n);
        key[n] = '\0';
        name++;
     }

   values = eina_hash_find(prefs_data->keys, key);
   if (!values)
     {
        /* new key, must add one */
        values = eina_hash_string_superfast_new(_data_values_hash_free_cb);
        eina_hash_set(prefs_data->keys, key, values);
     }

   item = eina_hash_find(values, name);
   if (item)
     {
        eina_value_flush(&(item->value));
        if (!value) /* had an item, but should delete */
          {
             eina_hash_del_by_key(values, name);
             free(item);
             /* THERE IS NO RETURN IN HERE, we need to emit changed signal */
          }
     }
   else
     {
        if (!value) return EINA_TRUE;  /* nothing to do, success.
                                          no signal to emit */

        /* had no item, but must add one */
        item = calloc(1, sizeof(*item));
        item->type = type;
        eina_hash_set(values, name, item);
     }

   /* there's an item, but its value is already flushed or
    * uninitialized, set it from tmp memory -- this is valid and
    * correct and it works */
   if (value) memcpy(&(item->value), &tmp, sizeof(tmp));

   /* emit signal to say it was changed (value == NULL is deleted) */
   evt_info.key = path;
   evt_info.value = value;
   _elm_prefs_data_event_callback_call
     (prefs_data, ELM_PREFS_DATA_EVENT_ITEM_CHANGED, &evt_info);

   _elm_prefs_data_mark_as_dirty(prefs_data);

   return EINA_TRUE;
}

EAPI Eina_Bool
elm_prefs_data_value_get(const Elm_Prefs_Data *prefs_data,
                         const char *path,
                         Elm_Prefs_Item_Type *type,
                         Eina_Value *value)
{
   char key[PATH_MAX];
   char *name;
   size_t n;
   Eina_Hash *values;
   Elm_Prefs_Data_Item *item;

   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(path, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE);

   name = strrchr(path, ':');
   if (!name)
     {
        ERR("invalid item path: %s", path);
        return EINA_FALSE;
     }
   else
     {
        n = name - path;
        strncpy(key, path, n);
        key[n] = '\0';
        name++;
     }

   values = eina_hash_find(prefs_data->keys, key);
   if (!values) return EINA_FALSE;

   item = eina_hash_find(values, name);
   if (!item) return EINA_FALSE;

   if (!eina_value_copy(&(item->value), value))
     {
        ERR("failed to fetch the value of '%s' key", name);
        return EINA_FALSE;
     }

   if (type) *type = item->type;

   return EINA_TRUE;
}

EAPI Eina_Bool
elm_prefs_data_event_callback_add(Elm_Prefs_Data *prefs_data,
                                  Elm_Prefs_Data_Event_Type type,
                                  Elm_Prefs_Data_Event_Cb cb,
                                  const void *cb_data)
{
   Elm_Prefs_Data_Event *evt;

   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE);

   EINA_SAFETY_ON_TRUE_RETURN_VAL
     (type >= ELM_PREFS_DATA_EVENT_LAST, EINA_FALSE);

   evt = malloc(sizeof(*evt));

   evt->cb = cb;
   evt->data = cb_data;
   evt->type = type;
   evt->deleted = EINA_FALSE;

   prefs_data->event_cbs = eina_inlist_append
       (prefs_data->event_cbs, EINA_INLIST_GET(evt));

   return EINA_TRUE;
}

EAPI Eina_Bool
elm_prefs_data_event_callback_del(Elm_Prefs_Data *prefs_data,
                                  Elm_Prefs_Data_Event_Type type,
                                  Elm_Prefs_Data_Event_Cb cb,
                                  const void *cb_data)
{
   Elm_Prefs_Data_Event *evt, *found = NULL;

   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE);

   EINA_INLIST_FOREACH(prefs_data->event_cbs, evt)
     {
        if (!evt->deleted &&
            evt->cb == cb && evt->data == cb_data && evt->type == type)
          {
             found = evt;
             break;
          }
     }

   if (!found)
     return EINA_FALSE;

   if (prefs_data->walking > 0)
     {
        found->deleted = EINA_TRUE;
        prefs_data->deleted = eina_list_append(prefs_data->deleted, found);
        return EINA_TRUE;
     }

   prefs_data->event_cbs =
     eina_inlist_remove(prefs_data->event_cbs, EINA_INLIST_GET(found));
   free(found);

   return EINA_TRUE;
}

EAPI void
elm_prefs_data_autosave_set(Elm_Prefs_Data *prefs_data,
                            Eina_Bool autosave)
{
   ELM_PREFS_DATA_CHECK(prefs_data);

   if (prefs_data->mode == EET_FILE_MODE_READ) return;

   prefs_data->autosave = !!autosave;

   if (prefs_data->autosave == autosave) return;
   if ((prefs_data->autosave) && (prefs_data->dirty))
     {
        if (prefs_data->saving_poller) return;

        prefs_data->saving_poller = ecore_poller_add
            (ECORE_POLLER_CORE, 1, _elm_prefs_data_save, prefs_data);
     }
   else if ((!prefs_data->autosave) && (prefs_data->saving_poller))
     {
        ecore_poller_del(prefs_data->saving_poller);
        prefs_data->saving_poller = NULL;

        _elm_prefs_data_save(prefs_data);
     }
}

EAPI Eina_Bool
elm_prefs_data_autosave_get(const Elm_Prefs_Data *prefs_data)
{
   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, EINA_FALSE);
   return prefs_data->autosave;
}

EAPI Eina_Bool
elm_prefs_data_save(const Elm_Prefs_Data *prefs_data,
                    const char *file,
                    const char *key)
{
   ELM_PREFS_DATA_CHECK_OR_RETURN_VAL(prefs_data, EINA_FALSE);

   if (!key) key = prefs_data->key;

   if (file)
     {
        _elm_prefs_data_save_do(prefs_data, file, key);
     }
   else
     {
        if (prefs_data->mode == EET_FILE_MODE_READ)
          {
             ERR("read only file %s, we can't save", prefs_data->data_file);
             return EINA_FALSE;
          }
        if (prefs_data->saving_poller)
          {
             ecore_poller_del(prefs_data->saving_poller);
             ((Elm_Prefs_Data *)prefs_data)->saving_poller = NULL;
          }
        _elm_prefs_data_save_do
           (prefs_data, prefs_data->data_file, prefs_data->key);

        /* we only clean the dirty flag if we save to our original
         * file */
        ((Elm_Prefs_Data *)prefs_data)->dirty = EINA_FALSE;
     }

   return EINA_TRUE;
}

#define DESC_NEW(_type, _desc)                             \
  EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, _type); \
  _desc = eet_data_descriptor_stream_new(&eddc)

static Eet_Data_Descriptor *
_bool_desc_new(void)
{
   Eet_Data_Descriptor_Class eddc;
   Eet_Data_Descriptor *ret;

   DESC_NEW(Eet_Boolean_Item, ret);
   EET_DATA_DESCRIPTOR_ADD_BASIC(ret, Eet_Boolean_Item, "b", b, EET_T_UCHAR);

   return ret;
}

static Eet_Data_Descriptor *
_int_desc_new(void)
{
   Eet_Data_Descriptor_Class eddc;
   Eet_Data_Descriptor *ret;

   DESC_NEW(Eet_Integer_Item, ret);
   EET_DATA_DESCRIPTOR_ADD_BASIC(ret, Eet_Integer_Item, "i", i, EET_T_INT);

   return ret;
}

static Eet_Data_Descriptor *
_float_desc_new(void)
{
   Eet_Data_Descriptor_Class eddc;
   Eet_Data_Descriptor *ret;

   DESC_NEW(Eet_Float_Item, ret);
   EET_DATA_DESCRIPTOR_ADD_BASIC(ret, Eet_Float_Item, "f", f, EET_T_FLOAT);

   return ret;
}

static Eet_Data_Descriptor *
_str_desc_new(void)
{
   Eet_Data_Descriptor_Class eddc;
   Eet_Data_Descriptor *ret;

   DESC_NEW(Eet_String_Item, ret);
   EET_DATA_DESCRIPTOR_ADD_BASIC(ret, Eet_String_Item, "s", s, EET_T_STRING);

   return ret;
}

static Eet_Data_Descriptor *
_date_desc_new(void)
{
   Eet_Data_Descriptor_Class eddc;
   Eet_Data_Descriptor *ret;

   DESC_NEW(Eet_Date_Item, ret);
   EET_DATA_DESCRIPTOR_ADD_BASIC(ret, Eet_Date_Item, "y", y, EET_T_UINT);
   EET_DATA_DESCRIPTOR_ADD_BASIC(ret, Eet_Date_Item, "m", m, EET_T_UINT);
   EET_DATA_DESCRIPTOR_ADD_BASIC(ret, Eet_Date_Item, "d", d, EET_T_UINT);

   return ret;
}

static void
_elm_prefs_data_descriptors_init(void)
{
   Eet_Data_Descriptor_Class eddc;

   DESC_NEW(Eet_Data_Value, _values_edd);
   EET_DATA_DESCRIPTOR_ADD_BASIC
     (_values_edd, Eet_Data_Value, "version", version, EET_T_UINT);

   _bool_value_edd = _bool_desc_new();
   _int_value_edd = _int_desc_new();
   _float_value_edd = _float_desc_new();
   _str_value_edd = _str_desc_new();
   _date_value_edd = _date_desc_new();

   DESC_NEW(Eet_Data_Item, _item_edd);

   EET_DATA_DESCRIPTOR_ADD_BASIC
     (_item_edd, Eet_Data_Item, "name", name, EET_T_STRING);

   eddc.version = EET_DATA_DESCRIPTOR_CLASS_VERSION;
   eddc.func.type_get = _union_type_get;
   eddc.func.type_set = _union_type_set;
   _item_unified_edd = eet_data_descriptor_stream_new(&eddc);

   EET_DATA_DESCRIPTOR_ADD_MAPPING
     (_item_unified_edd, "boolean", _bool_value_edd);
   EET_DATA_DESCRIPTOR_ADD_MAPPING
     (_item_unified_edd, "integer", _int_value_edd);
   EET_DATA_DESCRIPTOR_ADD_MAPPING
     (_item_unified_edd, "float", _float_value_edd);
   EET_DATA_DESCRIPTOR_ADD_MAPPING
     (_item_unified_edd, "text", _str_value_edd);
   EET_DATA_DESCRIPTOR_ADD_MAPPING
     (_item_unified_edd, "date", _date_value_edd);
   EET_DATA_DESCRIPTOR_ADD_MAPPING
     (_item_unified_edd, "page", _str_value_edd);

   EET_DATA_DESCRIPTOR_ADD_UNION
     (_item_edd, Eet_Data_Item, "value", value, type, _item_unified_edd);

   EET_DATA_DESCRIPTOR_ADD_LIST
     (_values_edd, Eet_Data_Value, "values", values, _item_edd);
}

#undef DESC_NEW

static void
_elm_prefs_data_descriptors_shutdown(void)
{
   eet_data_descriptor_free(_int_value_edd);
   eet_data_descriptor_free(_float_value_edd);
   eet_data_descriptor_free(_str_value_edd);
   eet_data_descriptor_free(_date_value_edd);
   eet_data_descriptor_free(_values_edd);
   eet_data_descriptor_free(_item_edd);
   eet_data_descriptor_free(_item_unified_edd);
}

void
_elm_prefs_data_init(void)
{
   _elm_prefs_data_init_count++;
   if (_elm_prefs_data_init_count > 1) return;

   eina_magic_string_set(ELM_PREFS_DATA_MAGIC, "Elm_Prefs_Data");

   _elm_prefs_data_descriptors_init();
}

void
_elm_prefs_data_shutdown(void)
{
   _elm_prefs_data_init_count--;
   if (_elm_prefs_data_init_count > 0) return;

   _elm_prefs_data_descriptors_shutdown();
}
