#include "e.h"

#ifdef USE_IPC
/* local subsystem functions */
static Eina_Bool _e_ipc_cb_client_add(void *data __UNUSED__, int type __UNUSED__, void *event);
static Eina_Bool _e_ipc_cb_client_del(void *data __UNUSED__, int type __UNUSED__, void *event);
static Eina_Bool _e_ipc_cb_client_data(void *data __UNUSED__, int type __UNUSED__, void *event);

/* local subsystem globals */
static Ecore_Ipc_Server *_e_ipc_server = NULL;
#endif

/* externally accessible functions */
EINTERN int
e_ipc_init(void)
{
#ifdef USE_IPC
   char buf[4096], buf2[128], buf3[4096];
   char *tmp, *user, *disp, *base;
   int pid, trynum = 0, id1 = 0;
   struct stat st;

   tmp = getenv("TMPDIR");
   if (!tmp) tmp = "/tmp";
   base = tmp;

   tmp = getenv("XDG_RUNTIME_DIR");
   if (tmp)
     {
        if (stat(tmp, &st) == 0)
          {
             if ((st.st_uid == getuid()) &&
                 ((st.st_mode & (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)) ==
                  (S_IRWXU | S_IFDIR)))
               base = tmp;
             else
               ERR("XDG_RUNTIME_DIR of '%s' failed permissions check", tmp);
          }
        else
          ERR("XDG_RUNTIME_DIR of '%s' cannot be accessed", tmp);
     }
   
   tmp = getenv("SD_USER_SOCKETS_DIR");
   if (tmp)
     {
        if (stat(tmp, &st) == 0)
          {
             if ((st.st_uid == getuid()) &&
                 ((st.st_mode & (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)) ==
                  (S_IRWXU | S_IFDIR)))
               base = tmp;
             else
               ERR("SD_USER_SOCKETS_DIR of '%s' failed permissions check", tmp);
          }
        else
          ERR("SD_USER_SOCKETS_DIR of '%s' cannot be accessed", tmp);
     }

   user = getenv("USER");
   if (!user)
     {
        int uidint;

        user = "__unknown__";
        uidint = getuid();
        if (uidint >= 0)
          {
             snprintf(buf2, sizeof(buf2), "%i", uidint);
             user = buf2;
          }
     }

   disp = getenv("DISPLAY");
   if (!disp) disp = ":0";

   e_util_env_set("E_IPC_SOCKET", "");

   pid = (int)getpid();
   for (trynum = 0; trynum <= 4096; trynum++)
     {
        snprintf(buf, sizeof(buf), "%s/e-%s@%x",
                 base, user, id1);
        if (mkdir(buf, S_IRWXU) < 0)
          goto retry;
        if (stat(buf, &st) < 0)
          goto retry;
        if ((st.st_uid == getuid()) &&
            ((st.st_mode & (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)) ==
             (S_IRWXU | S_IFDIR)))
          {
             snprintf(buf3, sizeof(buf3), "%s/%s-%i",
                      buf, disp, pid);
             _e_ipc_server = ecore_ipc_server_add
                (ECORE_IPC_LOCAL_SYSTEM, buf3, 0, NULL);
             if (_e_ipc_server) break;
          }
retry:
        id1 = rand();
     }
   if (!_e_ipc_server)
     {
        ERR("Gave up after 4096 sockets in '%s'. All failed", base);
        return 0;
     }

   e_util_env_set("E_IPC_SOCKET", buf3);
   ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_ADD,
                           _e_ipc_cb_client_add, NULL);
   ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_DEL,
                           _e_ipc_cb_client_del, NULL);
   ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_DATA,
                           _e_ipc_cb_client_data, NULL);

   e_ipc_codec_init();
#endif
   return 1;
}

EINTERN int
e_ipc_shutdown(void)
{
#ifdef USE_IPC
   e_ipc_codec_shutdown();
   if (_e_ipc_server)
     {
        ecore_ipc_server_del(_e_ipc_server);
        _e_ipc_server = NULL;
     }
#endif
   return 1;
}

#ifdef USE_IPC
/* local subsystem globals */
static Eina_Bool
_e_ipc_cb_client_add(void *data __UNUSED__, int type __UNUSED__, void *event)
{
   Ecore_Ipc_Event_Client_Add *e;

   e = event;
   if (ecore_ipc_client_server_get(e->client) != _e_ipc_server)
     return ECORE_CALLBACK_PASS_ON;
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_e_ipc_cb_client_del(void *data __UNUSED__, int type __UNUSED__, void *event)
{
   Ecore_Ipc_Event_Client_Del *e;

   e = event;
   if (ecore_ipc_client_server_get(e->client) != _e_ipc_server)
     return ECORE_CALLBACK_PASS_ON;
   /* delete client sruct */
   e_thumb_client_del(e);
   e_fm2_client_del(e);
   ecore_ipc_client_del(e->client);
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_e_ipc_cb_client_data(void *data __UNUSED__, int type __UNUSED__, void *event)
{
   Ecore_Ipc_Event_Client_Data *e;

   e = event;
   if (ecore_ipc_client_server_get(e->client) != _e_ipc_server)
     return ECORE_CALLBACK_PASS_ON;
   switch (e->major)
     {
      case E_IPC_DOMAIN_SETUP:
      case E_IPC_DOMAIN_REQUEST:
      case E_IPC_DOMAIN_REPLY:
      case E_IPC_DOMAIN_EVENT:
        switch (e->minor)
          {
           case E_IPC_OP_EXEC_ACTION:
           {
              E_Ipc_2Str *req = NULL;

              if (e_ipc_codec_2str_dec(e->data, e->size, &req))
                {
                   Eina_List *m = e_manager_list();
                   int len, ok = 0;
                   void *d;

                   if (m)
                     {
                        E_Manager *man = eina_list_data_get(m);

                        if (man)
                          {
                             E_Action *act = e_action_find(req->str1);

                             if ((act) && (act->func.go))
                               {
                                  act->func.go(E_OBJECT(man), req->str2);
                                  ok = 1;
                               }
                          }
                     }

                   d = e_ipc_codec_int_enc(ok, &len);
                   if (d)
                     {
                        ecore_ipc_client_send(e->client,
                                              E_IPC_DOMAIN_REPLY,
                                              E_IPC_OP_EXEC_ACTION_REPLY,
                                              0, 0, 0, d, len);
                        free(d);
                     }

                   if (req)
                     {
                        E_FREE(req->str1);
                        E_FREE(req->str2);
                        E_FREE(req);
                     }
                }
           }
           break;

           default:
             break;
          }
        break;

      case E_IPC_DOMAIN_THUMB:
        e_thumb_client_data(e);
        break;

      case E_IPC_DOMAIN_FM:
        e_fm2_client_data(e);
        break;

      case E_IPC_DOMAIN_ALERT:
      {
         switch (e->minor)
           {
            case E_ALERT_OP_RESTART:
              if (getenv("E_START_MTRACK"))
                e_util_env_set("MTRACK", "track");
              ecore_app_restart();
              break;

            case E_ALERT_OP_EXIT:
              exit(-11);
              break;
           }
      }
      break;

      default:
        break;
     }
   return ECORE_CALLBACK_PASS_ON;
}

#endif
