#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

/* by Azundris, with thanks to Corey Donohoe <atmos@atmos.org> */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>

#include <Ecore.h>
#include "ecore_private.h"
#include <Ecore_Ipc.h>

#include "ecore_config_ipc.h"
#include "ecore_config_util.h"
#include "ecore_config_private.h"

#include "Ecore_Config.h"


/*****************************************************************************/

static int
_ecore_config_ipc_ecore_string_get(char **m, char **r)
{
   char               *q;
   int                 l = 0;

   if (!m || !*m)
      return ECORE_CONFIG_ERR_NODATA;
   if (!r)
      return ECORE_CONFIG_ERR_FAIL;
   q = *m;
   if (*q != 's')
      return ECORE_CONFIG_ERR_TYPEMISMATCH;
   q++;
   l = (*(q++)) << 8;
   l += *(q++);
   *r = q;
   q += l;
   *m = q;
   WRN("IPC/eCore: got string-%d \"%s\"", l, *r);
   return ECORE_CONFIG_ERR_SUCC;
}

static char               *
_ecore_config_ipc_global_prop_list(Ecore_Config_Server * srv __UNUSED__, long serial __UNUSED__)
{
   Ecore_Config_DB_File  *db;
   char                 **keys;
   int                    key_count, x;
   estring               *s;
   int                    f;
   char                   buf[PATH_MAX], *p;
   // char		*data;   		UNUSED
   Ecore_Config_Type      type;

   db = NULL;
   s = estring_new(8192);
   f = 0;
   if ((p = getenv("HOME")))
     {
	snprintf(buf, sizeof(buf), "%s/.e/config.eet", p);
	if (!(db = _ecore_config_db_open_read(buf)))
	  {
	     strcpy(buf, PACKAGE_DATA_DIR"/system.eet");
	     if (!(db = _ecore_config_db_open_read(buf)))
	       return NULL;
	  }
     }
   if (!db) return NULL;
   key_count = 0;
   keys = _ecore_config_db_keys_get(db, &key_count);
   if (keys)
     {
	for (x = 0; x < key_count; x++)
	  {
	     type = _ecore_config_db_key_type_get(db, keys[x]);
	     switch (type)
	       {
		  case ECORE_CONFIG_INT:
		    estring_appendf(s, "%s%s: integer", f ? "\n" : "", keys[x]);
		    break;
		  case ECORE_CONFIG_BLN:
		    estring_appendf(s, "%s%s: boolean", f ? "\n" : "", keys[x]);
		    break;
		  case ECORE_CONFIG_FLT:
		    estring_appendf(s, "%s%s: float", f ? "\n" : "", keys[x]);
		    break;
		  case ECORE_CONFIG_STR:
		    estring_appendf(s, "%s%s: string", f ? "\n" : "", keys[x]);
		    break;
		  case ECORE_CONFIG_RGB:
		    estring_appendf(s, "%s%s: colour", f ? "\n" : "", keys[x]);
		    break;
		  case ECORE_CONFIG_THM:
		    estring_appendf(s, "%s%s: theme", f ? "\n" : "", keys[x]);
		    break;
		  case ECORE_CONFIG_SCT:
		    estring_appendf(s, "%s%s: structure", f ? "\n" : "", keys[x]);
		    break;
		  default:
		    estring_appendf(s, "%s%s: unknown", f ? "\n" : "", keys[x]);
		    continue;
	       }
	     f = 1;
	  }
     }
   _ecore_config_db_close(db);
   if (keys)
     {
	for (x = 0; x < key_count; x++)
	  {
	     free(keys[x]);
	  }
	free(keys);
     }

   return estring_disown(s);
}

/*****************************************************************************/

static int
_ecore_config_ipc_ecore_send(Ecore_Ipc_Event_Client_Data * e, int code,
			     char *reply)
{
   static int          our_ref = 0;
   int                 len = reply ? strlen(reply) + 1 : 0;

   our_ref++;
   WRN("IPC/eCore: replying [0,0] %d IRT %d => %d {\"%s\":%d}", our_ref,
     e->ref, code, reply ? reply : "", len);
   return ecore_ipc_client_send(e->client, 0, 0, our_ref, e->ref, code, reply,
				len);
}

/*****************************************************************************/

static int
_ecore_config_ipc_ecore_handle_request(Ecore_Ipc_Server * server,
				       Ecore_Ipc_Event_Client_Data * e)
{
   Ecore_Config_Server *srv;
   long                serial;
   int                 ret;
   char               *r, *k, *v, *m;

   srv = _ecore_config_server_convert(server);
   serial = e->minor;
   r = NULL;
   m = (char *)e->data;
   INF("IPC/eCore: client sent: [%d,%d] #%d (%d) @ %p", e->major, e->minor,
     e->ref, e->size, server);

   switch (e->major)
     {
     case IPC_PROP_LIST:
	if (srv == __ecore_config_server_global)
	   r = _ecore_config_ipc_global_prop_list(srv, serial);
	else
	   r = _ecore_config_ipc_prop_list(srv, serial);
	break;
     case IPC_PROP_DESC:
	if (_ecore_config_ipc_ecore_string_get(&m, &k) == ECORE_CONFIG_ERR_SUCC)
	   r = _ecore_config_ipc_prop_desc(srv, serial, k);
	break;
     case IPC_PROP_GET:
	if (_ecore_config_ipc_ecore_string_get(&m, &k) == ECORE_CONFIG_ERR_SUCC)
	   r = _ecore_config_ipc_prop_get(srv, serial, k);
	break;
     case IPC_PROP_SET:
	if (_ecore_config_ipc_ecore_string_get(&m, &k) == ECORE_CONFIG_ERR_SUCC)
	  {
	     if (_ecore_config_ipc_ecore_string_get(&m, &v) ==
		 ECORE_CONFIG_ERR_SUCC)
		return _ecore_config_ipc_ecore_send(e,
						    _ecore_config_ipc_prop_set
						    (srv, serial, k, v), NULL);
	  }
	break;

     case IPC_BUNDLE_LIST:
	r = _ecore_config_ipc_bundle_list(srv);
	break;
     case IPC_BUNDLE_NEW:
	if (_ecore_config_ipc_ecore_string_get(&m, &k) == ECORE_CONFIG_ERR_SUCC)
	   return _ecore_config_ipc_ecore_send(e,
					       k ?
					       _ecore_config_ipc_bundle_new(srv,
									    k) :
					       ECORE_CONFIG_ERR_FAIL, NULL);
	break;
     case IPC_BUNDLE_LABEL_SET:
	if (_ecore_config_ipc_ecore_string_get(&m, &k) == ECORE_CONFIG_ERR_SUCC)
	   return _ecore_config_ipc_ecore_send(e,
					       k ?
					       _ecore_config_ipc_bundle_label_set
					       (srv, serial,
						k) : ECORE_CONFIG_ERR_FAIL,
					       NULL);
	break;
     case IPC_BUNDLE_LABEL_FIND:
	if (_ecore_config_ipc_ecore_string_get(&m, &k) == ECORE_CONFIG_ERR_SUCC)
	   return _ecore_config_ipc_ecore_send(e,
					       _ecore_config_ipc_bundle_label_find
					       (srv, k), NULL);
	break;
     case IPC_BUNDLE_LABEL_GET:
	r = _ecore_config_ipc_bundle_label_get(srv, serial);
	break;
     }

   ret =
      _ecore_config_ipc_ecore_send(e,
				   r ? ECORE_CONFIG_ERR_SUCC :
				   ECORE_CONFIG_ERR_FAIL, r);
   if (r)
     {
	free(r);
	return ret;
     }
   return ECORE_CONFIG_ERR_NOTFOUND;
}

/*****************************************************************************/

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

   server = (Ecore_Ipc_Server **) data;
   e = (Ecore_Ipc_Event_Client_Data *) event;

   if (*server != ecore_ipc_client_server_get(e->client))
      return EINA_TRUE;

   INF("IPC/eCore: Client connected. @ %p", server);
   return EINA_TRUE;
}

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

   server = (Ecore_Ipc_Server **) data;
   e = (Ecore_Ipc_Event_Client_Data *) event;

   if (*server != ecore_ipc_client_server_get(e->client))
      return EINA_TRUE;

   INF("IPC/eCore: Client disconnected. @ %p", server);
   return EINA_TRUE;
}

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

   server = (Ecore_Ipc_Server **) data;
   e = (Ecore_Ipc_Event_Client_Data *) event;

   if (*server != ecore_ipc_client_server_get(e->client))
      return EINA_TRUE;

   _ecore_config_ipc_ecore_handle_request(*server, e);
   return EINA_TRUE;
}

/*****************************************************************************/

int
_ecore_config_ipc_ecore_init(const char *pipe_name, void **data)
{
   Ecore_Ipc_Server  **server;
   struct stat         st;
   char               *p;
   int                 port;
   char                socket[PATH_MAX];

   server = (Ecore_Ipc_Server **) data;
   port = 0;
   if (!server)
      return ECORE_CONFIG_ERR_FAIL;

/*  if(*server)
      return ECORE_CONFIG_ERR_IGNORED; */

   ecore_init();
   if (ecore_ipc_init() < 1)
      return ECORE_CONFIG_ERR_FAIL;

   if ((p = getenv("HOME")))
     {				/* debug-only ### FIXME */
	int                 stale;

	stale = 1;
	while (stale)
	  {
	     snprintf(socket, PATH_MAX, "%s/.ecore/%s/%d", p, pipe_name, port);

	     if (!stat(socket, &st))
	       {
		  INF("IPC/eCore: pipe \"%s\" already exists!?", socket);
/*      if(unlink(buf))
  	E(0,"IPC/eCore: could not remove pipe \"%s\": %d\n",buf,errno); }}*/
		  port++;
	       }
	     else
	       {
		  stale = 0;
	       }
	  }
     }
   *server = ecore_ipc_server_add(ECORE_IPC_LOCAL_USER, pipe_name, port, NULL);
   ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_ADD,
			   _ecore_config_ipc_client_add, server);
   ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_DEL,
			   _ecore_config_ipc_client_del, server);
   ecore_event_handler_add(ECORE_IPC_EVENT_CLIENT_DATA,
			   _ecore_config_ipc_client_sent, server);

   if (*server)
     {
	INF("IPC/eCore: Server is listening on %s.", pipe_name);
     }

   return ECORE_CONFIG_ERR_SUCC;
}

int
_ecore_config_ipc_ecore_exit(void **data)
{
   int                 ret;
   Ecore_Ipc_Server  **server;

   ret = ECORE_CONFIG_ERR_SUCC;
   server = (Ecore_Ipc_Server **) data;

   if (!server)
      return ECORE_CONFIG_ERR_FAIL;

   if (*server)
     {
	ecore_ipc_server_del(*server);
	*server = NULL;
     }

   ecore_ipc_shutdown();
   ecore_shutdown();

   return ret;
}

/*****************************************************************************/

int
_ecore_config_ipc_ecore_poll(void **data)
{
   Ecore_Ipc_Server  **server;

   server = (Ecore_Ipc_Server **) data;

   if (!server)
      return ECORE_CONFIG_ERR_FAIL;

   ecore_main_loop_iterate();

   return ECORE_CONFIG_ERR_SUCC;
}

/*****************************************************************************/
