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

#include <Elementary.h>

#include "elm_priv.h"

#include "elm_sys_notify_interface_eo.h"
#include "elm_sys_notify_eo.h"
#include "elm_sys_notify_dbus_eo.h"
#include "elm_sys_notify_dbus_eo.legacy.h"

#define MY_CLASS ELM_SYS_NOTIFY_CLASS

#define MY_CLASS_NAME        "Elm_Sys_Notify"
#define MY_CLASS_NAME_LEGACY "elm_sys_notify"

EAPI int ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED = 0;
EAPI int ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED      = 0;

typedef const Efl_Class *(*Class_Get_Func)(void);

static Elm_Sys_Notify *_singleton = NULL;

/*
 * Registration of notification servers is done UNIQUELY
 * in the two structures below.
 *   1) ALWAYS add a SRV_XXX before __SRV_LAST
 *   2) copy the #if ... #else ... #endif with the appropriate class
 *      getter (generated by Eolian) or NULL when unsupported
 *
 * The rest of the code relies on the Srv enum and _class_getters
 * to register/unregister notification servers.
 */

typedef enum
{
   SRV_DBUS = 0,
   __SRV_LAST /* Sentinel */
} Srv;

static Class_Get_Func _class_getters[__SRV_LAST] =
{
#ifdef ELM_SYS_NOTIFY_DBUS_CLASS
   [SRV_DBUS] = elm_sys_notify_dbus_class_get
#else
   [SRV_DBUS] = NULL
#endif
};



typedef struct
{
   Eo *servers[__SRV_LAST];
} Elm_Sys_Notify_Data;

/*============================================================================*
 *                  Constructor/Destructor - Singleton setup                  *
 *============================================================================*/

EOLIAN static Efl_Object *
_elm_sys_notify_efl_object_constructor(Eo                  *obj,
                                    Elm_Sys_Notify_Data *sd  EINA_UNUSED)
{
   if (_singleton != NULL)
     {
        ERR("Attempted to create another system notification manager");
        return NULL;
     }

   obj = efl_constructor(efl_super(obj, MY_CLASS));
   _singleton = obj;

   return obj;
}

EOLIAN static void
_elm_sys_notify_efl_object_destructor(Eo                  *obj,
                                   Elm_Sys_Notify_Data *sd  EINA_UNUSED)
{
   efl_destructor(efl_super(obj, MY_CLASS));
   _singleton = NULL;
}


/*============================================================================*
 *                           Notification Interface                           *
 *============================================================================*/

EOLIAN static void
_elm_sys_notify_elm_sys_notify_interface_send(const Eo *obj EINA_UNUSED,
                                              Elm_Sys_Notify_Data *sd,
                                              unsigned int replaces_id,
                                              const char *icon,
                                              const char *summary,
                                              const char *body,
                                              Elm_Sys_Notify_Urgency urgency,
                                              int timeout,
                                              Elm_Sys_Notify_Send_Cb cb,
                                              const void *cb_data)
{
   Srv i;

   /* Propagate to all registered servers */
   for (i = SRV_DBUS; i < __SRV_LAST; ++i)
     if (sd->servers[i])
       elm_obj_sys_notify_interface_send(sd->servers[i], replaces_id, icon, summary, body, urgency, timeout, cb, cb_data);
}

EOLIAN static void
_elm_sys_notify_elm_sys_notify_interface_simple_send(const Eo *obj EINA_UNUSED,
                                                     Elm_Sys_Notify_Data *sd,
                                                     const char *icon,
                                                     const char *summary,
                                                     const char *body)
{
   Srv i;

   /* Propagate to all registered servers */
   for (i = SRV_DBUS; i < __SRV_LAST; ++i)
     if (sd->servers[i])
       elm_obj_sys_notify_interface_simple_send(sd->servers[i], icon, summary, body);
}

EOLIAN static void
_elm_sys_notify_elm_sys_notify_interface_close(const Eo *obj EINA_UNUSED,
                                               Elm_Sys_Notify_Data *sd,
                                               unsigned int id)
{
   Srv i;

   /* Propagate to all registered servers */
   for (i = SRV_DBUS; i < __SRV_LAST; ++i)
     if (sd->servers[i])
       elm_obj_sys_notify_interface_close(sd->servers[i], id);
}


/*============================================================================*
 *                                   Methods                                  *
 *============================================================================*/

EOLIAN static Eina_Bool
_elm_sys_notify_servers_set(Eo                     *obj  EINA_UNUSED,
                            Elm_Sys_Notify_Data    *sd,
                            Elm_Sys_Notify_Server   servers)
{
   Srv i;
   Class_Get_Func class_get;

   /* Update the notification servers */
   for (i = SRV_DBUS; i < __SRV_LAST; ++i)
     {
        if (sd->servers[i])
          {
             /* Delete if server type is not provided */
             if (!(servers & (1 << i)))
               efl_del(sd->servers[i]);
          }
        else
          {
             /* If server is required, create when nonexistant */
             if (servers & (1 << i))
               {
                  class_get = _class_getters[i];
                  if (!class_get)
                    {
                       CRI("Unsupported notification server");
                       return EINA_FALSE;
                    }

                  sd->servers[i] = efl_add(class_get(), NULL);
                  if (EINA_UNLIKELY(!(sd->servers[i])))
                    {
                       CRI("Failed to create notification server");
                       return EINA_FALSE;
                    }
               }
          }
     }

   return EINA_TRUE;
}

EOLIAN static Elm_Sys_Notify_Server
_elm_sys_notify_servers_get(const Eo            *obj EINA_UNUSED,
                            Elm_Sys_Notify_Data *sd)
{
   Elm_Sys_Notify_Server servers = ELM_SYS_NOTIFY_SERVER_NONE;
   Srv i;

   for (i = SRV_DBUS; i < __SRV_LAST; ++i)
     if (sd->servers[i])
       servers |= (1 << i);

   return servers;
}

EOLIAN static Elm_Sys_Notify *
_elm_sys_notify_singleton_get(void)
{
   if (!_singleton)
     _singleton = efl_add(MY_CLASS, efl_main_loop_get());
   return _singleton;
}

EOLIAN static void
_elm_sys_notify_class_constructor(Efl_Class *klass EINA_UNUSED)
{
   ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED = ecore_event_type_new();
   ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED = ecore_event_type_new();
}

/*============================================================================*
 *                                 Legacy API                                 *
 *============================================================================*/

void
_elm_unneed_sys_notify(void)
{
   Elm_Sys_Notify *manager = elm_obj_sys_notify_singleton_get();
   if (manager)
     {
        elm_obj_sys_notify_servers_set(manager, ELM_SYS_NOTIFY_SERVER_NONE);
        efl_del(manager);
     }
}

EAPI Eina_Bool
elm_need_sys_notify(void)
{
   Elm_Sys_Notify_Server servers = ELM_SYS_NOTIFY_SERVER_NONE;
   Elm_Sys_Notify *manager;
   Srv i;

   /* In theory, there can be N notification managers, but
    * in the implementation there will be only one: the
    * singleton which is initialized here. */
   manager = elm_obj_sys_notify_singleton_get();
   if (EINA_UNLIKELY(!manager))
     {
        CRI("Failed to get notification manager");
        return EINA_FALSE;
     }

   /* If servers have alread been configured, this is not the right
    * function to be called! */
   if (elm_sys_notify_servers_get(manager) != ELM_SYS_NOTIFY_SERVER_NONE)
     {
        ERR("Notification servers have already been configured. "
            "Use elm_sys_notify_servers_set() instead.");
        return EINA_FALSE;
     }

   /* Register available notification servers */
   for (i = SRV_DBUS; i < __SRV_LAST; ++i)
     if (_class_getters[i])
       servers |= (1 << i);

   /* If no server are available, don't even bother... */
   if (servers == ELM_SYS_NOTIFY_SERVER_NONE)
     return EINA_FALSE;

   return elm_sys_notify_servers_set(manager, servers);
}

EAPI void
elm_sys_notify_send(unsigned int            replaces_id,
                    const char             *icon,
                    const char             *summary,
                    const char             *body,
                    Elm_Sys_Notify_Urgency  urgency,
                    int                     timeout,
                    Elm_Sys_Notify_Send_Cb  cb,
                    const void             *cb_data)
{
   elm_obj_sys_notify_interface_send(_singleton, replaces_id, icon, summary, body, urgency, timeout, cb, cb_data);
}

EAPI void
elm_sys_notify_close(unsigned int id)
{
   elm_obj_sys_notify_interface_close(_singleton, id);
}

#include "elm_sys_notify_eo.c"

