#include "eldbus_private.h"
#include "eldbus_private_types.h"
#include <dbus/dbus.h>
#include <stdint.h>

/* TODO: mempool of Eldbus_Message and Eldbus_Message_Iter */

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

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

#define ELDBUS_MESSAGE_ITERATOR_CHECK(iter)                        \
  do                                                             \
    {                                                            \
       EINA_SAFETY_ON_NULL_RETURN(iter);                          \
       if (!EINA_MAGIC_CHECK(iter, ELDBUS_MESSAGE_ITERATOR_MAGIC)) \
         {                                                       \
            EINA_MAGIC_FAIL(iter, ELDBUS_MESSAGE_ITERATOR_MAGIC);  \
            return;                                              \
         }                                                       \
    }                                                            \
  while (0)

#define ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, retval)          \
  do                                                              \
    {                                                             \
       EINA_SAFETY_ON_NULL_RETURN_VAL(iter, retval);               \
       if (!EINA_MAGIC_CHECK(iter, ELDBUS_MESSAGE_ITERATOR_MAGIC))  \
         {                                                        \
            EINA_MAGIC_FAIL(iter, ELDBUS_MESSAGE_ITERATOR_MAGIC);   \
            return retval;                                        \
         }                                                        \
    }                                                             \
  while (0)

static Eina_Bool append_basic(char type, va_list *vl, DBusMessageIter *iter);

Eina_Bool
eldbus_message_init(void)
{
   return EINA_TRUE;
}

void
eldbus_message_shutdown(void)
{
}

static Eldbus_Message_Iter *
_message_iterator_new(Eina_Bool writable)
{
   Eldbus_Message_Iter *iter;

   iter = calloc(1, sizeof(Eldbus_Message_Iter));
   EINA_SAFETY_ON_NULL_RETURN_VAL(iter, NULL);
   EINA_MAGIC_SET(iter, ELDBUS_MESSAGE_ITERATOR_MAGIC);
   iter->writable = writable;

   return iter;
}

Eldbus_Message *eldbus_message_new(Eina_Bool writable)
{
   Eldbus_Message *msg = calloc(1, sizeof(Eldbus_Message));
   EINA_SAFETY_ON_NULL_RETURN_VAL(msg, NULL);
   EINA_MAGIC_SET(msg, ELDBUS_MESSAGE_MAGIC);
   msg->refcount = 1;

   msg->iterator = _message_iterator_new(writable);
   EINA_SAFETY_ON_NULL_GOTO(msg->iterator, fail);

   return msg;

fail:
   eldbus_message_unref(msg);
   return NULL;
}

EAPI Eldbus_Message *
eldbus_message_method_call_new(const char *dest, const char *path, const char *iface, const char *method)
{
   Eldbus_Message *msg;

   EINA_SAFETY_ON_NULL_RETURN_VAL(dest, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(iface, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(method, NULL);

#ifdef DBUS_SYNTAX_H
   // added to libdbus:
   // f426c6cddd158d6324923f28117bc8e512d6f64f Fri Feb 24 12:43:55 2012 +0000
   if (!dbus_validate_bus_name(dest, NULL))
     {
        ERR("Invalid bus name '%s'", dest);
        return NULL;
     }
   if (!dbus_validate_path(path, NULL))
     {
        ERR("Invalid path '%s'", path);
        return NULL;
     }
   if (!dbus_validate_interface(iface, NULL))
     {
        ERR("Invalid interface '%s'", iface);
        return NULL;
     }
#endif

   msg = eldbus_message_new(EINA_TRUE);
   EINA_SAFETY_ON_NULL_GOTO(msg, fail);

   msg->dbus_msg = dbus_message_new_method_call(dest, path, iface, method);
   dbus_message_iter_init_append(msg->dbus_msg, &msg->iterator->dbus_iterator);

   return msg;

fail:
   eldbus_message_unref(msg);
   return NULL;
}

EAPI Eldbus_Message *
eldbus_message_ref(Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   DBG("msg=%p, pre-refcount=%d", msg, msg->refcount);
   msg->refcount++;
   return msg;
}

static void
_message_iterator_free(Eldbus_Message_Iter *iter)
{
   Eina_Inlist *lst, *next;
   Eldbus_Message_Iter *sub;
   if (!iter) return;

   lst = iter->iterators;
   while (lst)
     {
        next = lst->next;
        sub = EINA_INLIST_CONTAINER_GET(lst, Eldbus_Message_Iter);
        _message_iterator_free(sub);
        lst = next;
     }
   free(iter);
}

EAPI void
eldbus_message_unref(Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK(msg);
   DBG("msg=%p, pre-refcount=%d", msg, msg->refcount);
   msg->refcount--;
   if (msg->refcount > 0) return;

   DBG("message free %p", msg);

   EINA_MAGIC_SET(msg, EINA_MAGIC_NONE);
   if (msg->dbus_msg)
     dbus_message_unref(msg->dbus_msg);
   msg->dbus_msg = NULL;

   _message_iterator_free(msg->iterator);
   free(msg);
}

EAPI const char *
eldbus_message_path_get(const Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   return dbus_message_get_path(msg->dbus_msg);
}

EAPI const char *
eldbus_message_interface_get(const Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   return dbus_message_get_interface(msg->dbus_msg);
}

EAPI const char *
eldbus_message_member_get(const Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   return dbus_message_get_member(msg->dbus_msg);
}

EAPI const char *
eldbus_message_destination_get(const Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   return dbus_message_get_destination(msg->dbus_msg);
}

EAPI const char *
eldbus_message_sender_get(const Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   return dbus_message_get_sender(msg->dbus_msg);
}

EAPI const char *
eldbus_message_signature_get(const Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   return dbus_message_get_signature(msg->dbus_msg);
}

EAPI Eina_Bool
eldbus_message_error_get(const Eldbus_Message *msg, const char **name, const char **text)
{
   if (name) *name = NULL;
   if (text) *text = NULL;

   ELDBUS_MESSAGE_CHECK_RETVAL(msg, EINA_FALSE);

   if (dbus_message_get_type(msg->dbus_msg) != DBUS_MESSAGE_TYPE_ERROR)
     return EINA_FALSE;

   if (name)
     *name = dbus_message_get_error_name(msg->dbus_msg);

   if (text)
     dbus_message_get_args(msg->dbus_msg, NULL, DBUS_TYPE_STRING, text,
                           DBUS_TYPE_INVALID);

   return EINA_TRUE;
}

static Eina_Bool
_eldbus_message_arguments_vget(Eldbus_Message *msg, const char *signature, va_list ap)
{
   Eldbus_Message_Iter *iter;
   iter = eldbus_message_iter_get(msg);
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);

   return eldbus_message_iter_arguments_vget(iter, signature, ap);
}

EAPI Eldbus_Message_Iter *
eldbus_message_iter_get(const Eldbus_Message *msg)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   /* Something went wrong, msg->iterator should not be NULL */
   EINA_SAFETY_ON_NULL_RETURN_VAL(msg->iterator, NULL);

   return msg->iterator;
}

EAPI Eina_Bool
eldbus_message_arguments_get(const Eldbus_Message *msg, const char *signature, ...)
{
   Eina_Bool ret;
   va_list ap;

   ELDBUS_MESSAGE_CHECK_RETVAL(msg, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(signature, EINA_FALSE);

   va_start(ap, signature);
   ret = _eldbus_message_arguments_vget((Eldbus_Message *)msg, signature, ap);
   va_end(ap);
   return ret;
}

EAPI Eina_Bool
eldbus_message_arguments_vget(const Eldbus_Message *msg, const char *signature, va_list ap)
{
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(signature, EINA_FALSE);
   return _eldbus_message_arguments_vget((Eldbus_Message *)msg, signature, ap);
}

static Eina_Bool
_eldbus_message_iter_arguments_vappend(Eldbus_Message_Iter *iter, const char *signature, va_list *aq)
{
   DBusSignatureIter signature_iter;
   Eina_Bool r = EINA_TRUE;
   char *type;

   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(iter->writable, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(signature, EINA_FALSE);

   dbus_signature_iter_init(&signature_iter, signature);
   while ((type = dbus_signature_iter_get_signature(&signature_iter)))
     {
        if (type[0] != DBUS_TYPE_VARIANT && !type[1])
          r = append_basic(type[0], aq, &iter->dbus_iterator);
        else
          {
             Eldbus_Message_Iter **user_itr;
             Eldbus_Message_Iter *sub;

             user_itr = va_arg(*aq, Eldbus_Message_Iter **);
             sub = _message_iterator_new(EINA_TRUE);
             if (!sub)
               {
                  ERR("Could not create sub iterator");
                  r = EINA_FALSE;
                  goto next;
               }

             iter->iterators = eina_inlist_append(iter->iterators,
                                                  EINA_INLIST_GET(sub));

             if (type[0] == DBUS_TYPE_ARRAY)
               r = dbus_message_iter_open_container(&iter->dbus_iterator,
                                                    type[0], type+1,
                                                    &sub->dbus_iterator);
             else if (type[1] == DBUS_TYPE_VARIANT)
               {
                  ERR("variant not supported by \
                      eldbus_message_iter_arguments_append(), \
                      try eldbus_message_iter_container_new()");
                  r = EINA_FALSE;
                  goto next;
               }
             else
               {
                  char real_type;

                  if (type[0] == DBUS_STRUCT_BEGIN_CHAR)
                    real_type = DBUS_TYPE_STRUCT;
                  else real_type = DBUS_TYPE_DICT_ENTRY;
                  r = dbus_message_iter_open_container(&iter->dbus_iterator,
                                                       real_type, NULL,
                                                       &sub->dbus_iterator);
               }
             *user_itr = sub;
          }

next:
        dbus_free(type);
        if (!r || !dbus_signature_iter_next(&signature_iter))
          break;
     }

   return r;

}

EAPI Eina_Bool
eldbus_message_iter_arguments_vappend(Eldbus_Message_Iter *iter, const char *signature, va_list ap)
{
   va_list aq;
   Eina_Bool ret;

   va_copy(aq, ap);
   ret = _eldbus_message_iter_arguments_vappend(iter, signature, &aq);
   va_end(aq);

   return ret;
}

EAPI Eina_Bool
eldbus_message_iter_arguments_append(Eldbus_Message_Iter *iter, const char *signature, ...)
{
   Eina_Bool r;
   va_list ap;
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(signature, EINA_FALSE);

   va_start(ap, signature);
   r = eldbus_message_iter_arguments_vappend(iter, signature, ap);
   va_end(ap);
   return r;
}

static Eina_Bool
append_basic(char type, va_list *vl, DBusMessageIter *iter)
{
   switch (type)
     {
      case DBUS_TYPE_BYTE:
        {
           uint32_t read_byte = va_arg(*vl, uint32_t);
           uint8_t byte = read_byte;
           return dbus_message_iter_append_basic(iter, type, &byte);
        }
      case DBUS_TYPE_INT16:
        {
           int32_t read_int16 = va_arg(*vl, int32_t);
           int16_t int16 = read_int16;
           return dbus_message_iter_append_basic(iter, type, &int16);
        }
      case DBUS_TYPE_UINT16:
        {
           uint32_t read_uint16 = va_arg(*vl, uint32_t);
           uint16_t uint16 = read_uint16;
           return dbus_message_iter_append_basic(iter, type, &uint16);
        }
      case DBUS_TYPE_BOOLEAN:
      case DBUS_TYPE_INT32:
#ifdef DBUS_TYPE_UNIX_FD
      case DBUS_TYPE_UNIX_FD:
#endif
        {
           int32_t int32 = va_arg(*vl, int32_t);
           return dbus_message_iter_append_basic(iter, type, &int32);
        }
      case DBUS_TYPE_UINT32:
        {
           uint32_t uint32 = va_arg(*vl, uint32_t);
           return dbus_message_iter_append_basic(iter, type, &uint32);
        }
      case DBUS_TYPE_INT64:
        {
           int64_t int64 = va_arg(*vl, int64_t);
           return dbus_message_iter_append_basic(iter, type, &int64);
        }
      case DBUS_TYPE_UINT64:
        {
           uint64_t uint64 = va_arg(*vl, uint64_t);
           return dbus_message_iter_append_basic(iter, type, &uint64);
        }
      case DBUS_TYPE_DOUBLE:
        {
           double double_ieee = va_arg(*vl, double);
           return dbus_message_iter_append_basic(iter, type, &double_ieee);
        }
      case DBUS_TYPE_STRING:
      case DBUS_TYPE_OBJECT_PATH:
      case DBUS_TYPE_SIGNATURE:
        {
           char *string = va_arg(*vl, char*);
           return dbus_message_iter_append_basic(iter, type, &string);
        }
      default:
         ERR("Type not handled %c", type);
         return EINA_FALSE;
     }
}

static Eina_Bool
_eldbus_message_arguments_vappend(Eldbus_Message *msg, const char *signature, va_list *aq)
{
   DBusSignatureIter signature_iter;
   Eldbus_Message_Iter *iter;
   int type;
   Eina_Bool r = EINA_TRUE;

   if (!signature[0]) return EINA_TRUE;
   EINA_SAFETY_ON_FALSE_RETURN_VAL(dbus_signature_validate(signature, NULL),
                                   EINA_FALSE);

   iter = eldbus_message_iter_get(msg);
   EINA_SAFETY_ON_NULL_RETURN_VAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(iter->writable, EINA_FALSE);

   dbus_signature_iter_init(&signature_iter, signature);
   while ((type = dbus_signature_iter_get_current_type(&signature_iter)))
     {
        if (dbus_type_is_basic(type))
          r = append_basic(type, aq, &iter->dbus_iterator);
        else
          {
             ERR("sig = %s | eldbus_message_arguments_append() and \
                  eldbus_message_arguments_vappend() only support basic types, \
                  to complex types use eldbus_message_iter_* functions",
                  signature);
             r = EINA_FALSE;
          }

        if (!r || !dbus_signature_iter_next(&signature_iter))
          break;
     }

   return r;
}

EAPI Eina_Bool
eldbus_message_arguments_append(Eldbus_Message *msg, const char *signature, ...)
{
   Eina_Bool ret;
   va_list ap;

   ELDBUS_MESSAGE_CHECK_RETVAL(msg, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(signature, EINA_FALSE);

   va_start(ap, signature);
   ret = _eldbus_message_arguments_vappend(msg, signature, &ap);
   va_end(ap);
   return ret;
}

EAPI Eina_Bool
eldbus_message_arguments_vappend(Eldbus_Message *msg, const char *signature, va_list ap)
{
   va_list aq;
   Eina_Bool ret;

   ELDBUS_MESSAGE_CHECK_RETVAL(msg, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(signature, EINA_FALSE);

   va_copy(aq, ap);
   ret = _eldbus_message_arguments_vappend(msg, signature, &aq);
   va_end(aq);

   return ret;
}

EAPI Eldbus_Message_Iter *
eldbus_message_iter_container_new(Eldbus_Message_Iter *iter, int type, const char* contained_signature)
{
   Eldbus_Message_Iter *sub;

   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, NULL);
   sub = _message_iterator_new(EINA_TRUE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(sub, NULL);

   if (!dbus_message_iter_open_container(&iter->dbus_iterator, type,
                                         contained_signature,
                                         &sub->dbus_iterator))
     goto cleanup;

   iter->iterators = eina_inlist_append(iter->iterators, EINA_INLIST_GET(sub));
   return sub;

cleanup:
   free(sub);
   return NULL;
}

EAPI Eina_Bool
eldbus_message_iter_container_close(Eldbus_Message_Iter *iter, Eldbus_Message_Iter *sub)
{
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(sub, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(iter->writable, EINA_FALSE);
   return dbus_message_iter_close_container(&iter->dbus_iterator,
                                            &sub->dbus_iterator);
}

EAPI Eina_Bool
eldbus_message_iter_basic_append(Eldbus_Message_Iter *iter, int type, ...)
{
   Eina_Bool r;
   va_list vl;
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(iter->writable, EINA_FALSE);

   va_start(vl, type);
   r = append_basic(type, &vl, &iter->dbus_iterator);
   va_end(vl);

   return r;
}

EAPI Eina_Bool
eldbus_message_iter_fixed_array_append(Eldbus_Message_Iter *iter, int type, const void *array, unsigned int size)
{
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(iter->writable, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(dbus_type_is_basic(type), EINA_FALSE);
   return dbus_message_iter_append_fixed_array(&iter->dbus_iterator, type, &array, (int)size);
}

EAPI void
eldbus_message_iter_basic_get(Eldbus_Message_Iter *iter, void *value)
{
   ELDBUS_MESSAGE_ITERATOR_CHECK(iter);
   EINA_SAFETY_ON_TRUE_RETURN(iter->writable);
   dbus_message_iter_get_basic(&iter->dbus_iterator, value);
}

Eldbus_Message_Iter *
eldbus_message_iter_sub_iter_get(Eldbus_Message_Iter *iter)
{
   Eldbus_Message_Iter *sub;
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, NULL);
   EINA_SAFETY_ON_TRUE_RETURN_VAL(iter->writable, NULL);

   sub = _message_iterator_new(EINA_FALSE);
   dbus_message_iter_recurse(&iter->dbus_iterator, &sub->dbus_iterator);
   iter->iterators = eina_inlist_append(iter->iterators,
                                        EINA_INLIST_GET(sub));
   return sub;
}

EAPI char *
eldbus_message_iter_signature_get(Eldbus_Message_Iter *iter)
{
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, NULL);
   return dbus_message_iter_get_signature(&iter->dbus_iterator);
}

EAPI Eina_Bool
eldbus_message_iter_next(Eldbus_Message_Iter *iter)
{
   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_TRUE_RETURN_VAL(iter->writable, EINA_FALSE);
   return dbus_message_iter_next(&iter->dbus_iterator);
}

static void
get_basic(char type, DBusMessageIter *iter, va_list *vl)
{
   DBusBasicValue val;

   switch (type)
     {
      case DBUS_TYPE_BYTE:
        {
           uint8_t *byte = va_arg(*vl, uint8_t *);
           dbus_message_iter_get_basic(iter, &val);
           *byte = val.bool_val;
           break;
        }
      case DBUS_TYPE_BOOLEAN:
        {
           Eina_Bool *boolean = va_arg(*vl, Eina_Bool *);
           dbus_message_iter_get_basic(iter, &val);
           *boolean = val.bool_val;
           break;
        }
      case DBUS_TYPE_INT16:
        {
           int16_t *int16 = va_arg(*vl, int16_t *);
           dbus_message_iter_get_basic(iter, &val);
           *int16 = val.i16;
           break;
        }
      case DBUS_TYPE_UINT16:
        {
           uint16_t *uint16 = va_arg(*vl, uint16_t *);
           dbus_message_iter_get_basic(iter, &val);
           *uint16 = val.u16;
           break;
        }
      case DBUS_TYPE_INT32:
#ifdef DBUS_TYPE_UNIX_FD
      case DBUS_TYPE_UNIX_FD:
#endif
        {
           int32_t *int32 = va_arg(*vl, int32_t *);
           dbus_message_iter_get_basic(iter, &val);
           *int32 = val.i32;
           break;
        }
      case DBUS_TYPE_UINT32:
        {
           uint32_t *uint32 = va_arg(*vl, uint32_t *);
           dbus_message_iter_get_basic(iter, &val);
           *uint32 = val.u32;
           break;
        }
      case DBUS_TYPE_INT64:
        {
           int64_t *int64 = va_arg(*vl, int64_t *);
           dbus_message_iter_get_basic(iter, &val);
           *int64 = val.i64;
           break;
        }
      case DBUS_TYPE_UINT64:
        {
           uint64_t *uint64 = va_arg(*vl, uint64_t *);
           dbus_message_iter_get_basic(iter, &val);
           *uint64 = val.u64;
           break;
        }
      case DBUS_TYPE_DOUBLE:
        {
           double *double_ieee = va_arg(*vl, double *);
           dbus_message_iter_get_basic(iter, &val);
           *double_ieee = val.dbl;
           break;
        }
      case DBUS_TYPE_STRING:
      case DBUS_TYPE_OBJECT_PATH:
      case DBUS_TYPE_SIGNATURE:
        {
           char **string = va_arg(*vl, char**);
           dbus_message_iter_get_basic(iter, &val);
           *string = val.str;
           break;
        }
      default:
        {
           ERR("Type not handled %c", type);
        }
     }
}

EAPI Eina_Bool
eldbus_message_iter_fixed_array_get(Eldbus_Message_Iter *iter, int signature, void *value, int *n_elements)
{
   int iter_type;

   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_TRUE_RETURN_VAL(iter->writable, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(n_elements, EINA_FALSE);

   iter_type = dbus_message_iter_get_arg_type(&iter->dbus_iterator);

   if (iter_type == DBUS_TYPE_INVALID)
     {
        *n_elements = 0;
        return EINA_TRUE;
     }

   EINA_SAFETY_ON_FALSE_RETURN_VAL(
        (dbus_message_iter_get_arg_type(&iter->dbus_iterator) == signature),
        EINA_FALSE);
   dbus_message_iter_get_fixed_array(&iter->dbus_iterator, value, n_elements);
   return EINA_TRUE;
}

/**
 * Useful when iterating over arrays
 */
EAPI Eina_Bool
eldbus_message_iter_get_and_next(Eldbus_Message_Iter *iter, char signature, ...)
{
   char type;
   va_list vl;

   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_TRUE_RETURN_VAL(iter->writable, EINA_FALSE);
   va_start(vl, signature);

   type = dbus_message_iter_get_arg_type(&iter->dbus_iterator);
   if (type == DBUS_TYPE_INVALID)
     {
        va_end(vl);
        return EINA_FALSE;
     }
   if (type != signature)
     {
        if (signature == '(') signature = 'r';
        else if (signature == '{') signature = 'e';
        if (type != signature)
          {
             va_end(vl);
             return EINA_FALSE;
          }
     }

   if (dbus_type_is_basic(type))
     get_basic(type, &iter->dbus_iterator, &vl);
   else
     {
        Eldbus_Message_Iter *sub;
        Eldbus_Message_Iter **iter_var = va_arg(vl, Eldbus_Message_Iter**);

        sub = _message_iterator_new(EINA_FALSE);
        if (!sub)
          {
             va_end(vl);
             return EINA_FALSE;
          }
        dbus_message_iter_recurse(&iter->dbus_iterator,
                                  &sub->dbus_iterator);
        iter->iterators = eina_inlist_append(iter->iterators,
                                             EINA_INLIST_GET(sub));

        *iter_var = sub;
     }
   va_end(vl);

   dbus_message_iter_next(&iter->dbus_iterator);
   return EINA_TRUE;
}

static Eina_Bool
_eldbus_message_iter_arguments_vget(Eldbus_Message_Iter *iter, const char *signature, va_list *aq)
{
   int iter_type;
   DBusSignatureIter sig_iter;

   ELDBUS_MESSAGE_ITERATOR_CHECK_RETVAL(iter, EINA_FALSE);
   EINA_SAFETY_ON_TRUE_RETURN_VAL(iter->writable, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(signature, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(dbus_signature_validate(signature, NULL), EINA_FALSE);

   dbus_signature_iter_init(&sig_iter, signature);
   iter_type = dbus_message_iter_get_arg_type(&iter->dbus_iterator);

   while (iter_type != DBUS_TYPE_INVALID)
     {
        int sig_type = dbus_signature_iter_get_current_type(&sig_iter);

        if (sig_type == DBUS_TYPE_INVALID)
          break;

        if (sig_type != iter_type)
          {
             ERR("Type in iterator different of signature expected:%c got %c", iter_type, sig_type);
             return EINA_FALSE;
          }

        if (dbus_type_is_basic(iter_type))
          get_basic(iter_type, &iter->dbus_iterator, aq);
        else
          {
             Eldbus_Message_Iter **user_itr = va_arg(*aq, Eldbus_Message_Iter **);
             Eldbus_Message_Iter *sub_itr;

             sub_itr = _message_iterator_new(EINA_FALSE);
             EINA_SAFETY_ON_NULL_RETURN_VAL(sub_itr, EINA_FALSE);
             dbus_message_iter_recurse(&iter->dbus_iterator,
                                       &sub_itr->dbus_iterator);
             iter->iterators = eina_inlist_append(iter->iterators,
                                                  EINA_INLIST_GET(sub_itr));
             *user_itr = sub_itr;
          }

        dbus_message_iter_next(&iter->dbus_iterator);
        if (!dbus_signature_iter_next(&sig_iter))
          break;
        iter_type = dbus_message_iter_get_arg_type(&iter->dbus_iterator);
     }

   return dbus_signature_iter_get_current_type(&sig_iter) == DBUS_TYPE_INVALID;

}

EAPI Eina_Bool
eldbus_message_iter_arguments_get(Eldbus_Message_Iter *iter, const char *signature, ...)
{
   va_list ap;
   Eina_Bool ret;

   va_start(ap, signature);
   ret = _eldbus_message_iter_arguments_vget(iter, signature, &ap);
   va_end(ap);

   return ret;
}

EAPI Eina_Bool
eldbus_message_iter_arguments_vget(Eldbus_Message_Iter *iter, const char *signature, va_list ap)
{
   va_list aq;
   Eina_Bool ret;

   va_copy(aq, ap);
   ret = _eldbus_message_iter_arguments_vget(iter, signature, &aq);
   va_end(aq);

   return ret;
}

EAPI void
eldbus_message_iter_del(Eldbus_Message_Iter *iter)
{
   ELDBUS_MESSAGE_ITERATOR_CHECK(iter);
   _message_iterator_free(iter);
}

EAPI Eldbus_Message *
eldbus_message_error_new(const Eldbus_Message *msg, const char *error_name, const char *error_msg)
{
   Eldbus_Message *reply;

   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(error_name, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(error_msg, NULL);

   reply = eldbus_message_new(EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL);

   reply->dbus_msg = dbus_message_new_error(msg->dbus_msg,
                                            error_name, error_msg);

   dbus_message_iter_init(reply->dbus_msg, &reply->iterator->dbus_iterator);

   return reply;
}

EAPI Eldbus_Message *
eldbus_message_method_return_new(const Eldbus_Message *msg)
{
   Eldbus_Message *reply;
   ELDBUS_MESSAGE_CHECK_RETVAL(msg, NULL);

   reply = eldbus_message_new(EINA_TRUE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL);

   reply->dbus_msg = dbus_message_new_method_return(msg->dbus_msg);

   dbus_message_iter_init_append(reply->dbus_msg,
                                 &reply->iterator->dbus_iterator);

   return reply;
}

EAPI Eldbus_Message *
eldbus_message_signal_new(const char *path, const char *interface, const char *name)
{
   Eldbus_Message *msg;

   EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(name, NULL);

   msg = eldbus_message_new(EINA_TRUE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(msg, NULL);

   msg->dbus_msg = dbus_message_new_signal(path, interface, name);
   dbus_message_iter_init_append(msg->dbus_msg,
                                 &msg->iterator->dbus_iterator);

   return msg;
}
