#include "e.h"

typedef struct _E_Msg_Event E_Msg_Event;

struct _E_Msg_Handler
{
   void          (*func)(void *data, const char *name, const char *info, int val, E_Object *obj, void *msgdata);
   void         *data;
   unsigned char delete_me E_BITFIELD;
};

struct _E_Msg_Event
{
   char     *name;
   char     *info;
   int       val;
   E_Object *obj;
   void     *msgdata;
   void      (*afterfunc)(void *data, E_Object *obj, void *msgdata);
   void     *afterdata;
};

/* local subsystem functions */
static Eina_Bool _e_msg_event_cb(void *data, int ev_type, void *ev);
static void      _e_msg_event_free(void *data, void *ev);

/* local subsystem globals */
static Eina_List *handlers = NULL;
static Eina_List *del_handlers = NULL;
static int processing_handlers = 0;
static int E_EVENT_MSG = 0;
static Ecore_Event_Handler *hand = NULL;

/* externally accessible functions */
EINTERN int
e_msg_init(void)
{
   E_EVENT_MSG = ecore_event_type_new();
   hand = ecore_event_handler_add(E_EVENT_MSG, _e_msg_event_cb, NULL);
   return 1;
}

EINTERN int
e_msg_shutdown(void)
{
   while (handlers)
     e_msg_handler_del(eina_list_data_get(handlers));
   E_EVENT_MSG = 0;
   if (hand) ecore_event_handler_del(hand);
   hand = NULL;
   return 1;
}

E_API void
e_msg_send(const char *name, const char *info, int val, E_Object *obj, void *msgdata, void (*afterfunc)(void *data, E_Object *obj, void *msgdata), void *afterdata)
{
   unsigned int size, pos, name_len, info_len;
   E_Msg_Event *ev;

   name_len = info_len = 0;
   size = sizeof(E_Msg_Event);
   if (name) name_len = strlen(name) + 1;
   if (info) info_len = strlen(info) + 1;
   ev = malloc(size + name_len + info_len);
   if (!ev) return;
   pos = size;
   if (name)
     {
        ev->name = ((char *)ev) + pos;
        pos += name_len;
        strcpy(ev->name, name);
     }
   if (info)
     {
        ev->info = ((char *)ev) + pos;
        strcpy(ev->info, info);
     }
   ev->val = val;
   ev->obj = obj;
   ev->msgdata = msgdata;
   ev->afterfunc = afterfunc;
   ev->afterdata = afterdata;
   if (ev->obj) e_object_ref(ev->obj);
   ecore_event_add(E_EVENT_MSG, ev, _e_msg_event_free, NULL);
}

E_API E_Msg_Handler *
e_msg_handler_add(void (*func)(void *data, const char *name, const char *info, int val, E_Object *obj, void *msgdata), void *data)
{
   E_Msg_Handler *emsgh;

   emsgh = calloc(1, sizeof(E_Msg_Handler));
   if (!emsgh) return NULL;
   emsgh->func = func;
   emsgh->data = data;
   handlers = eina_list_append(handlers, emsgh);
   return emsgh;
}

E_API void
e_msg_handler_del(E_Msg_Handler *emsgh)
{
   if (processing_handlers > 0)
     {
        emsgh->delete_me = 1;
        del_handlers = eina_list_append(del_handlers, emsgh);
     }
   else
     {
        handlers = eina_list_remove(handlers, emsgh);
        free(emsgh);
     }
}

/* local subsystem functions */

static Eina_Bool
_e_msg_event_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *ev)
{
   E_Msg_Event *e;
   Eina_List *l;
   E_Msg_Handler *emsgh;

   processing_handlers++;
   e = ev;
   EINA_LIST_FOREACH(handlers, l, emsgh)
     {
        if (!emsgh->delete_me)
          emsgh->func(emsgh->data, e->name, e->info, e->val, e->obj, e->msgdata);
     }
   if (e->afterfunc) e->afterfunc(e->afterdata, e->obj, e->msgdata);
   processing_handlers--;
   if ((processing_handlers == 0) && (del_handlers))
     {
        E_FREE_LIST(del_handlers, e_msg_handler_del);
     }
   return 1;
}

static void
_e_msg_event_free(void *data EINA_UNUSED, void *ev)
{
   E_Msg_Event *e;

   e = ev;
   if (e->obj) e_object_unref(e->obj);

   E_FREE(ev);
}

