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

#include "eldbus_private.h"
#include "eldbus_private_types.h"

#define DBUS_ANNOTATION(name, value) \
        "<annotation" \
        " name=\"org.freedesktop.DBus." name "\"" \
        " value=\"" value "\"" \
        "/>"

#define DBUS_ANNOTATION_DEPRECATED  DBUS_ANNOTATION("Deprecated", "true")
#define DBUS_ANNOTATION_NOREPLY     DBUS_ANNOTATION("Method.NoReply", "true")

#ifndef DBUS_ERROR_UNKNOWN_INTERFACE
# define DBUS_ERROR_UNKNOWN_INTERFACE          "org.freedesktop.DBus.Error.UnknownInterface"
#endif

#ifndef DBUS_ERROR_UNKNOWN_PROPERTY
# define DBUS_ERROR_UNKNOWN_PROPERTY           "org.freedesktop.DBus.Error.UnknownProperty"
#endif

#ifndef DBUS_ERROR_PROPERTY_READ_ONLY
# define DBUS_ERROR_PROPERTY_READ_ONLY         "org.freedesktop.DBus.Error.PropertyReadOnly"
#endif

#define ELDBUS_SERVICE_INTERFACE_CHECK(obj)                         \
  do                                                            \
    {                                                           \
       EINA_SAFETY_ON_NULL_RETURN(obj);                         \
       if (!EINA_MAGIC_CHECK(obj, ELDBUS_SERVICE_INTERFACE_MAGIC))  \
         {                                                      \
            EINA_MAGIC_FAIL(obj, ELDBUS_SERVICE_INTERFACE_MAGIC);   \
            return;                                             \
         }                                                      \
    }                                                           \
  while (0)

#define ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(obj, retval)                  \
  do                                                                    \
    {                                                                   \
       EINA_SAFETY_ON_NULL_RETURN_VAL(obj, retval);                     \
       if (!EINA_MAGIC_CHECK(obj, ELDBUS_SERVICE_INTERFACE_MAGIC))          \
         {                                                              \
            EINA_MAGIC_FAIL(obj, ELDBUS_SERVICE_INTERFACE_MAGIC);           \
            return retval;                                              \
         }                                                              \
    }                                                                   \
  while (0)


static void _object_unregister(DBusConnection *conn, void *user_data);
static DBusHandlerResult _object_handler(DBusConnection *conn, DBusMessage *message, void *user_data);
static void _object_free(Eldbus_Service_Object *obj);
static void _interface_free(Eldbus_Service_Interface *interface);
static void _on_connection_free(void *data, const void *dead_pointer);

static DBusObjectPathVTable vtable = {
  _object_unregister,
  _object_handler,
  NULL,
  NULL,
  NULL,
  NULL
};

static Eldbus_Service_Interface *introspectable;
static Eldbus_Service_Interface *properties_iface;
static Eldbus_Service_Interface *objmanager;

static inline void
_introspect_arguments_append(Eina_Strbuf *buf, const Eldbus_Arg_Info *args,
                             const char *direction)
{
   for (; args && args->signature; args++)
     {
        if (args->name && args->name[0])
          eina_strbuf_append_printf(buf, "<arg type=\"%s\" name=\"%s\"",
                                    args->signature, args->name);
        else
          eina_strbuf_append_printf(buf, "<arg type=\"%s\"", args->signature);

        if (direction)
          eina_strbuf_append_printf(buf, " direction=\"%s\" />", direction);
        else
          eina_strbuf_append(buf, " />");
     }
}

static inline void
_introspect_append_signal(Eina_Strbuf *buf, const Eldbus_Signal *sig)
{
   eina_strbuf_append_printf(buf, "<signal name=\"%s\"", sig->name);

   if (!sig->flags && !(sig->args && sig->args->signature))
     {
        eina_strbuf_append(buf, " />");
        return;
     }

   eina_strbuf_append(buf, ">");

   if (sig->flags & ELDBUS_SIGNAL_FLAG_DEPRECATED)
     eina_strbuf_append(buf, DBUS_ANNOTATION_DEPRECATED);

   _introspect_arguments_append(buf, sig->args, NULL);

   eina_strbuf_append(buf, "</signal>");
}

static inline void
_instrospect_append_property(Eina_Strbuf *buf, const Eldbus_Property *prop, const Eldbus_Service_Interface *iface)
{
   eina_strbuf_append_printf(buf, "<property name=\"%s\" type=\"%s\" access=\"",
                             prop->name, prop->type);

   if (iface->get_func || prop->get_func)
     eina_strbuf_append(buf, "read");

   if (iface->set_func || prop->set_func)
     eina_strbuf_append(buf, "write");

   if (!prop->flags)
     {
        eina_strbuf_append(buf, "\" />");
        return;
     }

   eina_strbuf_append(buf, "\">");

   if (prop->flags & ELDBUS_PROPERTY_FLAG_DEPRECATED)
     eina_strbuf_append(buf, DBUS_ANNOTATION_DEPRECATED);

   eina_strbuf_append(buf, "</property>");
}

static inline void
_introspect_append_method(Eina_Strbuf *buf, const Eldbus_Method *method)
{
   eina_strbuf_append_printf(buf, "<method name=\"%s\">", method->member);

   if (method->flags & ELDBUS_METHOD_FLAG_DEPRECATED)
     eina_strbuf_append(buf, DBUS_ANNOTATION_DEPRECATED);

   if (method->flags & ELDBUS_METHOD_FLAG_NOREPLY)
     eina_strbuf_append(buf, DBUS_ANNOTATION_NOREPLY);

   _introspect_arguments_append(buf, method->in, "in");
   _introspect_arguments_append(buf, method->out, "out");
   eina_strbuf_append(buf, "</method>");
}

typedef struct _Property
{
   const Eldbus_Property *property;
   Eina_Bool is_invalidate:1;
} Property;

static void
_introspect_append_interface(Eina_Strbuf *buf, Eldbus_Service_Interface *iface)
{
   const Eldbus_Method *method;
   Property *prop;
   Eina_Iterator *iterator;
   unsigned short i;

   eina_strbuf_append_printf(buf, "<interface name=\"%s\">", iface->name);

   iterator = eina_hash_iterator_data_new(iface->methods);
   EINA_ITERATOR_FOREACH(iterator, method)
     _introspect_append_method(buf, method);
   eina_iterator_free(iterator);

   for (i = 0; i < eina_array_count(iface->sign_of_signals); i++)
     _introspect_append_signal(buf, &iface->signals[i]);

   iterator = eina_hash_iterator_data_new(iface->properties);
   EINA_ITERATOR_FOREACH(iterator, prop)
     _instrospect_append_property(buf, prop->property, iface);
   eina_iterator_free(iterator);

   eina_strbuf_append(buf, "</interface>");
}

static Eldbus_Message *
_cb_property_get(const Eldbus_Service_Interface *piface, const Eldbus_Message *msg)
{
   const char *propname, *iface_name;
   Eldbus_Service_Object *obj = piface->obj;
   Eldbus_Service_Interface *iface;
   Property *prop;
   Eldbus_Message *reply, *error_reply = NULL;
   Eldbus_Message_Iter *main_iter, *variant;
   Eina_Bool ret;
   Eldbus_Property_Get_Cb getter = NULL;

   if (!eldbus_message_arguments_get(msg, "ss", &iface_name, &propname))
     return NULL;

   iface = eina_hash_find(obj->interfaces, iface_name);
   if (!iface)
     return eldbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE,
                                    "Interface not found.");

   prop = eina_hash_find(iface->properties, propname);
   if (!prop || prop->is_invalidate) goto not_found;

   if (prop->property->get_func)
     getter = prop->property->get_func;
   else if (iface->get_func)
     getter = iface->get_func;

   if (!getter) goto not_found;

   reply = eldbus_message_method_return_new(msg);
   EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL);

   main_iter = eldbus_message_iter_get(reply);
   variant = eldbus_message_iter_container_new(main_iter, 'v',
                                              prop->property->type);

   ret = getter(iface, propname, variant, msg, &error_reply);

   if (ret)
     {
        eldbus_message_iter_container_close(main_iter, variant);
        return reply;
     }

   eldbus_message_unref(reply);
   return error_reply;

not_found:
   return eldbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_PROPERTY,
                                  "Property not found.");
}

static Eina_Bool
_props_getall(Eldbus_Service_Interface *iface, Eina_Iterator *iterator, Eldbus_Message_Iter *dict, const Eldbus_Message *input_msg, Eldbus_Message **error_reply)
{
   Property *prop;
   EINA_ITERATOR_FOREACH(iterator, prop)
     {
        Eldbus_Message_Iter *entry, *var;
        Eina_Bool ret;
        Eldbus_Property_Get_Cb getter = NULL;

        if (prop->property->get_func)
          getter = prop->property->get_func;
        else if (iface->get_func)
          getter = iface->get_func;

        if (!getter || prop->is_invalidate)
          continue;

        if (!eldbus_message_iter_arguments_append(dict, "{sv}", &entry))
          continue;

        eldbus_message_iter_basic_append(entry, 's', prop->property->name);
        var = eldbus_message_iter_container_new(entry, 'v',
                                               prop->property->type);

        ret = getter(iface, prop->property->name, var, input_msg, error_reply);
        if (!ret)
          return EINA_FALSE;

        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(dict, entry);
     }
   return EINA_TRUE;
}

static Eldbus_Message *
_cb_property_getall(const Eldbus_Service_Interface *piface, const Eldbus_Message *msg)
{
   const char *iface_name;
   Eldbus_Service_Object *obj = piface->obj;
   Eldbus_Service_Interface *iface;
   Eina_Iterator *iterator;
   Eldbus_Message *reply, *error_reply;
   Eldbus_Message_Iter *main_iter, *dict;

   if (!eldbus_message_arguments_get(msg, "s", &iface_name))
     return NULL;

   iface = eina_hash_find(obj->interfaces, iface_name);
   if (!iface)
     return eldbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE,
                                    "Interface not found.");

   reply = eldbus_message_method_return_new(msg);
   EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL);
   main_iter = eldbus_message_iter_get(reply);
   if (!eldbus_message_iter_arguments_append(main_iter, "a{sv}", &dict))
     {
        eldbus_message_unref(reply);
        return NULL;
     }

   iterator = eina_hash_iterator_data_new(iface->properties);
   if (!_props_getall(iface, iterator, dict, msg, &error_reply))
     {
        eldbus_message_unref(reply);
        eina_iterator_free(iterator);
        return error_reply;
     }
   eldbus_message_iter_container_close(main_iter, dict);

   eina_iterator_free(iterator);
   return reply;
}

static Eldbus_Message *
_cb_property_set(const Eldbus_Service_Interface *piface, const Eldbus_Message *msg)
{
   const char *propname, *iface_name;
   Eldbus_Service_Object *obj = piface->obj;
   Eldbus_Service_Interface *iface;
   Property *prop;
   Eldbus_Message *reply;
   Eldbus_Message_Iter *variant;
   Eldbus_Property_Set_Cb setter = NULL;

   if (!eldbus_message_arguments_get(msg, "ssv", &iface_name, &propname, &variant))
     return NULL;

   iface = eina_hash_find(obj->interfaces, iface_name);
   if (!iface)
     return eldbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE,
                                    "Interface not found.");

   prop = eina_hash_find(iface->properties, propname);
   if (!prop || prop->is_invalidate)
     return eldbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_PROPERTY,
                                    "Property not found.");

   if (prop->property->set_func)
     setter = prop->property->set_func;
   else if (iface->set_func)
     setter = iface->set_func;

   if (!setter)
     return eldbus_message_error_new(msg, DBUS_ERROR_PROPERTY_READ_ONLY,
                                    "This property is read only");

   reply = setter(iface, propname, variant, msg);
   return reply;
}

static Eldbus_Message *
cb_introspect(const Eldbus_Service_Interface *_iface, const Eldbus_Message *message)
{
   Eldbus_Service_Object *obj = _iface->obj;
   Eldbus_Message *reply = eldbus_message_method_return_new(message);
   if (obj->introspection_dirty || !obj->introspection_data)
     {
        Eina_Iterator *iterator;
        Eldbus_Service_Interface *iface;
        Eldbus_Service_Object *child;
        size_t baselen;

        if (obj->introspection_data)
          eina_strbuf_reset(obj->introspection_data);
        else
          obj->introspection_data = eina_strbuf_new();
        EINA_SAFETY_ON_NULL_GOTO(obj->introspection_data, fail);

        eina_strbuf_append(obj->introspection_data, "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">");
        eina_strbuf_append_printf(obj->introspection_data,
                                  "<node name=\"%s\">", obj->path);

        iterator = eina_hash_iterator_data_new(obj->interfaces);
        EINA_ITERATOR_FOREACH(iterator, iface)
          _introspect_append_interface(obj->introspection_data, iface);
        eina_iterator_free(iterator);

        baselen = strlen(obj->path);
        /* account for the last '/' */
        if (baselen != 1)
          baselen++;

        EINA_INLIST_FOREACH(obj->children, child)
           eina_strbuf_append_printf(obj->introspection_data,
                                     "<node name=\"%s\" />",
                                     child->path + baselen);

        eina_strbuf_append(obj->introspection_data, "</node>");
        obj->introspection_dirty = EINA_FALSE;
     }

   eldbus_message_arguments_append(reply, "s", eina_strbuf_string_get(obj->introspection_data));
   return reply;
fail:
   if (reply) eldbus_message_unref(reply);
   return NULL;
}

static const Eldbus_Method introspect = {
   "Introspect", NULL, ELDBUS_ARGS({ "s", "xml" }), cb_introspect, 0
};

static void
_introspectable_create(void)
{
   introspectable = calloc(1, sizeof(Eldbus_Service_Interface));
   EINA_SAFETY_ON_NULL_RETURN(introspectable);

   EINA_MAGIC_SET(introspectable, ELDBUS_SERVICE_INTERFACE_MAGIC);
   introspectable->sign_of_signals = eina_array_new(1);
   introspectable->properties = eina_hash_string_small_new(NULL);
   introspectable->name = ELDBUS_FDO_INTERFACE_INTROSPECTABLE;
   introspectable->methods = eina_hash_string_small_new(NULL);

   eina_hash_add(introspectable->methods, introspect.member, &introspect);
}

static void
_default_interfaces_free(void)
{
   eina_hash_free(introspectable->methods);
   eina_hash_free(introspectable->properties);
   eina_array_free(introspectable->sign_of_signals);
   free(introspectable);

   eina_hash_free(properties_iface->methods);
   eina_hash_free(properties_iface->properties);
   eina_array_free(properties_iface->sign_of_signals);
   free(properties_iface);

   eina_hash_free(objmanager->methods);
   eina_hash_free(objmanager->properties);
   eina_array_free(objmanager->sign_of_signals);
   free(objmanager);
}

static const Eldbus_Method _property_methods[] = {
   {
    "Get", ELDBUS_ARGS({"s", "interface"}, {"s", "property"}),
    ELDBUS_ARGS({"v", "value"}), _cb_property_get, 0
   },
   {
    "Set", ELDBUS_ARGS({"s", "interface"}, {"s", "property"}, {"v", "value"}),
    NULL, _cb_property_set, 0
   },
   {
    "GetAll", ELDBUS_ARGS({"s", "interface"}), ELDBUS_ARGS({"a{sv}", "props"}),
    _cb_property_getall, 0
   }
};

static const Eldbus_Signal _properties_signals[] = {
   {
    "PropertiesChanged",
    ELDBUS_ARGS({"s", "interface"}, {"a{sv}", "changed_properties"}, {"as", "invalidated_properties"}), 0
   }
};

static void
_properties_create(void)
{
   properties_iface = calloc(1, sizeof(Eldbus_Service_Interface));
   if (!properties_iface) return;

   properties_iface->sign_of_signals = eina_array_new(1);
   properties_iface->properties =  eina_hash_string_small_new(NULL);
   properties_iface->name = ELDBUS_FDO_INTERFACE_PROPERTIES;
   properties_iface->methods = eina_hash_string_small_new(NULL);
   EINA_MAGIC_SET(properties_iface, ELDBUS_SERVICE_INTERFACE_MAGIC);

   eina_hash_add(properties_iface->methods, _property_methods[0].member,
                 &_property_methods[0]);
   eina_hash_add(properties_iface->methods, _property_methods[1].member,
                 &_property_methods[1]);
   eina_hash_add(properties_iface->methods, _property_methods[2].member,
                 &_property_methods[2]);

   properties_iface->signals = _properties_signals;
   eina_array_push(properties_iface->sign_of_signals, "sa{sv}as");
}

static Eina_Bool
_propmgr_iface_props_append(Eldbus_Service_Interface *iface, Eldbus_Message_Iter *array)
{
   Eldbus_Message_Iter *iface_entry, *props_array;
   Eina_Iterator *iterator;
   Eldbus_Message *error_msg;

   eldbus_message_iter_arguments_append(array, "{sa{sv}}", &iface_entry);

   eldbus_message_iter_arguments_append(iface_entry, "sa{sv}", iface->name, &props_array);
   iterator = eina_hash_iterator_data_new(iface->properties);
   if (!_props_getall(iface, iterator, props_array, NULL, &error_msg))
     {
        ERR("Error reply was set without pass any input message.");
        eldbus_message_unref(error_msg);
        eina_iterator_free(iterator);
        return EINA_FALSE;
     }
   eina_iterator_free(iterator);
   eldbus_message_iter_container_close(iface_entry, props_array);
   eldbus_message_iter_container_close(array, iface_entry);
   return EINA_TRUE;
}

static Eina_Bool
_managed_obj_append(Eldbus_Service_Object *obj, Eldbus_Message_Iter *array, Eina_Bool first)
{
   Eldbus_Message_Iter *obj_entry, *array_interface;
   Eina_Iterator *iface_iter;
   Eldbus_Service_Interface *iface;
   Eldbus_Service_Object *children;

   if (first) goto foreach;
   if (obj->objmanager) return EINA_TRUE;

   eldbus_message_iter_arguments_append(array, "{oa{sa{sv}}}", &obj_entry);
   eldbus_message_iter_arguments_append(obj_entry, "oa{sa{sv}}", obj->path,
                                       &array_interface);
   iface_iter = eina_hash_iterator_data_new(obj->interfaces);

   EINA_ITERATOR_FOREACH(iface_iter, iface)
     {
        Eina_Bool ret;

        if (eina_list_data_find(obj->iface_added, iface))
          continue;

        ret = _propmgr_iface_props_append(iface, array_interface);
        if (ret)
          continue;

        eina_iterator_free(iface_iter);
        return EINA_FALSE;
     }
   eina_iterator_free(iface_iter);
   eldbus_message_iter_container_close(obj_entry, array_interface);
   eldbus_message_iter_container_close(array, obj_entry);

foreach:
   EINA_INLIST_FOREACH(obj->children, children)
     {
        Eina_Bool ret;
        ret = _managed_obj_append(children, array, EINA_FALSE);
        if (!ret) return EINA_FALSE;
     }
   return EINA_TRUE;
}

static Eldbus_Message *
_cb_managed_objects(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg)
{
   Eldbus_Message *reply = eldbus_message_method_return_new(msg);
   Eldbus_Message_Iter *array_path, *main_iter;
   Eina_Bool ret;

   EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL);
   main_iter =  eldbus_message_iter_get(reply);
   eldbus_message_iter_arguments_append(main_iter, "a{oa{sa{sv}}}", &array_path);

   ret = _managed_obj_append(iface->obj, array_path, EINA_TRUE);
   if (!ret)
     {
        eldbus_message_unref(reply);
        return eldbus_message_error_new(msg, "org.freedesktop.DBus.Error",
                                       "Irrecoverable error happen");
     }

   eldbus_message_iter_container_close(main_iter, array_path);
   return reply;
}

static Eldbus_Method get_managed_objects = {
   "GetManagedObjects", NULL, ELDBUS_ARGS({"a{oa{sa{sv}}}", "objects"}),
   _cb_managed_objects, 0
};

static const Eldbus_Signal _object_manager_signals[] = {
   {
    "InterfacesAdded", ELDBUS_ARGS({"o", "object"}, {"a{sa{sv}}", "interfaces"}), 0
   },
   {
    "InterfacesRemoved", ELDBUS_ARGS({"o", "object"}, {"as", "interfaces"}), 0
   }
};

static void
_object_manager_create(void)
{
   objmanager = calloc(1, sizeof(Eldbus_Service_Interface));
   if (!objmanager) return;

   EINA_MAGIC_SET(objmanager, ELDBUS_SERVICE_INTERFACE_MAGIC);
   objmanager->sign_of_signals = eina_array_new(1);
   objmanager->properties = eina_hash_string_small_new(NULL);
   objmanager->name = ELDBUS_FDO_INTERFACE_OBJECT_MANAGER;
   objmanager->methods = eina_hash_string_small_new(NULL);

   eina_hash_add(objmanager->methods, get_managed_objects.member,
                 &get_managed_objects);

   objmanager->signals = _object_manager_signals;
   eina_array_push(objmanager->sign_of_signals, "oa{sa{sv}}");
   eina_array_push(objmanager->sign_of_signals, "oas");
}

Eina_Bool
eldbus_service_init(void)
{
   _introspectable_create();
   EINA_SAFETY_ON_NULL_RETURN_VAL(introspectable, EINA_FALSE);
   _properties_create();
   EINA_SAFETY_ON_NULL_RETURN_VAL(properties_iface, EINA_FALSE);
   _object_manager_create();
   EINA_SAFETY_ON_NULL_RETURN_VAL(objmanager, EINA_FALSE);

   return EINA_TRUE;
}

void
eldbus_service_shutdown(void)
{
   _default_interfaces_free();
}

static Eldbus_Service_Object *
_eldbus_service_object_parent_find(Eldbus_Service_Object *obj)
{
   Eldbus_Service_Object *parent = NULL;
   size_t len = strlen(obj->path);
   char *path = strdup(obj->path);
   char *slash;

   for (slash = path[len] != '/' ? &path[len - 1] : &path[len - 2];
        slash > path; slash--)
     {
        if (*slash != '/')
          continue;

        *slash = '\0';

        if (dbus_connection_get_object_path_data(obj->conn->dbus_conn,
                                     path, (void **) &parent) && parent != NULL)
          break;
     }

   free(path);
   return parent;
}

static Eldbus_Service_Object *
_eldbus_service_object_add(Eldbus_Connection *conn, const char *path, Eina_Bool fallback)
{
   Eldbus_Service_Object *obj, *rootobj;
   Eina_Inlist *safe;
   size_t pathlen;

   obj = calloc(1, sizeof(Eldbus_Service_Object));
   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);

   obj->fallback = fallback;
   if ((fallback && !dbus_connection_register_fallback(conn->dbus_conn, path, &vtable, obj)) ||
      (!fallback && !dbus_connection_register_object_path(conn->dbus_conn, path, &vtable, obj)))
     {
        free(obj);
        return NULL;
     }

   obj->conn = conn;
   obj->path = eina_stringshare_add(path);
   obj->interfaces = eina_hash_string_superfast_new(NULL);
   eldbus_connection_free_cb_add(conn, _on_connection_free, obj);

   eina_hash_add(obj->interfaces, introspectable->name, introspectable);
   eina_hash_add(obj->interfaces, properties_iface->name, properties_iface);

   obj->parent = _eldbus_service_object_parent_find(obj);
   if (obj->parent)
     {
        obj->parent->children = eina_inlist_append(obj->parent->children,
                                                   EINA_INLIST_GET(obj));
        obj->parent->introspection_dirty = EINA_TRUE;
        return obj;
     }

   /*
    * If there wasn't any object above us, check if anyone in conn->root_obj
    * should become our child and append ourselves there.
    */
   pathlen = strlen(obj->path);
   EINA_INLIST_FOREACH_SAFE(conn->root_objs, safe, rootobj)
     {
        if (strncmp(obj->path,  rootobj->path, pathlen) != 0)
          continue;

        if (rootobj->path[pathlen] != '/' && pathlen > 1)
          continue;

        conn->root_objs = eina_inlist_remove(conn->root_objs,
                                             EINA_INLIST_GET(rootobj));
        obj->children = eina_inlist_append(obj->children,
                                           EINA_INLIST_GET(rootobj));
        rootobj->parent = obj;
     }
   conn->root_objs = eina_inlist_append(conn->root_objs, EINA_INLIST_GET(obj));

   return obj;
}

static void
_props_free(void *data)
{
   Property *p = data;
   free(p);
}

static void
_object_manager_iface_added_emit(Eldbus_Service_Object *obj,
                                 Eldbus_Service_Object *parent)
{
   Eldbus_Service_Interface *iface;
   Eldbus_Message_Iter *iter, *array;
   Eldbus_Message *sig = eldbus_message_signal_new(parent->path,
                                                 ELDBUS_FDO_INTERFACE_OBJECT_MANAGER,
                                                 "InterfacesAdded");
   EINA_SAFETY_ON_NULL_RETURN(sig);
   iter = eldbus_message_iter_get(sig);
   eldbus_message_iter_arguments_append(iter, "oa{sa{sv}}", obj->path,
                                       &array);

   EINA_LIST_FREE(obj->iface_added, iface)
     {
        if (!_propmgr_iface_props_append(iface, array))
          {
             ERR("Could not append properties to InterfacesAdded signal");
             eldbus_message_unref(sig);
             goto done;
          }
     }
   eldbus_message_iter_container_close(iter, array);
   eldbus_connection_send(parent->conn, sig, NULL, NULL, -1);
   return;

done:
   obj->iface_added = eina_list_free(obj->iface_added);
}

static void
_object_manager_iface_removed_emit(Eldbus_Service_Object *obj,
                                   Eldbus_Service_Object *parent)
{
   Eina_List *l;
   const char *name;
   Eldbus_Message_Iter *iter, *array;
   Eldbus_Message *sig = eldbus_message_signal_new(parent->path,
                                                   ELDBUS_FDO_INTERFACE_OBJECT_MANAGER,
                                                   "InterfacesRemoved");
   EINA_SAFETY_ON_NULL_RETURN(sig);

   iter = eldbus_message_iter_get(sig);
   eldbus_message_iter_arguments_append(iter, "oas", obj->path, &array);

   EINA_LIST_FOREACH(obj->iface_removed, l, name)
     {
        eldbus_message_iter_arguments_append(array, "s", name);
        eina_stringshare_del(name);
     }
   eldbus_message_iter_container_close(iter, array);
   eldbus_connection_send(parent->conn, sig, NULL, NULL, -1);
   obj->iface_removed = eina_list_free(obj->iface_removed);
}

static Eldbus_Service_Object *
_object_manager_parent_find(Eldbus_Service_Object *obj)
{
   if (!obj->parent)
     return NULL;
   if (obj->parent->objmanager)
     return obj->parent;
   return _object_manager_parent_find(obj->parent);
}

static Eina_Bool
_object_manager_changes_process(void *data)
{
   Eldbus_Service_Object *obj = data;
   Eldbus_Service_Object *parent = _object_manager_parent_find(obj);

   obj->idle_enterer_iface_changed = NULL;

   if (!parent)
     {
        const char *name;

        obj->iface_added = eina_list_free(obj->iface_added);
        EINA_LIST_FREE(obj->iface_removed, name)
          eina_stringshare_del(name);

        return EINA_FALSE;
     }

   if (obj->iface_added)
     _object_manager_iface_added_emit(obj, parent);
   if (obj->iface_removed)
     _object_manager_iface_removed_emit(obj, parent);

   return EINA_FALSE;
}

static Eldbus_Service_Interface *
_eldbus_service_interface_add(Eldbus_Service_Object *obj, const char *interface)
{
   Eldbus_Service_Interface *iface;

   iface = eina_hash_find(obj->interfaces, interface);
   if (iface) return iface;

   iface = calloc(1, sizeof(Eldbus_Service_Interface));
   EINA_SAFETY_ON_NULL_RETURN_VAL(iface, NULL);

   EINA_MAGIC_SET(iface, ELDBUS_SERVICE_INTERFACE_MAGIC);
   iface->name = eina_stringshare_add(interface);
   iface->methods = eina_hash_string_superfast_new(NULL);
   iface->properties = eina_hash_string_superfast_new(_props_free);
   iface->obj = obj;
   eina_hash_add(obj->interfaces, iface->name, iface);

   if (!obj->idle_enterer_iface_changed)
     obj->idle_enterer_iface_changed = ecore_idle_enterer_add(_object_manager_changes_process,
                                                obj);
   obj->iface_added = eina_list_append(obj->iface_added, iface);

   return iface;
}

static Eina_Bool
_have_signature(const Eldbus_Arg_Info *args, Eldbus_Message *msg)
{
   const char *sig = dbus_message_get_signature(msg->dbus_msg);
   const char *p = NULL;

   for (; args && args->signature && *sig; args++)
     {
        p = args->signature;
        for (; *sig && *p; sig++, p++)
          {
             if (*p != *sig)
               return EINA_FALSE;
          }
     }

   if (*sig || (p && *p) || (args && args->signature))
     return EINA_FALSE;

   return EINA_TRUE;
}

static Eina_Bool
_eldbus_service_method_add(Eldbus_Service_Interface *interface,
                          const Eldbus_Method *method)
{
   EINA_SAFETY_ON_TRUE_RETURN_VAL(!!eina_hash_find(interface->methods,
                                  method->member), EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(method->member, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(method->cb, EINA_FALSE);

   eina_hash_add(interface->methods, method->member, method);
   return EINA_TRUE;
}

static Eina_Bool
_eldbus_service_property_add(Eldbus_Service_Interface *interface,
                            const Eldbus_Property *property)
{
   Property *p;
   EINA_SAFETY_ON_TRUE_RETURN_VAL(!!eina_hash_find(interface->properties,
                                  property->name), EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(property->type, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(
      dbus_signature_validate_single(property->type, NULL), EINA_FALSE);

   p = calloc(1, sizeof(Property));
   EINA_SAFETY_ON_NULL_RETURN_VAL(p, EINA_FALSE);
   p->property = property;

   return eina_hash_add(interface->properties, property->name, p);
}

/* Check if all signals in desc have valid signatures and return an Eina_Array
 * with each of them. Return NULL if any of the signatures is invalid */
static inline Eina_Array *
_eldbus_service_interface_desc_signals_signatures_get(
   const Eldbus_Service_Interface_Desc *desc)
{
   const Eldbus_Signal *sig;
   Eina_Strbuf *buf = eina_strbuf_new();
   Eina_Array *signatures = eina_array_new(1);

   EINA_SAFETY_ON_NULL_GOTO(buf, fail_signature);
   EINA_SAFETY_ON_NULL_GOTO(signatures, fail_signature);

   for (sig = desc->signals; sig && sig->name; sig++)
     {
        const Eldbus_Arg_Info *arg;

        eina_strbuf_reset(buf);
        for (arg = sig->args; arg && arg->signature; arg++)
          eina_strbuf_append(buf, arg->signature);

        if (!dbus_signature_validate(eina_strbuf_string_get(buf), NULL))
          {
             ERR("Signal with invalid signature: interface=%s signal=%s",
                 desc->interface, sig->name);
             goto fail_signature;
          }

	eina_array_push(signatures,
                        eina_stringshare_add(eina_strbuf_string_get(buf)));
     }
   eina_strbuf_free(buf);

   return signatures;

fail_signature:
   if (buf) eina_strbuf_free(buf);
   if (signatures) eina_array_free(signatures);
   return NULL;
}

static Eldbus_Service_Interface *
_eldbus_service_interface_register(Eldbus_Connection *conn, const char *path, const Eldbus_Service_Interface_Desc *desc, Eina_Bool fallback, unsigned int version)
{
   Eldbus_Service_Object *obj;
   Eldbus_Service_Interface *iface;
   const Eldbus_Method *method;
   const Eldbus_Method2 *method2;
   const Eldbus_Property *property;
   Eina_Array *signatures;

   EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(desc, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(desc->interface, NULL);

   if (!dbus_connection_get_object_path_data(conn->dbus_conn, path,
                                             (void*)&obj))
     {
        ERR("Invalid object path");
        return NULL;
     }

   signatures = _eldbus_service_interface_desc_signals_signatures_get(desc);
   if (!signatures)
     return NULL;

   if (!obj)
     obj = _eldbus_service_object_add(conn, path, fallback);
   else
     obj->introspection_dirty = EINA_TRUE;
   EINA_SAFETY_ON_NULL_GOTO(obj, fail);

   iface = _eldbus_service_interface_add(obj, desc->interface);
   if (!iface)
     goto fail;

   for (method = desc->methods; method && method->member; method++)
     _eldbus_service_method_add(iface, method);

   if(version >= 2)
     {
        Eldbus_Service_Interface_Desc2* desc2 = (void*)desc;
        for (method2 = desc2->methods2; method2 && method2->method.member; method2++)
           _eldbus_service_method_add(iface, &method2->method);
     }

   iface->signals = desc->signals;
   iface->sign_of_signals = signatures;

   for (property = desc->properties; property && property->name; property++)
     _eldbus_service_property_add(iface, property);

   iface->get_func = desc->default_get;
   iface->set_func = desc->default_set;

   return iface;

fail:
   eina_array_free(signatures);

   if (obj && (eina_hash_population(obj->interfaces) < 2))
     _object_free(obj);

   return NULL;
}

EAPI Eldbus_Service_Interface *
eldbus_service_interface_register(Eldbus_Connection *conn, const char *path, const Eldbus_Service_Interface_Desc *desc)
{
   return _eldbus_service_interface_register(conn, path, desc, EINA_FALSE, 1u);
}

EAPI Eldbus_Service_Interface *
eldbus_service_interface_register2(Eldbus_Connection *conn, const char *path, const Eldbus_Service_Interface_Desc2 *desc)
{
   return _eldbus_service_interface_register(conn, path, &desc->description, EINA_FALSE, desc->version);
}

EAPI Eldbus_Service_Interface *
eldbus_service_interface_fallback_register(Eldbus_Connection *conn, const char *path, const Eldbus_Service_Interface_Desc *desc)
{
   return _eldbus_service_interface_register(conn, path, desc, EINA_TRUE, 1u);
}

EAPI Eldbus_Service_Interface *
eldbus_service_interface_fallback_register2(Eldbus_Connection *conn, const char *path, const Eldbus_Service_Interface_Desc2 *desc)
{
   return _eldbus_service_interface_register(conn, path, &desc->description, EINA_TRUE, desc->version);
}

static Eina_Bool
_idle_enterer_propschanged(void *data)
{
   Eldbus_Service_Interface *iface = data;
   Eldbus_Message *msg;
   Eldbus_Message_Iter *main_iter, *dict, *array_invalidate;
   Eina_Hash *added = NULL;
   Property *prop;

   iface->idle_enterer_propschanged = NULL;

   added = eina_hash_string_small_new(NULL);
   msg = eldbus_message_signal_new(iface->obj->path, properties_iface->name,
                                  properties_iface->signals[0].name);
   EINA_SAFETY_ON_NULL_GOTO(msg, error);

   main_iter = eldbus_message_iter_get(msg);
   if (!eldbus_message_iter_arguments_append(main_iter, "sa{sv}", iface->name, &dict))
     {
        eldbus_message_unref(msg);
        goto error;
     }

   if (!iface->props_changed)
     goto invalidate;
   while ((prop = eina_array_pop(iface->props_changed)))
     {
        Eldbus_Message_Iter *entry, *var;
        Eldbus_Message *error_reply = NULL;
        Eina_Bool ret;
        Eldbus_Property_Get_Cb getter = NULL;

        if (eina_hash_find(added, prop->property->name))
          continue;
        eina_hash_add(added, prop->property->name, prop);

        if (prop->property->get_func)
          getter = prop->property->get_func;
        else if (iface->get_func)
          getter = iface->get_func;

        if (!getter || prop->is_invalidate)
          continue;

        if (!eldbus_message_iter_arguments_append(dict, "{sv}", &entry))
          {
             eldbus_message_unref(msg);
             goto error;
          }


        eldbus_message_iter_basic_append(entry, 's', prop->property->name);
        var = eldbus_message_iter_container_new(entry, 'v',
                                               prop->property->type);

        ret = getter(iface, prop->property->name, var, NULL, &error_reply);
        if (!ret)
          {
             eldbus_message_unref(msg);
             if (error_reply)
               {
                  ERR("Error reply was set without pass any input message.");
                  eldbus_message_unref(error_reply);
               }
             ERR("Getter of property %s returned error.", prop->property->name);
             goto error;
          }

        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(dict, entry);
     }
invalidate:
   eldbus_message_iter_container_close(main_iter, dict);

   eldbus_message_iter_arguments_append(main_iter, "as", &array_invalidate);

   if (!iface->prop_invalidated)
     goto end;
   while ((prop = eina_array_pop(iface->prop_invalidated)))
     {
        if (!prop->is_invalidate)
          continue;
        eldbus_message_iter_basic_append(array_invalidate, 's',
                                        prop->property->name);
     }
end:
   eldbus_message_iter_container_close(main_iter, array_invalidate);

   eldbus_service_signal_send(iface, msg);
error:
   if (added)
     eina_hash_free(added);
   if (iface->props_changed)
     eina_array_flush(iface->props_changed);
   if (iface->prop_invalidated)
     eina_array_flush(iface->prop_invalidated);
   return ECORE_CALLBACK_CANCEL;
}

static void
_interface_free(Eldbus_Service_Interface *interface)
{
   const char *sig;
   Eldbus_Service_Object *obj;
   Eina_List *l;

   if (interface == introspectable || interface == properties_iface ||
       interface == objmanager)
     return;

   /**
    * flush props changes before remove interface
    */
   if (interface->idle_enterer_propschanged)
     {
        ecore_idle_enterer_del(interface->idle_enterer_propschanged);
        _idle_enterer_propschanged(interface);
     }

   eina_hash_free(interface->methods);
   while ((sig = eina_array_pop(interface->sign_of_signals)))
     eina_stringshare_del(sig);
   eina_array_free(interface->sign_of_signals);
   eina_hash_free(interface->properties);
   if (interface->props_changed)
     eina_array_free(interface->props_changed);
   if (interface->prop_invalidated)
     eina_array_free(interface->prop_invalidated);

   obj = interface->obj;
   l = eina_list_data_find_list(obj->iface_added, interface);
   if (l)
     {
        /* Adding and removing the interface in the same main loop iteration.
         * Let's not send any signal */
        obj->iface_added = eina_list_remove_list(obj->iface_added, l);
        if (!obj->iface_added && !obj->iface_removed && obj->idle_enterer_iface_changed)
          {
             ecore_idle_enterer_del(obj->idle_enterer_iface_changed);
             obj->idle_enterer_iface_changed = NULL;
          }
     }
   else
     {
        if (!obj->idle_enterer_iface_changed)
          {
             obj->idle_enterer_iface_changed = ecore_idle_enterer_add(
                _object_manager_changes_process, obj);
          }

        obj->iface_removed = eina_list_append(
           obj->iface_removed, eina_stringshare_ref(interface->name));
     }

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

static void _children_ifaces_add_removed_flush(Eldbus_Service_Object *obj)
{
   Eldbus_Service_Object *children;

   EINA_INLIST_FOREACH(obj->children, children)
     {
        /**
         * if there a object manager in some child
         * that object manager is responsible for they
         * children objects
         */
        if (!obj->objmanager)
          _children_ifaces_add_removed_flush(children);
     }

   if (obj->idle_enterer_iface_changed)
     {
        ecore_idle_enterer_del(obj->idle_enterer_iface_changed);
        _object_manager_changes_process(obj);
     }
}

static void
_object_free(Eldbus_Service_Object *obj)
{
   Eina_Iterator *iterator;
   Eldbus_Service_Interface *iface;

   if (obj->objmanager)
     {
        Eldbus_Service_Object *children;

        /**
         * Flush the iface_add/removed of all children objects
         * that this object is the ObjectManager
         */
        EINA_INLIST_FOREACH(obj->children, children)
          _children_ifaces_add_removed_flush(children);
     }

   iterator = eina_hash_iterator_data_new(obj->interfaces);
   EINA_ITERATOR_FOREACH(iterator, iface)
     _interface_free(iface);

   /**
    * Flush our iface_add/removed if this object are
    * children of some other path with ObjectManager
    */
   if (obj->idle_enterer_iface_changed)
     {
        ecore_idle_enterer_del(obj->idle_enterer_iface_changed);
        _object_manager_changes_process(obj);
     }

   while (obj->children)
     {
        Eldbus_Service_Object *child;
        child = EINA_INLIST_CONTAINER_GET(obj->children, Eldbus_Service_Object);
        obj->children = eina_inlist_remove(obj->children, obj->children);
        if (obj->parent)
          {
             obj->parent->children = eina_inlist_append(obj->parent->children,
							EINA_INLIST_GET(child));
             child->parent = obj->parent;
          }
        else
          {
             obj->conn->root_objs = eina_inlist_append(obj->conn->root_objs,
                                                       EINA_INLIST_GET(child));
             child->parent = NULL;
          }
     }

   if (obj->parent)
     obj->parent->children = eina_inlist_remove(obj->parent->children,
						EINA_INLIST_GET(obj));
   else
     obj->conn->root_objs = eina_inlist_remove(obj->conn->root_objs,
                                               EINA_INLIST_GET(obj));

   eldbus_data_del_all(&obj->data);

   eina_hash_free(obj->interfaces);
   eina_iterator_free(iterator);
   if (obj->introspection_data)
     eina_strbuf_free(obj->introspection_data);
   eina_stringshare_del(obj->path);
   free(obj);
}

static void
_on_connection_free(void *data, const void *dead_pointer EINA_UNUSED)
{
   Eldbus_Service_Object *obj = data;
   dbus_connection_unregister_object_path(obj->conn->dbus_conn, obj->path);
}

EAPI void
eldbus_service_interface_unregister(Eldbus_Service_Interface *iface)
{
   Eldbus_Service_Object *obj;
   ELDBUS_SERVICE_INTERFACE_CHECK(iface);
   obj = iface->obj;
   eina_hash_del(obj->interfaces, NULL, iface);
   _interface_free(iface);
   obj->introspection_dirty = EINA_TRUE;
}

EAPI void
eldbus_service_object_unregister(Eldbus_Service_Interface *iface)
{
   ELDBUS_SERVICE_INTERFACE_CHECK(iface);
   /*
    * It will be freed when _object_unregister() is called
    * by libdbus.
    */
   eldbus_connection_free_cb_del(iface->obj->conn, _on_connection_free, iface->obj);
   dbus_connection_unregister_object_path(iface->obj->conn->dbus_conn, iface->obj->path);
}

static void
_object_unregister(DBusConnection *conn EINA_UNUSED, void *user_data)
{
   Eldbus_Service_Object *obj = user_data;
   _object_free(obj);
}

static Eldbus_Message*
_eldbus_method_call(Eldbus_Method const* method, Eldbus_Service_Interface* iface, Eldbus_Message* msg)
{
  if(method->flags & ELDBUS_METHOD_FLAG_HAS_DATA)
    {
      Eldbus_Method_Data_Cb cb = (void *)method->cb;
      return cb(((Eldbus_Method2 const*)method)->data, iface, msg);
    }
  else
    {
      return method->cb(iface, msg);
    }
}

static DBusHandlerResult
_object_handler(DBusConnection *dbus_conn EINA_UNUSED, DBusMessage *msg, void *user_data)
{
   Eldbus_Service_Object *obj;
   Eldbus_Service_Interface *iface;
   const Eldbus_Method *method;
   Eldbus_Message *eldbus_msg, *reply;
   Eldbus_Connection *conn;

   obj = user_data;
   if (!obj) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
   conn = obj->conn;

   DBG("Connection@%p Got message:\n"
          "  Type: %s\n"
          "  Path: %s\n"
          "  Interface: %s\n"
          "  Member: %s\n"
          "  Sender: %s", obj->conn,
          dbus_message_type_to_string(dbus_message_get_type(msg)),
          dbus_message_get_path(msg),
          dbus_message_get_interface(msg),
          dbus_message_get_member(msg),
          dbus_message_get_sender(msg));

   iface = eina_hash_find(obj->interfaces, dbus_message_get_interface(msg));
   if (!iface) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

   method = eina_hash_find(iface->methods, dbus_message_get_member(msg));
   if (!method) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

   eldbus_msg = eldbus_message_new(EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(eldbus_msg, DBUS_HANDLER_RESULT_NEED_MEMORY);
   eldbus_msg->dbus_msg = msg;
   dbus_message_ref(eldbus_msg->dbus_msg);
   dbus_message_iter_init(eldbus_msg->dbus_msg, &eldbus_msg->iterator->dbus_iterator);

   eldbus_init();
   eldbus_connection_ref(conn);

   if (!_have_signature(method->in, eldbus_msg))
     reply = eldbus_message_error_new(eldbus_msg, DBUS_ERROR_INVALID_SIGNATURE,
                                          "See introspectable to know the expected signature");
   else
     {
       if (iface->obj)
         reply = _eldbus_method_call(method, iface, eldbus_msg);
       else
         {
           /* if iface does have obj it is some of FreeDesktop interfaces:
              Introspectable, Properties or ObjectManager */
           iface->obj = obj;
           reply = _eldbus_method_call(method, iface, eldbus_msg);
           iface->obj = NULL;
         }
     }

   eldbus_message_unref(eldbus_msg);
   if (reply)
     _eldbus_connection_send(conn, reply, NULL, NULL, -1);

   eldbus_connection_unref(conn);
   eldbus_shutdown();

   return DBUS_HANDLER_RESULT_HANDLED;
}

EAPI Eldbus_Connection *
eldbus_service_connection_get(const Eldbus_Service_Interface *iface)
{
   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL);
   return iface->obj->conn;
}

EAPI const char *
eldbus_service_object_path_get(const Eldbus_Service_Interface *iface)
{
   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL);
   return iface->obj->path;
}

EAPI Eldbus_Message *
eldbus_service_signal_new(const Eldbus_Service_Interface *iface, unsigned int signal_id)
{
   unsigned size;
   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL);
   size = eina_array_count(iface->sign_of_signals);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(signal_id < size, NULL);

   return eldbus_message_signal_new(iface->obj->path, iface->name,
                                   iface->signals[signal_id].name);
}

EAPI Eina_Bool
eldbus_service_signal_emit(const Eldbus_Service_Interface *iface, unsigned int signal_id, ...)
{
   Eldbus_Message *sig;
   va_list ap;
   Eina_Bool r;
   const char *signature;
   unsigned size;

   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE);
   size = eina_array_count(iface->sign_of_signals);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(signal_id < size, EINA_FALSE);

   sig = eldbus_service_signal_new(iface, signal_id);
   EINA_SAFETY_ON_NULL_RETURN_VAL(sig, EINA_FALSE);

   signature = eina_array_data_get(iface->sign_of_signals, signal_id);
   va_start(ap, signal_id);
   r = eldbus_message_arguments_vappend(sig, signature, ap);
   va_end(ap);
   if (!r)
     {
        eldbus_message_unref(sig);
        return EINA_FALSE;
     }

   eldbus_service_signal_send(iface, sig);
   return EINA_TRUE;
}

EAPI Eina_Bool
eldbus_service_signal_send(const Eldbus_Service_Interface *iface, Eldbus_Message *signal_msg)
{
   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(signal_msg, EINA_FALSE);
   _eldbus_connection_send(iface->obj->conn, signal_msg, NULL, NULL, -1);
   return EINA_TRUE;
}

EAPI void
eldbus_service_object_data_set(Eldbus_Service_Interface *iface, const char *key, const void *data)
{
   ELDBUS_SERVICE_INTERFACE_CHECK(iface);
   EINA_SAFETY_ON_NULL_RETURN(key);
   EINA_SAFETY_ON_NULL_RETURN(data);
   eldbus_data_set(&(iface->obj->data), key, data);
}

EAPI void *
eldbus_service_object_data_get(const Eldbus_Service_Interface *iface, const char *key)
{
   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
   return eldbus_data_get(&(((Eldbus_Service_Object *)iface->obj)->data), key);
}

EAPI void *
eldbus_service_object_data_del(Eldbus_Service_Interface *iface, const char *key)
{
   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
   return eldbus_data_del(&(((Eldbus_Service_Object *)iface->obj)->data), key);
}

EAPI Eina_Bool
eldbus_service_property_changed(const Eldbus_Service_Interface *interface, const char *name)
{
   Property *prop;
   Eldbus_Service_Interface *iface = (Eldbus_Service_Interface *)interface;
   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);

   prop = eina_hash_find(iface->properties, name);
   EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE);

   if (!iface->idle_enterer_propschanged)
     iface->idle_enterer_propschanged = ecore_idle_enterer_add(_idle_enterer_propschanged, iface);
   if (!iface->props_changed)
     iface->props_changed = eina_array_new(1);

   return eina_array_push(iface->props_changed, prop);
}

EAPI Eina_Bool
eldbus_service_property_invalidate_set(const Eldbus_Service_Interface *interface, const char *name, Eina_Bool is_invalidate)
{
   Property *prop;
   Eldbus_Service_Interface *iface = (Eldbus_Service_Interface *) interface;

   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);

   prop = eina_hash_find(iface->properties, name);
   EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE);

   if (prop->is_invalidate == is_invalidate)
     return EINA_TRUE;

   prop->is_invalidate = is_invalidate;

   if (!iface->idle_enterer_propschanged)
     iface->idle_enterer_propschanged = ecore_idle_enterer_add(_idle_enterer_propschanged, iface);

   if (is_invalidate)
     {
        if (!iface->props_changed)
          iface->props_changed = eina_array_new(1);
        return eina_array_push(iface->props_changed, prop);
     }

   if (!iface->prop_invalidated)
     iface->prop_invalidated = eina_array_new(1);
   return eina_array_push(iface->prop_invalidated, prop);
}

EAPI Eina_Bool
eldbus_service_object_manager_attach(Eldbus_Service_Interface *iface)
{
   Eldbus_Service_Object *obj;
   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE);

   obj = iface->obj;

   /* We already have it and we registered it ourselves */
   if (obj->objmanager)
     return EINA_TRUE;

   /* Ugh. User already registered the ObjectManager interface himself? */
   if (eina_hash_find(obj->interfaces, objmanager->name))
     return EINA_FALSE;

   if (!eina_hash_add(obj->interfaces, objmanager->name, objmanager))
     return EINA_FALSE;

   /*
    * Flush the iface_added and iface_removed, otherwise it could be sent
    * with path equal to our path rather than from the previous
    * ObjectManager
    */
   if (obj->idle_enterer_iface_changed)
     ecore_idle_enterer_del(obj->idle_enterer_iface_changed);
   _object_manager_changes_process(obj);

   obj->objmanager = objmanager;
   obj->introspection_dirty = EINA_TRUE;
   return EINA_TRUE;
}

EAPI Eina_Bool
eldbus_service_object_manager_detach(Eldbus_Service_Interface *iface)
{
   Eldbus_Service_Object *obj, *children;
   Eina_Bool ret;

   ELDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE);
   obj = iface->obj;
   if (!obj->objmanager)
     return EINA_TRUE;

   /**
    * Flush the iface_add/removed of all children objects
    * that this object is the ObjectManager
    */
   EINA_INLIST_FOREACH(obj->children, children)
     _children_ifaces_add_removed_flush(children);

   ret = eina_hash_del(obj->interfaces, objmanager->name, NULL);
   obj->objmanager = NULL;
   obj->introspection_dirty = EINA_TRUE;
   return ret;
}
